新增:折线图数据对接
This commit is contained in:
parent
9dd61be92f
commit
3983105628
@ -53,7 +53,7 @@
|
|||||||
|
|
||||||
<!-- 图表区域 -->
|
<!-- 图表区域 -->
|
||||||
<div class="chart-wrapper">
|
<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" />
|
<v-progress-circular indeterminate color="primary" size="32" />
|
||||||
</div>
|
</div>
|
||||||
<div ref="chartContainerRef" class="chart-container"></div>
|
<div ref="chartContainerRef" class="chart-container"></div>
|
||||||
@ -398,12 +398,11 @@ import {
|
|||||||
type OpenOrderDisplayItem,
|
type OpenOrderDisplayItem,
|
||||||
} from '../api/order'
|
} from '../api/order'
|
||||||
import { cancelOrder as apiCancelOrder } from '../api/order'
|
import { cancelOrder as apiCancelOrder } from '../api/order'
|
||||||
|
import type { ChartDataPoint, ChartTimeRange } from '../api/chart'
|
||||||
import {
|
import {
|
||||||
normalizeChartData,
|
getPmPriceHistoryPublic,
|
||||||
fetchChartHistory,
|
priceHistoryToChartData,
|
||||||
type ChartDataPoint,
|
} from '../api/priceHistory'
|
||||||
type ChartTimeRange,
|
|
||||||
} from '../api/chart'
|
|
||||||
import {
|
import {
|
||||||
isCryptoEvent as checkIsCryptoEvent,
|
isCryptoEvent as checkIsCryptoEvent,
|
||||||
inferCryptoSymbol,
|
inferCryptoSymbol,
|
||||||
@ -1242,52 +1241,47 @@ const timeRanges = [
|
|||||||
{ label: 'ALL', value: 'ALL' },
|
{ 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 chartContainerRef = ref<HTMLElement | null>(null)
|
||||||
const data = ref<[number, number][]>([])
|
const data = ref<[number, number][]>([])
|
||||||
|
/** Yes/No 折线图:接口返回的完整数据(time 已转 ms),用于分时筛选 */
|
||||||
|
const rawChartData = ref<ChartDataPoint[]>([])
|
||||||
const cryptoChartLoading = ref(false)
|
const cryptoChartLoading = ref(false)
|
||||||
|
const chartYesNoLoading = ref(false)
|
||||||
let chartInstance: ECharts | null = null
|
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 currentChance = computed(() => {
|
||||||
const ev = eventDetail.value
|
const ev = eventDetail.value
|
||||||
@ -1523,7 +1517,7 @@ function buildOptionForCrypto(
|
|||||||
|
|
||||||
function initChart() {
|
function initChart() {
|
||||||
if (!chartContainerRef.value) return
|
if (!chartContainerRef.value) return
|
||||||
data.value = generateData(selectedTimeRange.value)
|
data.value = []
|
||||||
chartInstance = echarts.init(chartContainerRef.value)
|
chartInstance = echarts.init(chartContainerRef.value)
|
||||||
const w = chartContainerRef.value.clientWidth
|
const w = chartContainerRef.value.clientWidth
|
||||||
if (chartMode.value === 'crypto') {
|
if (chartMode.value === 'crypto') {
|
||||||
@ -1533,12 +1527,15 @@ function initChart() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 从接口拉取图表数据(接入时在 updateChartData 中调用并赋给 data.value) */
|
/** 从 GET /pmPriceHistory/getPmPriceHistoryPublic 拉取价格历史,market 传 YES 对应的 clobTokenId */
|
||||||
async function loadChartFromApi(marketId: string): Promise<ChartDataPoint[]> {
|
async function loadChartFromApi(marketParam: string): Promise<ChartDataPoint[]> {
|
||||||
const res = await fetchChartHistory(
|
const res = await getPmPriceHistoryPublic({
|
||||||
{ marketID: marketId, range: selectedTimeRange.value as ChartTimeRange }
|
market: marketParam,
|
||||||
)
|
page: 1,
|
||||||
return normalizeChartData(res.data ?? [])
|
pageSize: 500,
|
||||||
|
})
|
||||||
|
const list = res.data?.list ?? []
|
||||||
|
return priceHistoryToChartData(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MINUTE_MS = 60 * 1000
|
const MINUTE_MS = 60 * 1000
|
||||||
@ -1566,7 +1563,6 @@ function applyCryptoRealtimePoint(point: [number, number]) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用接口时:在 updateChartData 内先 await loadChartFromApi(marketId),再 setOption;暂无接口时用 generateData
|
|
||||||
async function updateChartData() {
|
async function updateChartData() {
|
||||||
const w = chartContainerRef.value?.clientWidth
|
const w = chartContainerRef.value?.clientWidth
|
||||||
if (chartMode.value === 'crypto') {
|
if (chartMode.value === 'crypto') {
|
||||||
@ -1592,60 +1588,35 @@ async function updateChartData() {
|
|||||||
} else {
|
} else {
|
||||||
cryptoWsUnsubscribe?.()
|
cryptoWsUnsubscribe?.()
|
||||||
cryptoWsUnsubscribe = null
|
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)
|
if (chartInstance)
|
||||||
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
|
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
|
||||||
|
} finally {
|
||||||
|
chartYesNoLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectTimeRange(range: string) {
|
function selectTimeRange(range: string) {
|
||||||
selectedTimeRange.value = range
|
selectedTimeRange.value = range
|
||||||
updateChartData()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMaxPoints(range: string): number {
|
watch(selectedTimeRange, (range) => {
|
||||||
switch (range) {
|
if (chartMode.value === 'yesno') {
|
||||||
case '1H':
|
data.value = filterChartDataByRange(rawChartData.value, range)
|
||||||
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)
|
|
||||||
const w = chartContainerRef.value?.clientWidth
|
const w = chartContainerRef.value?.clientWidth
|
||||||
if (chartInstance)
|
if (chartInstance)
|
||||||
chartInstance.setOption(buildOption(data.value, w), { replaceMerge: ['series'] })
|
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)
|
// CLOB:当有 market 且存在 clobTokenIds 时连接(使用 Yes/No token ID)
|
||||||
const clobTokenIds = computed(() => {
|
const clobTokenIds = computed(() => {
|
||||||
@ -1711,15 +1682,13 @@ const unsubscribePositionUpdate = userStore.onPositionUpdate((data) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadEventDetail()
|
loadEventDetail().then(() => updateChartData())
|
||||||
initChart()
|
initChart()
|
||||||
startDynamicUpdate()
|
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unsubscribePositionUpdate()
|
unsubscribePositionUpdate()
|
||||||
stopDynamicUpdate()
|
|
||||||
cryptoWsUnsubscribe?.()
|
cryptoWsUnsubscribe?.()
|
||||||
cryptoWsUnsubscribe = null
|
cryptoWsUnsubscribe = null
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user