1
0
Files
DiAL/openspec/changes/archive/2026-05-11-card-ui-refactor/design.md

123 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
当前 Dashboard 使用 `TargetTable` + `TargetRow` + `TargetDetail` 的表格布局,所有目标扁平排列在同一个表格中,点击行展开内联详情面板。前端组件结构为:
```
App → SummaryCards(4) + TargetTable → TargetRow → TargetDetail(内联)
```
后端 API 提供 `GET /api/targets` 返回含 `sparkline: number[]` 的目标列表,`GET /api/targets/:id/trend?hours=24` 返回趋势数据,`GET /api/targets/:id/history?limit=20` 返回历史记录。全局汇总含 `avgDurationMs`
本次重构涉及全栈变更YAML 配置格式、后端数据存储、API 接口、前端组件和样式。
## Goals / Non-Goals
**Goals:**
- 引入 target 分组概念按组展示卡片default 组排最前
- 卡片内同时展示状态条UP/DOWN 可视化)和迷你 sparkline耗时趋势
- 模态框提供丰富的详情查看体验:多维统计图 + 带分页的检查结果列表
- 模态框支持自定义时间范围筛选(分钟精度)和快捷时间范围按钮
- 移除无实际意义的全局平均耗时统计
- 统一卡片迷你可视化的采样数量为全局可配置项 `recentSampleCount`
- 不引入新依赖,复用现有 recharts 库
**Non-Goals:**
- 分组折叠/展开功能
- 分组排序自定义(固定为 default 最前,其余按 YAML 出现顺序)
- per-target 的 sparkline 数量自定义(统一使用全局配置)
- 模态框内的状态筛选(仅支持时间范围筛选)
- 卡片内显示可用率数字
- 按耗时阈值筛选
## Decisions
### D1: group 配置采用扁平字段(方案 A
**选择**: 在每个 target 上加 `group?: string` 可选字段。
**替代方案**: 嵌套结构(`targets: [{ group: "x", items: [...] }]`)。
**理由**: 扁平字段是增量变更,完全向后兼容,不破坏现有 targets 数组格式。嵌套结构会改变整个配置文件的顶层结构,影响面大且无额外收益。
### D2: sparkline 替换为 recentSamples 结构化数据
**选择**: 将 `sparkline: number[]` 替换为 `recentSamples: RecentSample[]`,每个 sample 包含 `timestamp``durationMs``up`
**替代方案**: 新增独立的 status-bar API。
**理由**: 合并为一个接口减少请求数,前端一次数据同时满足状态条和 sparkline 两种可视化。`timestamp` 的包含使得 hover tooltip 有意义。
### D3: recentSampleCount 固定为 30
**选择**: StatusBar 和 MiniSparkline 的采样数量硬编码为 30。
**理由**: 30 是合理的默认值,覆盖最近 30 次检查,无需暴露配置项增加复杂度。
### D4: 模态框时间筛选同时支持快捷按钮和自定义日期选择器
**选择**: 快捷按钮1h/6h/24h/7d与分钟精度日期选择器并存联动设计——点击快捷按钮自动填入日期手动修改日期则快捷按钮取消高亮。
**理由**: 快捷按钮覆盖绝大多数场景,日期选择器提供精确控制能力。分钟精度对于拨测监控场景足够精确。
### D5: trend API 改用 from/to 时间范围参数
**选择**: `GET /api/targets/:id/trend?from=ISO&to=ISO` 替代 `?hours=24`
**理由**: 模态框支持自定义时间范围hours 参数无法表达任意时间范围。from/to 是更通用的设计。
### D6: history API 新增分页支持
**选择**: `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20`,返回 `{ items, total, page, pageSize }`
**理由**: 自定义时间范围可能导致大量数据(如选择 7 天范围),分页避免一次性传输过多数据。
### D7: SummaryCards 从 4 个减为 3 个
**选择**: 移除"平均耗时"卡片,保留"全部/正常/异常"。
**理由**: 引入分组后,不同分组目标的平均耗时混合计算没有实际参考价值。具体目标的耗时信息在模态框中查看。
### D8: targets 表使用 grp 列名
**选择**: 数据库列名使用 `grp` 而非 `group`
**理由**: `group` 是 SQL 关键字,使用 `grp` 避免转义问题。API 层和前端仍使用 `group` 作为字段名。
### D9: 卡片固定宽度 280px + CSS Grid auto-fill 响应式
**选择**: `grid-template-columns: repeat(auto-fill, 280px)` 实现响应式布局。
**替代方案**: 百分比宽度或 flex-wrap。
**理由**: 固定宽度保证卡片内容一致性auto-fill 自动适应视口宽度变化,从 1 列到多列无缝适配。
### D10: 分组排序由后端 SQL 保证
**选择**: `ORDER BY CASE WHEN grp='default' THEN 0 ELSE 1 END, grp, id`
**理由**: 后端排序后前端只需顺序遍历渲染,无需额外排序逻辑。分组名按 YAML 首次出现顺序(即 id 顺序)自然排序。
### D11: 环形图Donut Chart展示状态分布
**选择**: 模态框统计图使用 recharts 的 PieChart + 内部标签实现环形图,中间显示可用率百分比。
**替代方案**: 纯饼图。
**理由**: 环形图中间可展示关键数字(可用率 %),信息密度更高。
### D12: 状态条使用连续色块
**选择**: 方块数量固定 30 个,每个 6px 宽 2px 间距UP 绿色 `#1fbf75`DOWN 红色 `#e5484d`,无数据灰色 `#e2e8f0`
**理由**: 类似 GitHub contribution graph 的可视化方式,直观展示最近检查状态。
## Risks / Trade-offs
- [卡片信息密度] 卡片宽度仅 280px同时放状态条和 sparkline 可能显得拥挤 → 状态条和 sparkline 各占一行,垂直堆叠,控制高度在合理范围
- [API BREAKING 变更] sparkline → recentSamples、trend hours → from/to、history limit → page/pageSize 均为不兼容变更 → 项目未上线无需向前兼容,一次性完成
- [targets 表 schema 变更] 新增 grp 列需要数据库 migration → SQLite ALTER TABLE ADD COLUMN 是安全操作,新列有默认值不影响已有数据
- [模态框复杂度] 时间选择器 + 分页 + 多图表实现复杂度较高 → 拆分为独立子组件,每个组件职责单一
- [recentSampleCount 默认值] 固定为 30无法通过配置调整 → 合理值30 覆盖足够长的最近检查周期