diff --git a/frontend/src/api/backtest.ts b/frontend/src/api/backtest.ts index c0a24d1..3275145 100644 --- a/frontend/src/api/backtest.ts +++ b/frontend/src/api/backtest.ts @@ -19,6 +19,13 @@ export interface TaskStatus { task_id: string status: string stage: string + /** 因子分析心跳(仅 factor_ 任务运行中有值) */ + progress?: { + stage: string + detail: string + ts: number + age: number + } | null } export interface EquityPoint { diff --git a/frontend/src/composables/useTask.ts b/frontend/src/composables/useTask.ts index b1cd6e5..5e0ef40 100644 --- a/frontend/src/composables/useTask.ts +++ b/frontend/src/composables/useTask.ts @@ -4,6 +4,14 @@ import { useAuthStore } from '@/stores/auth' export type TaskState = 'pending' | 'running' | 'done' | 'failed' | 'unknown' +export interface TaskProgress { + stage: string + detail: string + ts: number + /** 距上次心跳秒数(后端算) */ + age: number +} + /** * Track a task's status + stage via polling (2s) and WebSocket (real-time). * Auto-stops on unmount. @@ -11,6 +19,7 @@ export type TaskState = 'pending' | 'running' | 'done' | 'failed' | 'unknown' export function useTask(taskId: string) { const status = ref('unknown') const stage = ref('') + const progress = ref(null) let timer: ReturnType | null = null let ws: WebSocket | null = null @@ -19,6 +28,7 @@ export function useTask(taskId: string) { const s = await getStatus(taskId) status.value = s.status as TaskState stage.value = s.stage + progress.value = (s as { progress?: TaskProgress | null }).progress ?? null } catch { /* transient — keep last known state */ } @@ -51,5 +61,5 @@ export function useTask(taskId: string) { if (ws) ws.close() }) - return { status, stage, start } + return { status, stage, progress, start } } diff --git a/frontend/src/views/backtest/Progress.vue b/frontend/src/views/backtest/Progress.vue index 798bf51..983f44a 100644 --- a/frontend/src/views/backtest/Progress.vue +++ b/frontend/src/views/backtest/Progress.vue @@ -1,4 +1,12 @@ diff --git a/frontend/src/views/factor/FactorPicker.vue b/frontend/src/views/factor/FactorPicker.vue index b20dedb..0a245ad 100644 --- a/frontend/src/views/factor/FactorPicker.vue +++ b/frontend/src/views/factor/FactorPicker.vue @@ -11,6 +11,16 @@ const emit = defineEmits<{ (e: 'update:modelValue', v: string[]): void }>() const q = ref('') const picked = ref>(new Set(props.modelValue)) +// 默认收起(1-2 行预览);搜索或点「展开全部」后放开 +const collapsed = ref(true) + +function toggleCollapse(): void { + collapsed.value = !collapsed.value +} + +function onSearch(): void { + if (q.value.trim()) collapsed.value = false +} const grouped = computed(() => { const map = new Map() @@ -68,11 +78,11 @@ defineExpose({ hydrate })