feat(frontend): S1 回测核心页(新建/进度/结果 + ECharts 图表)
- api/strategy.ts、api/backtest.ts(含类型) - 回测-新建(策略下拉+动态参数表单+日期+提交) - useTask 组合式(轮询+WS 实时阶段)+ 进度页 - 结果页:统计全表 + 资金曲线 + 每日盈亏(红涨绿跌) + 成交表 + K线买卖点 - build 通过;result 接口扩 symbol/start/end 供 K线调用
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { ref, onUnmounted } from 'vue'
|
||||
import { getStatus } from '@/api/backtest'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
export type TaskState = 'pending' | 'running' | 'done' | 'failed' | 'unknown'
|
||||
|
||||
/**
|
||||
* Track a task's status + stage via polling (2s) and WebSocket (real-time).
|
||||
* Auto-stops on unmount.
|
||||
*/
|
||||
export function useTask(taskId: string) {
|
||||
const status = ref<TaskState>('unknown')
|
||||
const stage = ref('')
|
||||
let timer: ReturnType<typeof setInterval> | null = null
|
||||
let ws: WebSocket | null = null
|
||||
|
||||
async function poll(): Promise<void> {
|
||||
try {
|
||||
const s = await getStatus(taskId)
|
||||
status.value = s.status as TaskState
|
||||
stage.value = s.stage
|
||||
} catch {
|
||||
/* transient — keep last known state */
|
||||
}
|
||||
}
|
||||
|
||||
function start(): void {
|
||||
poll()
|
||||
timer = setInterval(poll, 2000)
|
||||
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
||||
const auth = useAuthStore()
|
||||
const url = `${proto}://${window.location.host}/api/v1/ws/task/${taskId}?token=${auth.token}`
|
||||
try {
|
||||
ws = new WebSocket(url)
|
||||
ws.onmessage = (ev) => {
|
||||
try {
|
||||
const msg = JSON.parse(ev.data)
|
||||
if (msg.stage) stage.value = msg.stage
|
||||
if (msg.status) status.value = msg.status
|
||||
} catch {
|
||||
/* ignore non-JSON keepalive frames */
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* WS optional — polling covers it */
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (timer) clearInterval(timer)
|
||||
if (ws) ws.close()
|
||||
})
|
||||
|
||||
return { status, stage, start }
|
||||
}
|
||||
Reference in New Issue
Block a user