feat(strategy): TODO#68-#75八项全做+导航自测——#68灯职责拆分:实走/影子在跑=直达/paper/live/{aid}监控页,实盘在跑=/live/monitor/{aid},未跑=发起;回放灯done=直达/paper/result/{aid}(run_meta.replay.account_id);#69回测任务按实例过滤:backtest_stats加instance_id列(ALTER迁移)+_write_back落列+GET /task?instance=N+任务中心过滤条+策略库「回测历史」按钮;回测灯done=直达最新结果页(run_meta.backtest.task_id按前缀分流),跑过无结果=任务中心过滤;#70模拟盘创建后直接跳列表(不跳结果/监控页);列宽重排(策略/实例150模式96频率60操作250,折行实测消除);#71列内容对齐实盘:策略/实例列显示实例名(instances映射,#id退化)+持仓数列(list_papers顺带count volume>0)+删创建时间列(净值日期已含);#72实盘名自动生成={实例名}_v{YYYYMMDD}{minor}(同实例同日递增,listLives计数);模拟盘名={实例名}·{模式};#73术语统一档案→实例(全局8文件UI文案+后端409提示,精确短语防误伤,spec比喻保留文档);#74MonacoDiff v4:优先monaco原生diff(差异高亮),挂载450ms自检original栏占比<30%自动降级双只读编辑器(滚动联动)——全屏模态下大概率吃到原生diff;#75收益标签与数值同源(retInfoOf:运行ret优先含kind,fallback run_returns,标签跟随数值来源,根治影子1044%实为回测收益挂影子标签);927绿+build绿+dev浏览器自测:策略库6入口(回测历史/模拟历史/实盘历史/回测灯/实走灯直达监控/组合无回放)与模拟盘列表新列全过 [vps]
CI/CD / test (push) Successful in 13s
CI/CD / nas-deploy (push) Successful in 32s
CI/CD / nas-verify (push) Successful in 12s

This commit is contained in:
2026-08-16 09:34:39 +08:00
parent 8ee513a17d
commit bab7c4d893
16 changed files with 293 additions and 126 deletions
+9 -3
View File
@@ -1,16 +1,21 @@
<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getTasks, getTaskParams, deleteTask, type TaskListItem } from '@/api/backtest'
const router = useRouter()
const route = useRoute()
const tasks = ref<TaskListItem[]>([])
const loading = ref(false)
const deleting = ref(false)
const selection = ref<TaskListItem[]>([])
// reserve-selection 后勾选按 row-key 持久;删除完要显式清表内勾选
const tableRef = ref()
// #69 按实例过滤(策略库「回测历史」跳 ?instance=N 只看该实例的任务)
const instFilter = route.query.instance && !Array.isArray(route.query.instance)
? ref<number | null>(Number(route.query.instance))
: ref<number | null>(null)
// 筛选
const typeFilter = ref<'all' | 'cta' | 'portfolio' | 'optimize' | 'factor'>('all')
@@ -94,7 +99,7 @@ onUnmounted(() => {
async function load(): Promise<void> {
loading.value = true
try {
tasks.value = await getTasks()
tasks.value = await getTasks(undefined, instFilter.value ?? undefined)
} catch {
ElMessage.error('任务列表加载失败')
} finally {
@@ -110,7 +115,7 @@ function hasActive(): boolean {
}
async function silentRefresh(): Promise<void> {
try {
tasks.value = await getTasks()
tasks.value = await getTasks(undefined, instFilter.value ?? undefined)
} catch {
/* ignore */
}
@@ -232,6 +237,7 @@ function durationText(row: TaskListItem): string {
<p class="page-subtitle">回测 / 参数优化 / 因子分析 的全部任务 · 完成 {{ counts.done }} · 失败 {{ counts.failed }}</p>
</div>
<div class="head-actions">
<el-button v-if="instFilter != null" size="small" @click="router.push('/backtest/history')">实例 #{{ instFilter }} 过滤中 · 查看</el-button>
<el-button type="danger" plain :disabled="!selection.length" :loading="deleting" @click="doDelete(selection)">
删除选中({{ selection.length }})
</el-button>
+3 -3
View File
@@ -246,13 +246,13 @@ async function onSubmit(): Promise<void> {
<el-card class="blk" shadow="never" v-loading="loading">
<template #header><span class="section-title">策略与参数</span></template>
<el-form :model="form" label-width="120px">
<el-form-item label="策略 / 档案">
<el-form-item label="策略 / 实例">
<el-select v-model="form.strategy" placeholder="选择策略" style="width: 300px">
<el-option v-for="s in strategies" :key="s.name" :label="s.name" :value="s.name" />
</el-select>
<el-select
:model-value="selectedInstanceId" clearable filterable
placeholder="实例档案(可选·带出参数)"
placeholder="策略实例(可选·带出参数)"
style="width: 260px; margin-left: 8px"
@update:model-value="onPickInstance"
>
@@ -261,7 +261,7 @@ async function onSubmit(): Promise<void> {
:label="`${i.name}${i.interval}`"
/>
</el-select>
<span v-if="selectedInstanceId != null" class="muted form-hint">完成后回写档案 #{{ selectedInstanceId }}</span>
<span v-if="selectedInstanceId != null" class="muted form-hint">完成后回写实例 #{{ selectedInstanceId }}</span>
</el-form-item>
<el-form-item v-if="paramsList.length" label="参数">
<div class="param-grid">
@@ -495,13 +495,13 @@ function fmtNum(v: number | null | undefined, digits = 2): string {
</el-select>
<span class="muted form-hint">MVP 默认 HS300 子集(20-30 ),快速验证链路</span>
</el-form-item>
<el-form-item label="策略 / 档案">
<el-form-item label="策略 / 实例">
<el-select v-model="form.strategy" style="width: 300px">
<el-option v-for="opt in strategyOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
<el-select
:model-value="selectedInstanceId" clearable filterable
placeholder="实例档案(可选·带出参数)"
placeholder="策略实例(可选·带出参数)"
style="width: 260px; margin-left: 8px"
@update:model-value="onPickInstance"
>
@@ -510,7 +510,7 @@ function fmtNum(v: number | null | undefined, digits = 2): string {
:label="`${i.name}${i.interval}`"
/>
</el-select>
<span v-if="selectedInstanceId != null" class="muted form-hint">完成后回写档案 #{{ selectedInstanceId }}</span>
<span v-if="selectedInstanceId != null" class="muted form-hint">完成后回写实例 #{{ selectedInstanceId }}</span>
</el-form-item>
<el-form-item label="选股池上限">
<el-input-number v-model="form.max_pool" :min="0" :step="10" :controls="false" style="width: 220px" />