缺少代码差异信息,无法生成具体的commit message。

请提供 "code differences" 的具体内容,以便我能够根据代码变更情况生成符合 Angular 规范的中文 commit message。
This commit is contained in:
link2026
2026-06-07 09:40:59 +08:00
parent 675c33bea1
commit 60b6ad6d65
18 changed files with 1552 additions and 299 deletions

View File

@@ -0,0 +1,176 @@
# 趋势大改 + 健康日历移至主页 — 设计文档
> 日期:2026-06-07 · 状态:已定方案(用户授权直接实现,免确认)
## 1. 背景与目标
当前「趋势」Tab(`TrendsView.swift`)把两件事混在一起:
1. **健康日历**(月/年视图 + 当日详情)—— 占据页面上半部分。
2. **长期监测折线图**(`seriesSection`)—— 页面下半部分。
两个问题:
- **日历放错了地方**。它是「总览记录情况」的入口,更适合放在主页(用户每天第一眼看的页面),而不是埋在趋势 Tab 里。
- **趋势能力太弱**。`SeriesBucket.build` **只按 `seriesKey` 分桶**,因此只有 8 个长期监测预设(血压/血糖/体温…)和自定义指标能成图。所有**没有 seriesKey 的指标**——报告里解析出来的化验项、VL 快拍、自由输入——即使在多份报告里反复出现(如「血红蛋白」体检了 3 次),也**完全看不到趋势**。
### 目标
1. **健康日历移到主页**:主页新增一张紧凑的「健康日历」卡(当前周的横条 + 本月记录摘要),点击展开完整的月/年总览页(可切月视图/年视图、看当日详情)。
2. **趋势 Tab 重构**:对**任何出现 ≥2 次的指标**(不限于长期监测预设)做时间序列查看。趋势页变成一个「可成趋势的指标」总览列表(分长期监测 / 化验指标两段),点任一项进入详情页:大图表 + 参考范围带 + 统计摘要(最新/最高/最低/平均/对比上次)+ 时间范围筛选 + 数据点列表(点击跳当日详情)。
### 非目标(本次不做)
- **AI 趋势解读**:需要 AIRuntime + TrendService 跑通,风险大、与本次「时间序列查看」正交。本次预留 UI 位但不接 LLM,留作后续。
- 不改 SwiftData schema(无 @Model 字段变更,规避迁移丢数据风险)。
- 不改 `Localizable.xcstrings`(新文案用 `String(appLoc: "中文")`,无对应词条时优雅回退到中文 key,符合既有大量用法;避免 xcstrings 噪声 diff)。
- 不动 TabBar 5 槽骨架、不动录入流程。
## 2. 架构总览
```
主页 HomeView
└─ HomeCalendarCard(自包含 @Query) ← 新增
当前周横条 + "本月 N 天有记录" + chevron
tap → fullScreenCover(CalendarOverviewView) ← 新增(从 TrendsView 抽出)
趋势 TrendsView(重写)
└─ TrendSeriesList:两段 section
├─ 长期监测(kind=.monitor:seriesKey 分桶,含血压合并/自定义)
└─ 化验指标趋势(kind=.lab:按 name+unit 分桶,≥2 点)
每行 TrendRow:名称 + 最新值/状态 + mini sparkline + 条数·跨度
tap → TrendDetailView(bucket) ← 新增
大图表 + 参考范围带 + 时间范围 chips + 统计摘要 + 数据点列表
数据点 tap → DayDetailSheet(date)(复用)
```
数据层只扩展 `SeriesBucket.build`,UI 层新增 4 个文件、改 2 个文件、删 1 段。
## 3. 数据层:`SeriesBucket` 扩展
文件:`Features/Trends/SeriesBucket.swift`(改)
### 3.1 新增 `kind` 区分两段
```swift
enum SeriesKind { case monitor, lab } // monitor=//;lab=
struct SeriesBucket: Identifiable {
let id: String
let title: String
let unit: String
let lines: [SeriesLine]
let latestDate: Date
let kind: SeriesKind //
let sourceIndicatorIDs: [String] // : Indicator persistentModelID ,
// ... SeriesLine / Point
}
```
### 3.2 `build` 流程改为两段
1. **seriesKey 段(原逻辑,kind=.monitor)**:血压合并、单系列预设、自定义。这些桶里的 Indicator 标记为「已消费」。
2. **name 段(新,kind=.lab)**:对**所有没有 seriesKey** 的 Indicator,按 `normalizedKey(name, unit)` 分桶;每桶 ≥ `minPoints` 才保留。参考范围从该桶**最新一条** Indicator 的 `range` 字符串解析。
3. 两段合并返回,各自按 `latestDate` 倒序。详情/列表按 `kind` 分段。
```swift
// name :trim + + ;unit trimkey = "name|unit"
static func normalizedKey(name: String, unit: String) -> String
// ClosedRange<Double>?
// "3.9-6.1" / "3.9~6.1" / "3.9 - 6.1";("<5.2"/">40"/"120") nil(,)
static func parseRange(_ raw: String) -> ClosedRange<Double>?
```
> **去重**:有 seriesKey 的指标只进 monitor 段;无 seriesKey 的只进 lab 段。即使同名也不混。
> **状态着色**:lab 段每个 Point 的 `status` 直接取 Indicator.status(已由 VL/录入判定),无需重算。
## 4. UI:健康日历移至主页
### 4.1 `CalendarOverviewView`(新文件 `Features/Calendar/CalendarOverviewView.swift`)
把现 `TrendsView` 的日历部分**原样抽出**为独立页:`modeSwitch`(月/年)+ `anchorBar`(◀ 年月 ▶)+ `calendarBody`(`CalendarMonthGrid`/`CalendarYearGrid`)+ `legend` + 月视图下的 `dayDetailInline`
- 自带 `@Query`(indicators/reports/diaries/symptoms/profiles/customMetrics)。
- 接收可选 `initialDate`(从主页某天进入时定位选中)。
- 包在 `NavigationStack`,标题「健康日历」,右上「完成」关闭(用于 fullScreenCover)。
- `CalendarMonthGrid` / `CalendarYearGrid` / `CalendarMarkers` / `DayDetailSheet` **不改**,直接复用。
### 4.2 `HomeCalendarCard`(新文件 `Features/Home/HomeCalendarCard.swift`)
自包含组件(对齐 `TodayRemindersCard` 模式):
- 自带 `@Query`,`CalendarData.build` 计算标记。
- **当前周横条**:周一→周日 7 个紧凑日格(日期数字 + 标记圆点,复用 `DayMarks` 颜色规则:异常红 / 报告灰 / 正常绿 / 日记浅灰;有进行中症状则该格底色淡 amber)。今天高亮。
- 顶部标题「健康日历」+ 右侧「本月 N 天有记录 ›」。
- 整卡可点 → `fullScreenCover(CalendarOverviewView())`;点某一天 → 带 `initialDate` 进入。
- 样式走 `.tjCard()`,放在主页 `greeting` 之后、`TodayRemindersCard` 之前。
### 4.3 `HomeView` 改动
`body` 的 VStack 在 `greeting` 后插入 `HomeCalendarCard()`。其余不动。
## 5. UI:趋势 Tab 重构
### 5.1 `TrendsView`(重写)
移除所有日历相关代码(已迁到主页)。新结构:
- header「趋势」。
- 若无可成趋势的桶 → 空状态(「还没有可成趋势的指标 / 同一指标记录满 2 次后会出现在这里」)。
- 否则两段:
- **长期监测**(`kind == .monitor`):标题 + 计数。
- **化验指标趋势**(`kind == .lab`):标题 + 计数。
- 每段 `ForEach` 渲染 `TrendRow`,点击 push/present `TrendDetailView`
- 导航:`TrendsView``NavigationStack`,行用 `NavigationLink` 进详情(趋势 Tab 当前无 NavigationStack,新增之)。
### 5.2 `TrendRow`(新文件 `Features/Trends/TrendRow.swift`)
紧凑行:
- 左:指标名 + 「N 条 · 近 X 个月」副标题。
- 中:mini sparkline(小号 `Chart`,height≈36,无坐标轴,单/双线,异常点红)。
- 右:最新值 + 单位(异常红)+ chevron。
- `.tjCard(bordered: true)`
### 5.3 `TrendDetailView`(新文件 `Features/Trends/TrendDetailView.swift`)
接收 `bucket: SeriesBucket`,自带 `@Query` 用于数据点→来源跳转。
- **大图表**(height≈220):复用 `SeriesChartCard` 的绘制逻辑(参考范围带 + catmullRom 折线 + 点 + 双线图例),但加坐标轴、按所选时间范围裁剪 domain。
- **时间范围 chips**:全部 / 近1年 / 近6月 / 近3月(仅当跨度 > 该范围才显示对应 chip)。切换裁剪图表点 + 重算 domain + 重算统计。
- **统计摘要卡**:最新值(带状态)/ 对比上次(Δ 绝对值+百分比+升降箭头,跨参考范围边界标红)/ 最低 / 最高 / 平均 / 记录数 / 时间跨度。文案模板拼装,不走 LLM。
- **AI 解读占位**:一行灰字「AI 解读即将上线」(预留,不接 LLM)。
- **数据点列表**(倒序):日期 + 值+单位 + 状态箭头/徽章;`onTapGesture``DayDetailSheet(date:)`(复用现有 sheet,给出当天来源上下文)。
- 标题 = bucket.title。
血压(双线)在详情页:统计摘要按「收缩/舒张」分别给最新值;列表每行显示「收缩/舒张」两值。
## 6. 受影响文件清单
**新增**
- `Features/Calendar/CalendarOverviewView.swift`
- `Features/Home/HomeCalendarCard.swift`
- `Features/Trends/TrendRow.swift`
- `Features/Trends/TrendDetailView.swift`
**修改**
- `Features/Trends/SeriesBucket.swift`(加 kind / sourceIndicatorIDs / name 段 / parseRange
- `Features/Trends/TrendsView.swift`(删日历,重写为趋势列表 + NavigationStack)
- `Features/Home/HomeView.swift`(插入 HomeCalendarCard)
- `康康.xcodeproj/project.pbxproj`(新文件加入 target — 若用 file-system-synchronized group 则免改;需确认)
**不改**:`CalendarMonthGrid/YearGrid/Markers/DayDetailSheet``SeriesChartCard`(详情页复用其绘制思路,可抽 helper 或直接内置)、Models、xcstrings、RootView/TabBar、录入流程。
## 7. 验证
- 构建无错误/无新警告(`DEVELOPER_DIR` 指完整 Xcode,touch 强制重编 — 见记忆 build-from-cli)。
- 主页:日历卡显示当前周标记;点卡进总览;月/年切换;点某天→当日详情正确。
- 趋势:制造同名指标 ≥2 条(如手动录两次「血红蛋白」或两份报告同含一项)→ 出现在「化验指标趋势」段;预设监测仍在「长期监测」段;详情图表/统计/数据点跳转正确;血压双线正常。
- 空状态:全新库时两个页面都给出友好空态。
## 8. 风险与回退
- **range 解析覆盖不全**:单边区间("<5.2")暂不画带,图仍可用 —— 可接受,后续增强。
- **lab 段噪声**:同名但单位不同的指标会分成两桶(key 含 unit)—— 正确行为。若用户名字录入不一致(「血红蛋白」vs「Hb」)会分开 —— demo 可接受,不做模糊归并。
- **pbxproj**:若新文件未自动入 target,构建会报 missing symbol;届时手动加 build file 引用。