新增:折线图数据对接

This commit is contained in:
ivan 2026-03-15 21:04:11 +08:00
parent 9dd61be92f
commit 3983105628

View File

@ -53,7 +53,7 @@
<!-- 图表区域 -->
<div class="chart-wrapper">
<div v-if="cryptoChartLoading" class="chart-loading-overlay">
<div v-if="cryptoChartLoading || chartYesNoLoading" class="chart-loading-overlay">
<v-progress-circular indeterminate color="primary" size="32" />
</div>
<div ref="chartContainerRef" class="chart-container"></div>
@ -398,12 +398,11 @@ import {
type OpenOrderDisplayItem,
} from '../api/order'
import { cancelOrder as apiCancelOrder } from '../api/order'
import type { ChartDataPoint, ChartTimeRange } from '../api/chart'
import {
normalizeChartData,
fetchChartHistory,
type ChartDataPoint,
type ChartTimeRange,
} from '../api/chart'
getPmPriceHistoryPublic,
priceHistoryToChartData,
} from '../api/priceHistory'
import {
isCryptoEvent as checkIsCryptoEvent,
inferCryptoSymbol,
@ -1242,52 +1241,47 @@ const timeRanges = [
{ label: 'ALL', value: 'ALL' },
]
// [(ms), (0-100)][] src/api/chart.tsChartHistoryParams / ChartHistoryItem / normalizeChartData
function generateData(range: string): ChartDataPoint[] {
const now = Date.now()
const data: [number, number][] = []
let stepMs: number
let count: number
switch (range) {
case '1H':
stepMs = 60 * 1000
count = 60
break
case '6H':
stepMs = 10 * 60 * 1000
count = 36
break
case '1D':
stepMs = 60 * 60 * 1000
count = 24
break
case '1W':
stepMs = 24 * 60 * 60 * 1000
count = 7
break
case '1M':
case 'ALL':
stepMs = 24 * 60 * 60 * 1000
count = 30
break
default:
stepMs = 60 * 60 * 1000
count = 24
}
let value = 15 + Math.random() * 25
for (let i = count; i >= 0; i--) {
const t = now - i * stepMs
value = Math.max(10, Math.min(90, value + (Math.random() - 0.5) * 6))
data.push([t, Math.round(value * 10) / 10])
}
return data
}
const chartContainerRef = ref<HTMLElement | null>(null)
const data = ref<[number, number][]>([])
/** Yes/No 折线图接口返回的完整数据time 已转 ms用于分时筛选 */
const rawChartData = ref<ChartDataPoint[]>([])
const cryptoChartLoading = ref(false)
const chartYesNoLoading = ref(false)
let chartInstance: ECharts | null = null
let dynamicInterval: number | undefined
/** 分时范围对应的毫秒数ALL 返回 null 表示不截断 */
function getTimeRangeMs(range: string): number | null {
const H = 60 * 60 * 1000
const D = 24 * H
switch (range) {
case '1H':
return 1 * H
case '6H':
return 6 * H
case '1D':
return 1 * D
case '1W':
return 7 * D
case '1M':
return 30 * D
case 'ALL':
default:
return null
}
}
/** 按分时范围过滤 [timestamp_ms, value][],保留区间 [now - rangeMs, now] 内的点 */
function filterChartDataByRange(
points: ChartDataPoint[],
range: string,
): ChartDataPoint[] {
if (!points.length) return []
const rangeMs = getTimeRangeMs(range)
if (rangeMs == null) return points
const nowMs = Date.now()
const cutoffMs = nowMs - rangeMs
return points.filter(([ts]) => ts >= cutoffMs)
}
const currentChance = computed(() => {
const ev = eventDetail.value
@ -1523,7 +1517,7 @@ function buildOptionForCrypto(
function initChart() {
if (!chartContainerRef.value) return
data.value = generateData(selectedTimeRange.value)
data.value = []
chartInstance = echarts.init(chartContainerRef.value)
const w = chartContainerRef.value.clientWidth
if (chartMode.value === 'crypto') {
@ -1533,12 +1527,15 @@ function initChart() {
}
}
/** 从接口拉取图表数据(接入时在 updateChartData 中调用并赋给 data.value */
async function loadChartFromApi(marketId: string): Promise<ChartDataPoint[]> {
const res = await fetchChartHistory(
{ marketID: marketId, range: selectedTimeRange.value as ChartTimeRange }
)
return normalizeChartData(res.data ?? [])
/** 从 GET /pmPriceHistory/getPmPriceHistoryPublic 拉取价格历史market 传 YES 对应的 clobTokenId */
async function loadChartFromApi(marketParam: string): Promise<ChartDataPoint[]> {
const res = await getPmPriceHistoryPublic({
market: marketParam,
page: 1,
pageSize: 500,
})
const list = res.data?.list ?? []
return priceHistoryToChartData(list)
}
const MINUTE_MS = 60 * 1000
@ -1566,7 +1563,6 @@ function applyCryptoRealtimePoint(point: [number, number]) {
)
}
// 使 updateChartData await loadChartFromApi(marketId) setOption generateData
async function updateChartData() {
const w = chartContainerRef.value?.clientWidth
if (chartMode.value === 'crypto') {
@ -1592,60 +1588,35 @@ async function updateChartData() {
} else {
cryptoWsUnsubscribe?.()
cryptoWsUnsubscribe = null
data.value = generateData(selectedTimeRange.value)
chartYesNoLoading.value = true
try {
// market clobTokenIds[0]YES token ID
const yesTokenId = clobTokenIds.value[0]
const points = yesTokenId ? await loadChartFromApi(yesTokenId) : []
rawChartData.value = points
data.value = filterChartDataByRange(points, selectedTimeRange.value)
if (chartInstance)
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
} finally {
chartYesNoLoading.value = false
}
}
}
function selectTimeRange(range: string) {
selectedTimeRange.value = range
updateChartData()
}
function getMaxPoints(range: string): number {
switch (range) {
case '1H':
return 60
case '6H':
return 36
case '1D':
return 24
case '1W':
return 7
case '1M':
case 'ALL':
return 30
default:
return 24
}
}
function startDynamicUpdate() {
dynamicInterval = window.setInterval(() => {
if (chartMode.value === 'crypto') return
const list = [...data.value]
const last = list[list.length - 1]
if (!last) return
const nextVal = Math.max(10, Math.min(90, last[1] + (Math.random() - 0.5) * 4))
const nextT = Date.now()
list.push([nextT, Math.round(nextVal * 10) / 10])
const max = getMaxPoints(selectedTimeRange.value)
data.value = list.slice(-max)
watch(selectedTimeRange, (range) => {
if (chartMode.value === 'yesno') {
data.value = filterChartDataByRange(rawChartData.value, range)
const w = chartContainerRef.value?.clientWidth
if (chartInstance)
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
}, 3000)
} else {
updateChartData()
}
function stopDynamicUpdate() {
if (dynamicInterval) {
clearInterval(dynamicInterval)
dynamicInterval = undefined
}
}
watch(selectedTimeRange, () => updateChartData())
})
// CLOB market clobTokenIds 使 Yes/No token ID
const clobTokenIds = computed(() => {
@ -1711,15 +1682,13 @@ const unsubscribePositionUpdate = userStore.onPositionUpdate((data) => {
})
onMounted(() => {
loadEventDetail()
loadEventDetail().then(() => updateChartData())
initChart()
startDynamicUpdate()
window.addEventListener('resize', handleResize)
})
onUnmounted(() => {
unsubscribePositionUpdate()
stopDynamicUpdate()
cryptoWsUnsubscribe?.()
cryptoWsUnsubscribe = null
window.removeEventListener('resize', handleResize)