diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-18 12:03:32 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-18 12:03:32 +0800 |
| commit | 61f5c545953bb4ee20b123bf9671ac35d7f1f3c1 (patch) | |
| tree | 9ede3483db5f7e0d81189f2d29514892b8f77867 /frontend/src/pages/ProjectResultsTab.svelte | |
| parent | 1f16a357213611a6d7f84e506064fc0d9c6317c6 (diff) | |
| download | strategy-lab-61f5c545953bb4ee20b123bf9671ac35d7f1f3c1.tar.gz | |
[变更性质] feat(frontend)
[新增功能] round3 页面组件全量迁移 shadcn-svelte(12 页面 + Modal/DatasetWizard/DatasetCard/Status)
[实现方案] .btn→Button、.banner→Alert、table.data→Table.Root、.badge→Badge、.tabs→Tabs.Root、.metric-card/form.card→Card.Root;ProjectStrategyTab div 配对修复
[影响范围] src/pages/*.svelte、src/components/*.svelte
Diffstat (limited to 'frontend/src/pages/ProjectResultsTab.svelte')
| -rw-r--r-- | frontend/src/pages/ProjectResultsTab.svelte | 131 |
1 files changed, 52 insertions, 79 deletions
diff --git a/frontend/src/pages/ProjectResultsTab.svelte b/frontend/src/pages/ProjectResultsTab.svelte index c08bf3f..82a7009 100644 --- a/frontend/src/pages/ProjectResultsTab.svelte +++ b/frontend/src/pages/ProjectResultsTab.svelte @@ -15,6 +15,10 @@ metricsRow } from '../lib/format'; import { capitalReporting } from '../lib/capitalMetrics'; + import { Alert } from '$lib/components/ui/alert'; + import { Badge } from '$lib/components/ui/badge'; + import * as Card from '$lib/components/ui/card'; + import * as Table from '$lib/components/ui/table'; import type { Run, BacktestResult } from '../lib/types'; interface Props { @@ -117,7 +121,7 @@ {#if run} <span class="row"> <span class="hint">Run {run.id.slice(0, 8)}…</span> - <span class="badge {runStatusBadge[run.status]}">{runStatusLabel[run.status]}</span> + <Badge variant={runStatusBadge[run.status] === 'ok' ? 'secondary' : runStatusBadge[run.status] === 'warn' ? 'outline' : 'destructive'}>{runStatusLabel[run.status]}</Badge> </span> {/if} </div> @@ -141,67 +145,38 @@ {/if} {#if run.error} - <div class="banner error" role="alert"><span aria-hidden="true">✕</span><span>{run.error}</span></div> + <Alert variant="destructive" class="bg-[var(--danger-soft)] border-[#f2c7c7]"><span aria-hidden="true">✕</span><span>{run.error}</span></Alert> {/if} {#if !result} <Status busy={false} empty="该回测尚无结果" emptyHint={runStatusBadge[run.status] === "danger" ? "见错误提示" : "回测仍在执行中,稍后刷新"} /> {:else} {#if result.warnings?.length} - <div class="banner warn" role="alert"> + <Alert variant="default" class="bg-[var(--warn-soft)] border-[#f0ddb4]"> <span aria-hidden="true">⚠</span> - <div> - <ul class="ul-plain"> - {#each result.warnings as w, i (i)}<li>{w}</li>{/each} - </ul> - </div> - </div> + <ul class="ul-plain"> + {#each result.warnings as w, i (i)}<li>{w}</li>{/each} + </ul> + </Alert> {/if} - <div class="card"> + <Card.Root size="sm"> {#if result.metrics} {@const mrow = result.metrics} <div class="metric-grid"> - <div class="metric"> - <div class="mlabel">总收益率</div> - <div class="mvalue">{fmtPct(mrow.total_return)}</div> - </div> - <div class="metric"> - <div class="mlabel">年化收益率</div> - <div class="mvalue">{fmtPct(mrow.annual_return)}</div> - </div> - <div class="metric"> - <div class="mlabel">最大回撤</div> - <div class="mvalue">{fmtPct(mrow.max_drawdown)}</div> - </div> - <div class="metric"> - <div class="mlabel">夏普比率</div> - <div class="mvalue">{fmtNum(mrow.sharpe)}</div> - </div> - <div class="metric"> - <div class="mlabel">平仓回合数</div> - <div class="mvalue">{mrow.trade_count ?? "—"}</div> - </div> - <div class="metric"> - <div class="mlabel">期末权益</div> - <div class="mvalue">{formatMoney(mrow.final_equity)}</div> - </div> + <Card.Root size="sm" class="m-tile"><div class="mlabel">总收益率</div><div class="mvalue">{fmtPct(mrow.total_return)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">年化收益率</div><div class="mvalue">{fmtPct(mrow.annual_return)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">最大回撤</div><div class="mvalue">{fmtPct(mrow.max_drawdown)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">夏普比率</div><div class="mvalue">{fmtNum(mrow.sharpe)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">平仓回合数</div><div class="mvalue">{mrow.trade_count ?? "—"}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">期末权益</div><div class="mvalue">{formatMoney(mrow.final_equity)}</div></Card.Root> </div> {/if} {#if cap} <div class="metric-grid" data-testid="capital-reporting"> - <div class="metric"> - <div class="mlabel">累计投入</div> - <div class="mvalue">{formatMoney(cap.invested)}</div> - </div> - <div class="metric"> - <div class="mlabel">最终持仓估值</div> - <div class="mvalue">{formatMoney(cap.holdingValue)}</div> - </div> - <div class="metric"> - <div class="mlabel">投入回报率</div> - <div class="mvalue">{cap.returnPct ?? '—'}</div> - </div> + <Card.Root size="sm" class="m-tile"><div class="mlabel">累计投入</div><div class="mvalue">{formatMoney(cap.invested)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">最终持仓估值</div><div class="mvalue">{formatMoney(cap.holdingValue)}</div></Card.Root> + <Card.Root size="sm" class="m-tile"><div class="mlabel">投入回报率</div><div class="mvalue">{cap.returnPct ?? '—'}</div></Card.Root> </div> <p class="hint">投入口径不含初始账户闲置现金:累计投入 = 首日现金 − 末日现金;持仓估值 = 末日持仓 × 最新收盘价。</p> {/if} @@ -211,9 +186,9 @@ {#if result.elapsed_ms}<span> · 耗时 {(result.elapsed_ms / 1000).toFixed(1)}s</span>{/if} {#if result.peak_rss_kb}<span> · 峰值内存 {fmtNum(result.peak_rss_kb / 1024)} MB</span>{/if} </p> - </div> + </Card.Root> - <div class="card stack"> + <Card.Root size="sm" class="stack"> <div class="row"> <Button variant={chartMode === "equity" ? "default" : "outline"} size="sm" aria-pressed={chartMode === "equity"} onclick={() => (chartMode = "equity")}>权益曲线({result.equity.length} 点)</Button> <Button variant={chartMode === "drawdown" ? "default" : "outline"} size="sm" aria-pressed={chartMode === "drawdown"} onclick={() => (chartMode = "drawdown")}>回撤曲线</Button> @@ -223,42 +198,40 @@ {:else} <Chart data={drawdownData} series={drawdownSeries} ariaLabel="回撤曲线" height={300} valueFormatter={(v) => (typeof v === 'number' ? `${v.toFixed(2)}%` : String(v))} /> {/if} - </div> + </Card.Root> - <div class="card stack"> + <Card.Root size="sm" class="stack"> <h2>订单与持仓记录</h2> <div class="cols-2"> <div> <h3>成交记录 ({result.trades.length})</h3> {#if result.trades.length > 0} - <div class="table-wrap"> - <table class="data"> - <thead> - <tr> - <th>日期</th> - <th>标的</th> - <th>方向</th> - <th>数量</th> - <th>价格</th> - <th>金额</th> - <th>佣金</th> - </tr> - </thead> - <tbody> - {#each result.trades.slice(0, 80) as t, i (i)} - <tr> - <td>{t.date}</td> - <td class="num">{t.symbol}</td> - <td>{t.side === "buy" ? "买入" : "卖出"}</td> - <td class="num">{t.quantity}</td> - <td class="num">{fmtNum(t.price, 3)}</td> - <td class="num">{fmtNum(t.value)}</td> - <td class="num">{fmtNum(t.commission, 4)}</td> - </tr> - {/each} - </tbody> - </table> - </div> + <Table.Root> + <Table.Header> + <Table.Row> + <Table.Head>日期</Table.Head> + <Table.Head>标的</Table.Head> + <Table.Head>方向</Table.Head> + <Table.Head>数量</Table.Head> + <Table.Head>价格</Table.Head> + <Table.Head>金额</Table.Head> + <Table.Head>佣金</Table.Head> + </Table.Row> + </Table.Header> + <Table.Body> + {#each result.trades.slice(0, 80) as t, i (i)} + <Table.Row> + <Table.Cell>{t.date}</Table.Cell> + <Table.Cell class="num">{t.symbol}</Table.Cell> + <Table.Cell>{t.side === "buy" ? "买入" : "卖出"}</Table.Cell> + <Table.Cell class="num">{t.quantity}</Table.Cell> + <Table.Cell class="num">{fmtNum(t.price, 3)}</Table.Cell> + <Table.Cell class="num">{fmtNum(t.value)}</Table.Cell> + <Table.Cell class="num">{fmtNum(t.commission, 4)}</Table.Cell> + </Table.Row> + {/each} + </Table.Body> + </Table.Root> {#if result.trades.length > 80} <p class="hint">仅显示前 80 条,共 {result.trades.length} 条</p> {/if} @@ -283,7 +256,7 @@ 结果由对应策略代码、配置、数据清单(数据哈希 {result.data_manifest_hash?.slice(0, 12)})以及当时引擎共同决定。 </p> </div> - </div> + </Card.Root> {/if} {/if} |
