From 9f834d76625d857afc38e98bacc7ff8f70c92706 Mon Sep 17 00:00:00 2001 From: claude_dev Date: Sun, 16 Aug 2026 19:56:40 +0800 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20#91+#92+#93=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E4=B8=89=E8=BF=9E=E4=BF=AE=E2=80=94=E2=80=94#91:=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E7=BC=96=E8=BE=91=E4=BF=9D=E5=AD=98=E5=8F=98=E6=96=B0?= =?UTF-8?q?=E5=BB=BA(onSave=E6=97=A0=E8=AE=BA=E7=BC=96=E8=BE=91/=E6=96=B0?= =?UTF-8?q?=E5=BB=BA=E9=83=BDPOST=3D=E5=BB=BA=E6=96=B0=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B;=E6=94=B9=E5=B8=A6editId=E8=B5=B0PUT=20/stra?= =?UTF-8?q?tegy/instances/{id},payload=E8=A1=A5type=E6=8C=89=E6=89=80?= =?UTF-8?q?=E9=80=89=E6=96=87=E4=BB=B6=E5=88=A4=E5=AE=9A=E9=98=B2PUT?= =?UTF-8?q?=E5=85=A8=E9=87=8F=E6=9B=BF=E6=8D=A2=E6=8A=8A=E7=BB=84=E5=90=88?= =?UTF-8?q?=E5=AE=9E=E4=BE=8B=E6=94=B9=E5=86=99cta;=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E6=B8=85(mock)=E6=AE=8B=E7=95=99=E6=96=87?= =?UTF-8?q?=E6=A1=88)+=E5=8D=A1=E7=89=87=E6=A0=87=E7=9A=84=E8=A1=8C?= =?UTF-8?q?=E6=95=B4=E4=BD=93=E7=A7=BB=E9=99=A4(#86a=E5=AE=9E=E4=BE=8B?= =?UTF-8?q?=E4=B8=8D=E6=89=BF=E8=BD=BD=E6=A0=87=E7=9A=84,=E5=AD=98?= =?UTF-8?q?=E9=87=8Fall=E6=98=BE=E7=A4=BA=E8=AF=AF=E5=AF=BC);#92:=E6=A0=87?= =?UTF-8?q?=E7=9A=84=E6=B1=A0=E6=98=BE=E7=A4=BA=E7=BB=9F=E4=B8=80=E2=80=94?= =?UTF-8?q?=E2=80=94constants/strategy.ts=E6=96=B0=E5=A2=9EPOOL=5FLABELS+p?= =?UTF-8?q?oolLabel(all=E2=86=92=E5=85=A8=E5=B8=82=E5=9C=BA/hs300=5Fsubset?= =?UTF-8?q?=E2=86=92HS300=E5=AD=90=E9=9B=86/ZZ500=E2=86=92=E4=B8=AD?= =?UTF-8?q?=E8=AF=81500=E7=AD=89),=E4=BB=BB=E5=8A=A1=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=A0=87=E7=9A=84=E5=88=97+=E6=A8=A1=E6=8B=9F=E7=9B=98?= =?UTF-8?q?=E5=88=97=E8=A1=A8symbolsShort=E5=85=A8=E8=B5=B0=E6=98=A0?= =?UTF-8?q?=E5=B0=84(=E8=82=A1=E7=A5=A8=E4=BB=A3=E7=A0=81=E5=8E=9F?= =?UTF-8?q?=E6=A0=B7);#93:=E6=96=B0=E5=BB=BA=E6=A8=A1=E6=8B=9F=E7=9B=98?= =?UTF-8?q?=E5=8A=A0=E5=90=8D=E5=AD=97=E5=8F=AA=E8=AF=BB=E9=A2=84=E8=A7=88?= =?UTF-8?q?(=E5=AF=B9=E9=BD=90=E5=AE=9E=E7=9B=98:=E9=80=89=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E5=90=8E=E6=98=BE{=E5=AE=9E=E4=BE=8B=E5=90=8D}=5Fv{YY?= =?UTF-8?q?YYMMDD}{minor},=E6=9C=AA=E9=80=89=E6=98=BE=E5=8D=A0=E4=BD=8D?= =?UTF-8?q?=E6=8F=90=E7=A4=BA);build=E7=BB=BF(vue-tsc)=20[nas]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/constants/strategy.ts | 18 ++++++++++++++++++ frontend/src/views/backtest/History.vue | 5 ++++- frontend/src/views/paper/List.vue | 5 ++++- frontend/src/views/paper/New.vue | 6 ++++++ frontend/src/views/strategy/InstanceEdit.vue | 16 ++++++++++++---- frontend/src/views/strategy/List.vue | 2 +- 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/frontend/src/constants/strategy.ts b/frontend/src/constants/strategy.ts index 6fa3380..6fa6ad9 100644 --- a/frontend/src/constants/strategy.ts +++ b/frontend/src/constants/strategy.ts @@ -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 = { + 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 +} diff --git a/frontend/src/views/backtest/History.vue b/frontend/src/views/backtest/History.vue index 7e6324a..fc9f690 100644 --- a/frontend/src/views/backtest/History.vue +++ b/frontend/src/views/backtest/History.vue @@ -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 { - + + + diff --git a/frontend/src/views/paper/List.vue b/frontend/src/views/paper/List.vue index ae2fbba..b9f6f88 100644 --- a/frontend/src/views/paper/List.vue +++ b/frontend/src/views/paper/List.vue @@ -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 = { cta: '个股', portfolio: '组合' } function open(a: PaperAccount): void { diff --git a/frontend/src/views/paper/New.vue b/frontend/src/views/paper/New.vue index 8382cc3..435ed80 100644 --- a/frontend/src/views/paper/New.vue +++ b/frontend/src/views/paper/New.vue @@ -273,6 +273,12 @@ function onSymbols(v: string): void { + +
+ 模拟盘名称 + {{ form.name || '—(选实例后自动生成)' }} +
+ diff --git a/frontend/src/views/strategy/InstanceEdit.vue b/frontend/src/views/strategy/InstanceEdit.vue index 393eafc..975a8e6 100644 --- a/frontend/src/views/strategy/InstanceEdit.vue +++ b/frontend/src/views/strategy/InstanceEdit.vue @@ -115,16 +115,24 @@ async function onSave(): Promise { }) 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('保存失败') diff --git a/frontend/src/views/strategy/List.vue b/frontend/src/views/strategy/List.vue index 50d4de2..755b473 100644 --- a/frontend/src/views/strategy/List.vue +++ b/frontend/src/views/strategy/List.vue @@ -318,7 +318,7 @@ const runningRows = computed(() => #{{ i.id }} · {{ i.interval }} · 更新 {{ i.updated_at }}
{{ paramSummary(i.params) }}
-
{{ i.symbol_or_pool || '—(发起时定)' }}
+
{{ pct(latestRet(i)) }}{{ retInfoOf(i).label }}