summaryrefslogtreecommitdiff
path: root/frontend/src/pages
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-18 09:50:49 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-18 09:50:49 +0800
commite6ff0f697160ce152b363be97ecbeee13792cf6f (patch)
tree028cae07a7860242731518100ac1db024eac63a8 /frontend/src/pages
parent0b1ad75571b312dd60f9ae0099b47cc02ac54c78 (diff)
downloadstrategy-lab-e6ff0f697160ce152b363be97ecbeee13792cf6f.tar.gz
feat(frontend): 项目列表接入指标卡行与最近回测 sparkline 列
[变更性质] 页面集成,属于前端重设计第二轮功能延展。 [新增功能] ProjectsPage 引入 ProjectMetricRow 顶部指标卡,表格新增"最近回测"列内嵌 sparkline(无回测的项目不渲染)。 [实现方案] records 并行拉取 listDatasets/listRuns/getAIUsage 经 Promise.allSettled 容错;sparkline 按项目 id 懒取最近 result.equity。 [影响范围] ProjectsPage.svelte;新增真实渲染断言测试 projectSparkline.test.ts。
Diffstat (limited to 'frontend/src/pages')
-rw-r--r--frontend/src/pages/ProjectsPage.svelte34
1 files changed, 33 insertions, 1 deletions
diff --git a/frontend/src/pages/ProjectsPage.svelte b/frontend/src/pages/ProjectsPage.svelte
index 13525b6..c3d7e5e 100644
--- a/frontend/src/pages/ProjectsPage.svelte
+++ b/frontend/src/pages/ProjectsPage.svelte
@@ -1,8 +1,11 @@
<script lang="ts">
import Page from '../components/Page.svelte';
import Status from '../components/Status.svelte';
+ import ProjectMetricRow from '../components/ProjectMetricRow.svelte';
+ import Sparkline from '../components/Sparkline.svelte';
import { projectsStore } from '../lib/listsStore.svelte';
- import { createProject } from '../lib/client';
+ import { createProject, listDatasets, listRuns, getAIUsage } from '../lib/client';
+ import { sparkFirstLast } from '../lib/dashboardMetrics';
import { extractStatusMessage } from '../lib/state';
import { go } from '../lib/router';
import { fmtDateTime } from '../lib/format';
@@ -21,11 +24,30 @@
let newDesc = $state('');
let createErr = $state<string | null>(null);
+ // ---- round2 指标卡行输入 ----
+ // projectsStore 持有项目列表;其余三项在本页拉取一次。
+ // TODO: AI 用量若 /api/ai/usage 不给 totals.requests,占位 "-"(见 ProjectMetricRow)。
+ let datasets: { id: string }[] | null = $state(null);
+ let runs: { id: string; status: string; result?: { equity?: { equity: number }[] } | null; project_id?: string }[] | null = $state(null);
+ let usage: { totals?: { input_tokens?: number; output_tokens?: number; requests?: number } } | null = $state(null);
+ let sparks = $state(new Map<string, { first: number; last: number }>());
+
async function load() {
busy = true;
error = null;
try {
await projectsStore.load();
+ // 指标卡的三项远程输入并行拉取;任何一项失败不影响项目列表本身。
+ // TODO: 用量字段若有专门计数端点(总数/时长),替换 getAIUsage。
+ const [ds, runList, usageRes] = await Promise.allSettled([
+ listDatasets(),
+ listRuns(),
+ getAIUsage()
+ ]);
+ if (ds.status === 'fulfilled') datasets = Array.isArray(ds.value.items) ? ds.value.items : null;
+ if (runList.status === 'fulfilled') runs = runList.value.items;
+ if (usageRes.status === 'fulfilled') usage = usageRes.value;
+ if (runList.status === 'fulfilled') sparks = sparkFirstLast(runList.value.items ?? []);
} catch (e) {
error = extractStatusMessage(e);
} finally {
@@ -68,6 +90,7 @@
{/snippet}
<div class="stack">
+ <ProjectMetricRow projects={projectsStore.all} {datasets} {runs} {usage} />
<Status {busy} {error} busyText="加载项目列表…" empty={null} />
{#if showCreate}
<form class="card stack" onsubmit={create}>
@@ -99,6 +122,7 @@
<Table.Header>
<Table.Row>
<Table.Head>名称</Table.Head>
+ <Table.Head>最近回测</Table.Head>
<Table.Head>简介</Table.Head>
<Table.Head>版本代</Table.Head>
<Table.Head>最近更新</Table.Head>
@@ -106,10 +130,18 @@
</Table.Header>
<Table.Body>
{#each projectsStore.all as p (p.id)}
+ {@const sp = sparks.get(p.id)}
<Table.Row>
<Table.Cell>
<a href={`#/projects/${p.id}/data`} class="p-name">{p.name}</a>
</Table.Cell>
+ <Table.Cell>
+ {#if sp}
+ <a href={`#/projects/${p.id}/results`} aria-label="查看 {p.name} 最近回测曲线" data-testid="sparkline-cell">
+ <Sparkline first={sp.first} last={sp.last} up={sp.last >= sp.first} />
+ </a>
+ {/if}
+ </Table.Cell>
<Table.Cell class="hint">{p.description ?? '—'}</Table.Cell>
<Table.Cell class="num">{p.draft_generation ?? '—'}</Table.Cell>
<Table.Cell>{fmtDateTime(p.updated_at)}</Table.Cell>