fix(frontend): #91+#92+#93验收三连修——#91:实例编辑保存变新建(onSave无论编辑/新建都POST=建新重复实例;改带editId走PUT /strategy/instances/{id},payload补type按所选文件判定防PUT全量替换把组合实例改写cta;成功提示清(mock)残留文案)+卡片标的行整体移除(#86a实例不承载标的,存量all显示误导);#92:标的池显示统一——constants/strategy.ts新增POOL_LABELS+poolLabel(all→全市场/hs300_subset→HS300子集/ZZ500→中证500等),任务列表标的列+模拟盘列表symbolsShort全走映射(股票代码原样);#93:新建模拟盘加名字只读预览(对齐实盘:选实例后显{实例名}_v{YYYYMMDD}{minor},未选显占位提示);build绿(vue-tsc) [nas]
CI/CD / test (push) Successful in 19s
CI/CD / nas-deploy (push) Successful in 33s
CI/CD / nas-verify (push) Successful in 10s

This commit is contained in:
2026-08-16 19:56:40 +08:00
parent 0d95de2fc2
commit 9f834d7662
6 changed files with 45 additions and 7 deletions
+18
View File
@@ -17,3 +17,21 @@ export function strategyLabel(fileName: string): string {
const key = fileName.replace(/\.py$/, '')
return STRATEGY_LABELS[key] || key
}
// 标的池显示名(#92 全链路统一:任务列表/模拟盘列表等不显裸值 all/hs300_subset
// 下拉当时选的什么 label 就显示什么)
export const POOL_LABELS: Record<string, string> = {
hs300_subset: 'HS300 子集',
all: '全市场',
HS300: '沪深300',
ZZ500: '中证500',
ZZ1000: '中证1000',
ZZ2000: '中证2000',
ALL: '全市场',
}
/** 池值 → 显示名;非池值(股票代码等)原样返回 */
export function poolLabel(value: string): string {
const v = (value || '').trim()
return POOL_LABELS[v] || v
}
+4 -1
View File
@@ -3,6 +3,7 @@ import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getTasks, getTaskParams, deleteTask, type TaskListItem } from '@/api/backtest'
import { poolLabel } from '@/constants/strategy'
const router = useRouter()
const route = useRoute()
@@ -270,7 +271,9 @@ function durationText(row: TaskListItem): string {
<template #default="{ row }"><span class="chip" :class="`chip-${row.type}`">{{ typeLabel(row.type) }}</span></template>
</el-table-column>
<el-table-column prop="strategy" label="策略 / 因子" min-width="130" show-overflow-tooltip />
<el-table-column prop="symbol" label="标的" width="130" />
<el-table-column label="标的" width="130">
<template #default="{ row }">{{ poolLabel(row.symbol) }}</template>
</el-table-column>
<el-table-column label="状态" width="88">
<template #default="{ row }"><span class="chip" :class="`st-${row.status}`">{{ statusLabel(row.status) }}</span></template>
</el-table-column>
+4 -1
View File
@@ -3,6 +3,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import CodeSnapshotDrawer from '@/views/strategy/CodeSnapshotDrawer.vue'
import { poolLabel } from '@/constants/strategy'
import {
listPapers, stopPaper, resumePaper, deletePaper, updatePaper,
type PaperAccount,
@@ -83,7 +84,9 @@ function symbolsShort(s: string | undefined): string {
arr = String(s).split(',').map((x) => x.trim())
}
if (!arr.length) return '—'
return arr.length > 3 ? arr.slice(0, 3).join(',') + ` +${arr.length - 3}` : arr.join(',')
// #92: 池值显中文 label(组合账户 symbols=["all"] → 全市场),股票代码原样
const shown = arr.map(poolLabel)
return shown.length > 3 ? shown.slice(0, 3).join(',') + ` +${shown.length - 3}` : shown.join(',')
}
const typeLabel: Record<string, string> = { cta: '个股', portfolio: '组合' }
function open(a: PaperAccount): void {
+6
View File
@@ -273,6 +273,12 @@ function onSymbols(v: string): void {
</div>
</div>
<!-- #93: 名字只读预览对齐实盘autoName 选实例后生成,格式 {实例名}_v{YYYYMMDD}{minor} -->
<div style="margin-top:16px">
<span class="seg-label" style="margin-right:10px">模拟盘名称</span>
<span class="mono" style="font-size:13px;color:var(--brand)">{{ form.name || '—(选实例后自动生成)' }}</span>
</div>
</el-card>
<el-card class="blk" shadow="never">
+12 -4
View File
@@ -115,16 +115,24 @@ async function onSave(): Promise<void> {
})
saving.value = true
try {
await apiClient.post('/strategy/instances', {
id: editId.value,
const payload = {
code_file: form.code_file,
name: form.name,
// PUT 全量替换:type 按所选文件判定(漏传会被默认 cta 覆盖组合实例)
type: selectedCode.value?.type || 'cta',
params,
symbol_or_pool: form.symbol_or_pool,
interval: form.interval,
match_session: form.match_session,
})
ElMessage.success(isEdit.value ? '实例已更新(mock' : '实例已创建(mock')
}
// #91 编辑必须走 PUT:POST 恒为新建(原实现编辑保存再点就多出一个重复实例)
if (isEdit.value && editId.value) {
await apiClient.put(`/strategy/instances/${editId.value}`, payload)
ElMessage.success('实例已更新')
} else {
await apiClient.post('/strategy/instances', payload)
ElMessage.success('实例已创建')
}
router.push('/strategy')
} catch {
ElMessage.error('保存失败')
+1 -1
View File
@@ -318,7 +318,7 @@ const runningRows = computed(() =>
<span class="sub mono">#{{ i.id }} · {{ i.interval }} · 更新 {{ i.updated_at }}</span>
</div>
<div class="params mono muted">{{ paramSummary(i.params) }}</div>
<div class="sym mono">{{ i.symbol_or_pool || '—(发起时定)' }}</div>
<!-- #91: 标的行移除#86a 定稿实例不承载标的(发起时定),老实例存量值显示只会误导 -->
<div class="ret mono" :class="retClass(latestRet(i))">{{ pct(latestRet(i)) }}<span class="sub">{{ retInfoOf(i).label }}</span></div>
<div class="runs">
<button class="run-btn" @click="runGo(i, 'backtest')"><span class="lamp" :class="lampOf(i.status.backtest)"></span>回测{{ badge(i, 'backtest') }}</button>