added sse and description for withdraw

This commit is contained in:
2026-06-18 19:09:50 +03:30
parent 3e74699f7e
commit f3aaf9d7c0
10 changed files with 921 additions and 73 deletions
+69 -11
View File
@@ -51,11 +51,18 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { useConfigStore } from '@/store/configStore'
import { usePrice } from '@/utils/usePrice'
import { getProjectFontFamily } from '@/utils/config'
import { computeTightChartYBounds, amplifyChartDeviation, shouldAmplifyKatodMesChart } from '@/utils/chartPriceAxis'
import {
computeKatodMesVisualYBounds,
prepareKatodMesChartPoints,
shouldAmplifyKatodMesChart,
shouldUseKatodVisualChartTransform,
} from '@/utils/chartPriceAxis'
import { useSettingsStore } from '@/store/settingsStore'
import { useElementSize } from '@vueuse/core'
import { price } from '@/utils/price'
import type { ChartMetalType } from '../composables/usePriceChart'
import { resolveMetalDayRangePrices, usePriceChart } from '../composables/usePriceChart'
const props = withDefaults(
defineProps<{
@@ -93,6 +100,7 @@ const emit = defineEmits<{
const config = useConfigStore()
const settingsStore = useSettingsStore()
const { maftolChartRatio } = usePriceChart()
const chartContainerRef = ref<HTMLElement | null>(null)
const chartMounted = ref(false)
const selectedValue = ref(props.chartSelection)
@@ -106,21 +114,43 @@ const amplifyKatodChart = computed(() =>
metalTypeSetting: settingsStore.metalTypeSettingValue,
})
)
const katodUsesVisualTransform = computed(
() => amplifyKatodChart.value && shouldUseKatodVisualChartTransform(props.chartSelection)
)
const katodDayRange = computed(() => {
if (!amplifyKatodChart.value) {
return undefined
}
return resolveMetalDayRangePrices({
price: price.value,
metal: props.selectedMetal,
isBoursecuLayout: true,
maftolRatio: maftolChartRatio.value,
})
})
const displayChartSeries = computed(() => {
const baseSeries = props.priceChartSeries?.[0]
if (!baseSeries?.data?.length || !amplifyKatodChart.value) {
return props.priceChartSeries
}
const originalData = baseSeries.data as [number, number][]
const { data: displayData, tooltipPrices, dayMeta, pointMeta } = prepareKatodMesChartPoints(originalData, {
timeline: props.chartSelection,
dayRange: katodDayRange.value,
})
return [
{
...baseSeries,
data: amplifyChartDeviation(baseSeries.data as [number, number][]),
data: displayData,
meta: { tooltipPrices, dayMeta, pointMeta },
},
]
})
const chartYBounds = computed(() => {
if (!amplifyKatodChart.value) {
if (!katodUsesVisualTransform.value) {
return {}
}
@@ -129,7 +159,7 @@ const chartYBounds = computed(() => {
?.map((point: [number, number]) => Number(point?.[1]))
.filter((value: number) => Number.isFinite(value)) ?? []
return computeTightChartYBounds(prices)
return computeKatodMesVisualYBounds(prices)
})
const canRenderChart = computed(
() => chartMounted.value && chartWidth.value > 0 && seriesLength.value > 0
@@ -157,7 +187,7 @@ const chartOptions = computed(() => {
type: 'area',
height: 200,
toolbar: { show: false },
zoom: { autoScaleYaxis: !amplifyKatodChart.value },
zoom: { autoScaleYaxis: !katodUsesVisualTransform.value },
redrawOnParentResize: true,
},
grid: {
@@ -165,8 +195,8 @@ const chartOptions = computed(() => {
yaxis: { lines: { show: false } },
},
stroke: {
width: amplifyKatodChart.value ? 2.5 : 2,
curve: amplifyKatodChart.value ? 'smooth' : 'straight',
width: katodUsesVisualTransform.value ? 2.5 : 2,
curve: 'straight',
},
dataLabels: {
enabled: false,
@@ -201,11 +231,20 @@ const chartOptions = computed(() => {
})}`
},
},
...(amplifyKatodChart.value
...(katodUsesVisualTransform.value
? {
custom: ({ dataPointIndex, w }: { dataPointIndex: number; w: { globals: { seriesX: number[][] } } }) => {
const timestamp = w.globals.seriesX[0]?.[dataPointIndex]
const realPrice = props.priceChartSeries?.[0]?.data?.[dataPointIndex]?.[1]
const seriesMeta = displayChartSeries.value?.[0]?.meta as
| {
tooltipPrices?: number[]
dayMeta?: { high: number; low: number; avg: number }[]
pointMeta?: { kind: 'high' | 'low' | 'avg'; dayHigh: number; dayLow: number }[]
}
| undefined
const pointMeta = seriesMeta?.pointMeta?.[dataPointIndex]
const dayMeta = seriesMeta?.dayMeta?.[dataPointIndex]
const realPrice = seriesMeta?.tooltipPrices?.[dataPointIndex] ?? dayMeta?.avg
if (timestamp == null || realPrice == null) {
return ''
}
@@ -216,12 +255,31 @@ const chartOptions = computed(() => {
minute: '2-digit',
})}`
const priceLine = usePrice(realPrice, true, 'ه.ریال')
let valueLabel = priceLine
if (pointMeta?.kind === 'high') {
valueLabel = `بالاترین روز: ${priceLine}`
} else if (pointMeta?.kind === 'low') {
valueLabel = `پایین‌ترین روز: ${priceLine}`
} else if (dayMeta) {
valueLabel = `میانگین روز: ${priceLine}`
}
const rangeHigh = pointMeta?.dayHigh ?? dayMeta?.high
const rangeLow = pointMeta?.dayLow ?? dayMeta?.low
const rangeLine =
rangeHigh != null && rangeLow != null
? `<div style="font-family:${fontFamily};font-size:12px;opacity:0.85;margin-top:4px">
بازه روز: ${usePrice(rangeHigh, true, 'ه.ریال')} — ${usePrice(rangeLow, true, 'ه.ریال')}
</div>`
: ''
return `<div class="apexcharts-tooltip-title" style="font-family:${fontFamily}">${timeLabel}</div>
<div class="apexcharts-tooltip-series-group apexcharts-active" style="order:1;display:flex">
<span class="apexcharts-tooltip-text" style="font-family:${fontFamily}">
<span class="apexcharts-tooltip-text-y-value">${usePrice(realPrice, true, 'ه.ریال')}</span>
<span class="apexcharts-tooltip-text-y-value">${valueLabel}</span>
</span>
</div>`
</div>${rangeLine}`
},
}
: {
@@ -1,10 +1,15 @@
import { ref } from 'vue'
import { getProductsCatalog } from '@/composables/useProductsCatalog'
import { ref, watch } from 'vue'
import { getProductsCatalog, getProductsCatalogRef } from '@/composables/useProductsCatalog'
import { loading } from '@/utils'
import type { Product } from '@/models/products'
import { loadServiceFeeFromSettings } from '@/utils/serviceFee'
import { loadProductCategoryTabNames } from '@/utils/categoryTabs'
import { getLastPrices, startLastPricePolling, stopLastPricePolling } from '@/utils/price'
import {
priceSocketConnected,
priceWssEnabled,
shouldUsePricePolling,
} from '@/utils/priceSocketState'
/** Shared products page datasets — mobile/desktop views read this module state only. */
let initPromise: Promise<void> | null = null
@@ -99,12 +104,49 @@ export async function refreshProductsPageData() {
}
function startProductListPolling() {
if (productListInterval) return
if (productListInterval || !shouldUsePricePolling()) {
return
}
productListInterval = setInterval(() => {
void refreshProductsPageData()
if (shouldUsePricePolling()) {
void refreshProductsPageData()
}
}, 10000)
}
function syncProductListPolling() {
const usePolling = shouldUsePricePolling()
if (usePolling) {
if (initialized.value) {
startProductListPolling()
}
return
}
stopProductListPolling()
}
watch(
() => [priceWssEnabled.value, priceSocketConnected.value] as const,
([wssEnabled, connected], prev) => {
const prevConnected = prev?.[1]
syncProductListPolling()
if (wssEnabled && prevConnected && !connected && initialized.value) {
void refreshProductsPageData()
}
},
)
export function applyProductsListFromSocket(products: Product[]) {
if (!products?.length) {
return
}
getProductsCatalogRef().value = products
applyProductsList(products)
}
function stopProductListPolling() {
if (productListInterval) {
clearInterval(productListInterval)
@@ -134,7 +176,7 @@ export function onProductsPageProviderUnmounted() {
export async function initProductsPageData(force = false) {
if (!force && initialized.value) {
startProductListPolling()
syncProductListPolling()
return
}
@@ -152,7 +194,7 @@ export async function initProductsPageData(force = false) {
loadCategoryTabs(),
])
initialized.value = true
startProductListPolling()
syncProductListPolling()
} catch (error) {
console.error('Products page initialization error:', error)
} finally {
@@ -41,6 +41,7 @@ export interface WalletPageContext {
infoVisible: Ref<boolean>
refOtpInput: Ref<InstanceType<typeof OtpInput> | null>
form: Ref<Transaction>
extraNote: Ref<string>
cardForm: Ref<Card>
disableButton: ReturnType<typeof computed<boolean>>
cardItems: typeof walletCardItems
@@ -106,6 +107,8 @@ export function useWalletPage() {
buildDepositSmsUrl(supportMessengerSettings.value.depositSmsSupportNumber),
)
const extraNote = ref('')
const form = ref<Transaction>({
value: undefined,
percentWallet: 0,
@@ -146,6 +149,7 @@ export function useWalletPage() {
function resetData() {
form.value.value = undefined
form.value.percentWallet = 0
extraNote.value = ''
}
async function addCard() {
@@ -217,7 +221,11 @@ export function useWalletPage() {
try {
infoVisible.value = true
showConfirmCode.value = false
await withdraw({ ...form.value, otp: value })
await withdraw({
...form.value,
otp: value,
extra_note: extraNote.value.trim() || undefined,
})
await walletStore.getMyWallet()
await message.success('عملیات با موفقیت انجام شد')
withdrawInfModal.value = true
@@ -264,6 +272,7 @@ export function useWalletPage() {
infoVisible,
refOtpInput,
form,
extraNote,
cardForm,
disableButton,
cardItems: walletCardItems,
+10
View File
@@ -55,6 +55,15 @@
@on-complete="handleOnComplete"
/>
</div>
<div class="px-5 pb-2 mt-4">
<a-textarea
v-model:value="extraNote"
:maxlength="500"
:rows="3"
placeholder="توضیحات اضافی"
show-count
/>
</div>
</div>
</template>
<template #button>
@@ -136,6 +145,7 @@ const {
withdrawInfModal,
infoVisible,
refOtpInput,
extraNote,
supportMessengerText,
depositGuideSupportLink,
depositSmsSupportLink,