新增:折线图数据对接
This commit is contained in:
parent
9dd61be92f
commit
3983105628
@ -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.ts(ChartHistoryParams / 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)
|
||||
if (chartInstance)
|
||||
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
|
||||
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)
|
||||
}
|
||||
|
||||
function stopDynamicUpdate() {
|
||||
if (dynamicInterval) {
|
||||
clearInterval(dynamicInterval)
|
||||
dynamicInterval = undefined
|
||||
} else {
|
||||
updateChartData()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user