80d7f58589
- api/strategy.ts、api/backtest.ts(含类型) - 回测-新建(策略下拉+动态参数表单+日期+提交) - useTask 组合式(轮询+WS 实时阶段)+ 进度页 - 结果页:统计全表 + 资金曲线 + 每日盈亏(红涨绿跌) + 成交表 + K线买卖点 - build 通过;result 接口扩 symbol/start/end 供 K线调用
56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
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 }
|
|
}
|