blob: 69c990ae6e8872f77e5f6970134ebcd41a0b8145 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
# 前端重设计 · 第二轮交付说明
状态:**READY_FOR_LEADER_REVIEW**(2026-09-18)
## 一、范围回顾
Leader 指派第二轮前端重设计四项任务,全部交付:
1. **首页指标卡行 + 项目行 sparkline**
2. **375px 底部 Tab Bar / 窄标题栏 / 面包屑**
3. **图表增强**(网格、hover 当日净值+回撤、回测区间阴影带、移动端图表 ≥240px)
4. **质感打磨**(150ms 微过渡、卡片 hover 上浮、登录页留白、字阶统一)
硬约束全部满足:续用 shadcn 组件与 layerchart,未引入新框架;后端零改动;vitest 全绿。
## 二、实现明细
### 1. 指标卡与 sparkline
- `lib/dashboardMetrics.ts`:`aggregateDashboard` 把 projects/datasets/runs/AI 用量聚合为 4 张指标卡数据;无数据以 `-` 占位(data-todo 标注)。AI 用量 `totals.requests` 暂无独立计数字段时以 `-` 呈现。
- `components/ProjectMetricRow.svelte`:指标卡行组件(props 注入聚合数据,便于测试)。
- `components/Sparkline.svelte`:仅依赖 `first/last` 两个字段的方向 mini 线段(up/down 配色 --ok/--danger,端点圆点),首末任一为空则不渲染 SVG。
- `ProjectsPage.svelte`:顶部接入 `ProjectMetricRow`;表格新增「最近回测」列内嵌 sparkline(`data-testid="sparkline-cell"`,作为结果链接的 aria-label 入口);并行取数经 `Promise.allSettled` 容错。
### 2. 移动端导航(≤767px)
- `components/Layout.svelte`:`matchMedia('(max-width: 767px)')` 反应式 `$effect`(Safari <14 走 `addListener` 兼容回退)。手机态渲染:
- `data-testid="mobile-tabbar"` 底部 Tab Bar:projects/datasets/runs/account 四项,svg 图标 + `tab-label`,选中态 `aria-current="page"`;
- `data-testid="mobile-topbar"` 窄标题栏:品牌 / 项目面包屑 / 账户入口;
- 内容区底部 padding 5.2rem 预留 Tab Bar 高度。
- ≥768px 维持既有桌面布局;900px 断点结构保留(`lib/mobileCss.test.ts` 正则依赖)。
- `ProjectPage.svelte` 新增 `nav.crumbs` 面包屑:我的项目 / 项目名 / 当前页签(`projectsStore.ensure(id)` 拉项目名)。
### 3. 图表增强
`components/Chart.svelte` 扩展(round1 合同不变:`resolvedWidth = measured ?? width ?? 640`、`data` 长格式、`chart-host`):
- 网格线 stroke 使用 `var(--border)`;
- hover 高亮(lines+points)+ 自定义 tooltip:当日净值 + 额外行(`extras`,如回撤),可注入 `valueFormatter`;
- `bandShaded`(默认开):belowMarks 画 `rgba(15,118,110,0.05)` 回测区间阴影带;
- series 新支持 `dashed`(`strokeDasharray: 6 6`)与 `opacity`,`Area` 填充渲染;
- `ProjectResultsTab`:equity 合并回撤列(`extras=[{key:'dd',label:'回撤'}]`),drawdown 图百分比格式化。
- 自定义 marks/tooltip 以 `lcExtras` snippet 透传绕开 TS 精确字面量检查;`chart-assurance` 伪属性已清理。
### 4. 质感打磨
- `app.css`:全元素 150ms transition 层;仅 `hover:hover` 且 768px+ 生效的卡片 hover 上浮;`h1/h2/h3` 字阶 1.55/1.18/1.02;`.hint` 重复规则清理。
- `AuthPages.svelte`:登录/注册页留白重排(2rem 窄屏 / 2.4rem 宽屏断点)。
## 三、验证证据
| 项 | 结果 |
| --- | --- |
| `npx vitest run` | **75/75 通过**(23 个测试文件;含新增 dashboardMetrics×5、sparkline×3、mobileTabbar×3、metricRow×3、projectSparkline×1、round2DashboardIntegration×2) |
| `npm run check`(svelte-check + tsc) | 0 error 0 warning |
| `npm run build` | 成功,`dist/` 已更新(`index-DUEbs4Rg.js` 起) |
| `scripts/ui-verify.mjs` | 30 route×宽度组合全 OK(overflow=0 / pageErrors=0) |
| `scripts/chart-overflow-probe.mjs` | runs/results × 375/768/1440 全 OK(svgs 17/16/25) |
| `scripts/ui-auth-shots.mjs`(新增) | 登录态 18 组合全 OK:overflow=0、pageErrors=0、375 端 tabbar=true 且 768/1440 为 false(形态正确)、项目页 sparklineCells=2、metricCards=4(真实数据渲染) |
| 截图 | `artifacts/ui-shadcn-round2/*.png`:probe 6 张 + dashboards 3 张 + 登录态 18 张 |
`ui-auth-shots.mjs` 说明:以生产站点实名凭据(admin.private.json)登录生产站点换取 HttpOnly 会话,经本地 dist 服务器 + `/api` 反向代理访问真实后端数据;代理修补了 `accept-encoding`(防 gzip 直传)与 `Set-Cookie`(去 Secure 后注入本地域);密码不落任何日志/截图。
## 四、提交清单(本轮 6 个 commit)
- `0b1ad75` feat(frontend): 首页指标卡与项目行 sparkline
- `e6ff0f6` feat(frontend): 项目列表接入指标卡行与最近回测 sparkline 列
- `4390ca3` feat(frontend): 375px 底部 Tab Bar、窄标题栏与项目面包屑
- `ac55af5` feat(frontend): 图表增强——网格、hover 当日净值/回撤、回测区间阴影带
- `4462473` feat(frontend): 质感打磨——微过渡、卡片悬停、登录页留白与字阶
- `5c98e8b` test(frontend): 登录态三档宽度真浏览器截图与渲染断言脚本
## 五、验收返工记录(2026-09-18 第二轮)
Leader 独立验收发现三处问题,已修复:
### 返工 1(高):tabbar=false 根因与 probe 健壮化
**根因**:原 `ui-auth-shots.mjs` 默认不带 `AUTH_SHOTS_DIST` 时直接打生产站点——生产尚在跑**旧构建**(无 Tab Bar),因此 Leader 复跑得到 18 组合全 false(tabbar=false / sparks=0 / metricCards=0)且先覆盖了我本地产物 JSON。脚本本身:先前还用固定 `waitForTimeout`,断言可能早于挂载。
**修复**:
- 脚本内置 `npm run build`(无 `SKIP_BUILD=1` 时强制先建,`_SKIP_BUILD=1` 才免),启动前输出所服务的 dist 资产哈希(当前 `index-BmY9NAYz.js / index-6tSy0ZnS.css`),绝不可能再 service 旧 dist;
- 一律以 `waitForSelector('[data-testid="mobile-tabbar"], .sidebar')` 条件等待挂载,替换固定 timeout;
- 断言直接输出 DOM 事实:`tabbarAttached` 与 `tabbarVisible`(attached+display!==none)两个独立值、面包屑 `nav.crumbs` 文本;
- 每轮先经 `/api/auth/me` 断言会话有效(me=200),失败即 FATAL 退出而非静默降级。
**实测证据(最新一轮,dist 资产 index-BmY9NAYz.js)**:
| 指标 | 375 | 768 | 1440 |
| --- | --- | --- | --- |
| tabbarAttached | true | false | false |
| tabbarVisible | **true** | false | false |
| metricCards | 4 | 4 | 4 |
| sparklineCells | 2 | 2 | 2 |
| overflow / pageErrors | 0 / 0 | 0 / 0 | 0 / 0 |
18/18 组合 `bad=0`,`artifacts/ui-shadcn-round2/auth-shots.json` 为真值数据。
### 返工 2(中):metricCards/sparks 断言条件明确化
原报告 `metricCards=4 sparks=2` 对应 **`#/projects` 首页**(375/768/1440 三档相同),本轮已把断言改为**真实数据驱动**:probe 先拉取 `/api/auth/me`、`/api/projects`、`/api/runs`、`/api/ai/usage` 并记录前置条件 —— 当前生产数据:projects=4、runs=3、含 `result.equity`(长度≥2)的 run=2 → sparks=2 正确;若某轮生产数据 projects=0 则会显式 `SKIP proj-*` 而非恒假断言。前置条件连同每次断言一并写入 `auth-shots.json` 的 `preconditions` 字段。
### 返工 3(低):AI 用量与面包屑
- 核查后端 `server/src/ai.rs:17 list_ai_usage`:`/api/ai/usage` **本来就返回** `totals.requests`(ai_usage COUNT)。前端 `dashboardMetrics.ts` 实际早已消费该字段,之前 docs 表述有误;现指标卡展示真实计数(本轮生产数据 usageRequests=0 → 显示「0 次」),`-` 占位仅在该端点未返回 totals 时兜底,`data-todo` 文案已更正。
- 面包屑已加断言:probe 对 proj-data/strategy/results 三页直接输出 `crumbs="我的项目 / 现金流ETF定投POC / 数据|策略|结果"`,18/18 通过。
### 本轮返工 commit
- fix(frontend): ui-auth-shots 构建先行 + 条件等待 + DOM 事实断言(含前置条件记录)
- fix(frontend): AI 用量占位文案与 docs 更正
- docs(frontend): 返工证据补充
## 六、遗留项 / 建议
1. Sparkline 目前为两点方向线段;若要更像"曲线"可后续接入折线渲染(保持不引依赖仍可行)。
2. 生产部署:本轮为纯前端构建产物 + 脚本,部署流程与前轮一致(deploy.sh 幂等拒重复部署),未重复执行生产发布;_leader 审核后按需发布_。
|