From 45ecb4e9dfe225bc982e9af9315590bcb0783df7 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Fri, 18 Sep 2026 08:26:57 +0800 Subject: feat(frontend): 结果/实验/策略/数据页迁移 shadcn 与新 Chart API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [变更性质] 四个业务标签页与数据集卡片迁移 shadcn 组件,图表消费切换到 layerchart 长格式 API,功能与数据语义不变。 [新增功能] 结果页权益曲线(策略权益/现金/基准,基准可缺省)与回撤曲线(desc 面积)改由 Chart.svelte data/series 渲染;实验记录页多回测归一对比 曲线改为快速长格式行构造。空数据行允许 undefined 列(基准缺口), Chart data 类型放宽 [key:string]: string|number|undefined。 [实现方案] RunsPage:查看入口改 shadcn Button(href 语义保留 hash 路由), 对比图数据由 compareOption(echarts option)化为 compare。 ProjectResultsTab:图表开关、日志折叠用 Button;equityData/ drawdownData $derived 计算峰值回撤。ProjectStrategyTab:快照输入、 冲突/AI/版本操作迁移 Button/Input。ProjectDataTab 与 DatasetCard: shadcn 组件 + 保留 banner error/warn/info、retry-row 等 legacy class, "使用相同请求重新获取"重试入口与 onRecreated 逻辑不变,配套 datasetCardRetry/datasetCardWarnings/projectDataTabRecreated 回归 测试。resultsAccounting.test.ts:layerchart 在 jsdom 挂载约 3 秒, suite 全量下易撞默认 5s 超时,首测提高至 20s。 [影响范围] npm run check 0 error、58 tests 全绿、npm run build 通过;真浏览器 375/768/1440 图表页零溢出(artifacts/ui-shadcn 截图与探针 JSON)。 --- frontend/src/pages/RunsPage.svelte | 59 ++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 28 deletions(-) (limited to 'frontend/src/pages/RunsPage.svelte') diff --git a/frontend/src/pages/RunsPage.svelte b/frontend/src/pages/RunsPage.svelte index bd77892..154fd67 100644 --- a/frontend/src/pages/RunsPage.svelte +++ b/frontend/src/pages/RunsPage.svelte @@ -2,7 +2,7 @@ import Page from '../components/Page.svelte'; import Status from '../components/Status.svelte'; import Chart from '../components/Chart.svelte'; - import type { EChartsOption } from 'echarts'; + import { Button } from '$lib/components/ui/button'; import { listRuns } from '../lib/client'; import { extractStatusMessage } from '../lib/state'; import { fmtDateTime, runStatusLabel, runStatusBadge, fmtPct, fmtNum, formatMoney } from '../lib/format'; @@ -40,31 +40,34 @@ else chosenIds = [...chosenIds.slice(-3), id]; } - const compareOption: EChartsOption = $derived.by(() => { - if (comparable.length === 0) return {}; - const series = comparable.map((r, idx) => { - const eq = r.result?.equity ?? []; - const base = eq[0]?.equity ?? 1; - const colors = ['#0f766e', '#b45309', '#1d4ed8', '#8b1d8b']; - return { - name: `Run ${r.id.slice(0, 6)}.`, - type: 'line' as const, - data: eq.map((p) => p.equity / base), - showSymbol: false, - lineStyle: { width: 1.5 }, - itemStyle: { color: colors[idx % colors.length] } - }; - }); - let xLen = Math.max(...comparable.map((r) => (r.result?.equity ?? []).length), 0); - const longest = comparable.reduce((best, r) => ((r.result?.equity ?? []).length > best.length ? r.result!.equity : best), [] as { date: string }[]); - return { - legend: { top: 0 }, - grid: { top: 32, left: 64, right: 20, bottom: 50 }, - tooltip: { trigger: 'axis' }, - xAxis: { type: 'category', data: longest.map((p) => p.date) }, - yAxis: { type: 'value', scale: true, name: '归一净值' }, - series - }; + // long-format rows for the layerchart renderer: one row per index, one + // column per selected run, first-day-normalized equity + const compare = $derived.by(() => { + if (comparable.length === 0) return { data: [], series: [] }; + const colors = ['#0f766e', '#b45309', '#1d4ed8', '#8b1d8b']; + const flat = comparable.map((r, i) => ({ + run: r, + eq: r.result?.equity ?? [], + key: `s${i}`, + color: colors[i % colors.length] + })); + const len = Math.max(0, ...flat.map((f) => f.eq.length)); + const data: { [key: string]: string | number; i: number }[] = []; + for (let i = 0; i < len; i++) { + const row: { [key: string]: string | number; i: number } = { i, date: flat[0].eq[i]?.date ?? String(i) }; + for (const f of flat) { + const base = f.eq[0]?.equity ?? 1; + if (f.eq[i]) row[f.key] = f.eq[i].equity / base; + } + data.push(row); + } + const series = flat.map((f) => ({ + key: f.key, + label: `Run ${f.run.id.slice(0, 6)}.`, + value: f.key, + color: f.color + })); + return { data, series }; }); const configDiffRows = $derived.by(() => { @@ -133,7 +136,7 @@ {r.result ? fmtPct(r.result.metrics.total_return) : '—'} {r.result ? fmtPct(r.result.metrics.max_drawdown) : '—'} - 查看 + {/each} @@ -168,7 +171,7 @@ {#if comparable.length > 1}

收益曲线对齐比较(首日归一)

- +

起点对齐为 1,仅叠加不同起点的曲线时只在各自区间内展示。

{:else} -- cgit v1.2.3