Files
boursecu_user/src/views/base/home/components/PriceChart.vue
T

202 lines
4.7 KiB
Vue
Raw Normal View History

2026-05-14 13:44:43 +03:30
<template>
<div class="bg-gray-50 w-full flex justify-evenly rounded-lg py-4 mt-4 h-[15rem]">
<div class="w-full text-center pt-2">
<a-radio-group v-model:value="selectedValue" button-style="solid">
<a-radio-button value="DAILY" @click="handleUpdate('DAILY')">24 ساعت اخیر</a-radio-button>
<a-radio-button value="WEEKLY" @click="handleUpdate('WEEKLY')">هفته گذشته</a-radio-button>
<a-radio-button value="MONTHLY" @click="handleUpdate('MONTHLY')">ماه گذشته</a-radio-button>
</a-radio-group>
2026-06-15 11:32:46 +03:30
<div class="min-h-[200px] relative">
<div v-if="hasChartData" ref="chartEl" class="h-[200px] w-full" />
<div
v-else
class="h-[200px] flex items-center justify-center text-gray-400 text-sm"
>
داده نمودار در دسترس نیست
</div>
2026-05-14 13:44:43 +03:30
</div>
</div>
</div>
</template>
<script setup lang="ts">
2026-06-15 11:32:46 +03:30
import * as echarts from 'echarts'
import type { EChartsOption } from 'echarts'
import { ref, computed, watch, onBeforeUnmount, nextTick } from 'vue'
2026-05-14 13:44:43 +03:30
import { useConfigStore } from '@/store/configStore'
2026-06-15 11:32:46 +03:30
import { usePrice } from '@/utils/usePrice'
type ChartPoint = [number, number]
2026-05-14 13:44:43 +03:30
const props = defineProps<{
2026-06-15 11:32:46 +03:30
priceChartSeries?: Array<{ name?: string; data?: unknown[] }> | null
2026-05-14 13:44:43 +03:30
}>()
const emit = defineEmits<{
updateData: [timeline: string]
}>()
2026-06-15 11:32:46 +03:30
const configStore = useConfigStore()
2026-05-14 13:44:43 +03:30
const selectedValue = ref('DAILY')
2026-06-15 11:32:46 +03:30
const chartEl = ref<HTMLElement | null>(null)
let chart: echarts.ECharts | null = null
let renderToken = 0
2026-05-14 13:44:43 +03:30
2026-06-15 11:32:46 +03:30
function extractPoints(): ChartPoint[] {
const source = props.priceChartSeries
if (!Array.isArray(source) || source.length === 0) {
return []
}
const raw = source[0]?.data
if (!Array.isArray(raw)) {
return []
}
return raw
.map((item) => {
if (!Array.isArray(item) || item.length < 2) {
return null
}
let x = Number(item[0])
let y = Number(item[1])
if (!Number.isFinite(x) || !Number.isFinite(y) || y <= 0) {
return null
}
if (x > 0 && x < 1e11) {
x *= 1000
}
return [x, y] as ChartPoint
})
.filter((point): point is ChartPoint => point !== null)
.sort((a, b) => a[0] - b[0])
2026-05-14 13:44:43 +03:30
}
2026-06-15 11:32:46 +03:30
const chartPoints = computed(() => extractPoints())
const hasChartData = computed(() => chartPoints.value.length >= 2)
const toPersianNumber = (num: number) =>
num.toString().replace(/\d/g, (d) => '۰۱۲۳۴۵۶۷۸۹'[d])
function buildOption(points: ChartPoint[]): EChartsOption {
const color = configStore.config?.color || '#CEA34C'
return {
2026-06-15 11:32:46 +03:30
animation: true,
grid: {
left: 4,
right: 4,
top: 8,
bottom: 4,
containLabel: false
},
xAxis: {
type: 'time',
show: false,
boundaryGap: false
},
yAxis: {
type: 'value',
show: false,
scale: true
},
tooltip: {
trigger: 'axis',
confine: true,
formatter: (params) => {
const row = Array.isArray(params) ? params[0] : params
if (!row || !Array.isArray(row.value)) {
return ''
}
const [timestamp, value] = row.value as [number, number]
const date = new Date(timestamp)
const dateText = `${date.toLocaleDateString('fa-IR')} - ${date.toLocaleTimeString('fa-IR', {
2026-05-14 13:44:43 +03:30
hour: '2-digit',
minute: '2-digit'
})}`
2026-06-15 11:32:46 +03:30
return `${dateText}<br/>${toPersianNumber(usePrice(value, true, 'ه.ریال'))}`
2026-05-14 13:44:43 +03:30
}
},
2026-06-15 11:32:46 +03:30
series: [
{
name: 'قیمت',
type: 'line',
smooth: true,
showSymbol: false,
data: points,
lineStyle: {
width: 2,
color
},
areaStyle: {
color,
opacity: 0.25
}
}
]
}
}
function destroyChart() {
if (!chart) {
return
}
chart.dispose()
chart = null
}
async function renderChart() {
const token = ++renderToken
if (!hasChartData.value) {
destroyChart()
return
}
await nextTick()
await nextTick()
if (token !== renderToken || !chartEl.value) {
return
}
const points = chartPoints.value
try {
if (!chart || chart.isDisposed()) {
chart = echarts.init(chartEl.value, undefined, { renderer: 'canvas' })
2026-05-14 13:44:43 +03:30
}
2026-06-15 11:32:46 +03:30
chart.setOption(buildOption(points), true)
chart.resize()
} catch (error) {
console.error('Price chart render failed:', error)
destroyChart()
2026-05-14 13:44:43 +03:30
}
2026-06-15 11:32:46 +03:30
}
watch(
() => [chartPoints.value.length, selectedValue.value, configStore.config?.color] as const,
() => {
void renderChart()
},
{ immediate: true, flush: 'post' }
)
onBeforeUnmount(() => {
renderToken++
destroyChart()
})
2026-05-14 13:44:43 +03:30
const handleUpdate = (timeline: string) => {
2026-06-15 11:32:46 +03:30
selectedValue.value = timeline
2026-05-14 13:44:43 +03:30
emit('updateData', timeline)
}
2026-06-15 11:32:46 +03:30
</script>