added sse and description for withdraw
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user