[阶段8] 新增每日数据模块;趋势改为全指标并列 + 周期聚合

每日数据(新页面 /daily):
- 按活动/能量/心率/压力/睡眠/血氧呼吸/训练七组,列出全部 40 项指标
- 日期选择器 + 前后一天翻页;"只显示有数据的指标"开关
- 附当天的运动记录明细
- 标题处显示当天记录到多少项,缺数据一目了然

趋势(重写):
- 15 组指标全部并列展示,不再一次只能看一个
- 标签可逐个隐藏/显示,选择存入 localStorage(每次刷新都重置的
  选择算不上偏好);提供全选/全不选
- 两级筛选:范围(一月/一季/半年/一年/两年)× 周期(每天/每 7 天/
  每月/每季度)。周期选项按范围过滤,避免出现"近一月按季度聚合"
- 所有图表共用同一份聚合结果,一行筛选器统摄全部图表

聚合口径(lib/aggregate.ts):
- 无论累计型还是速率型指标,一律折算为"周期内日均",这样 30 天的
  月份和 31 天的月份不会仅因日历差 3%
- 周按最新一天往回切,而不是按自然周一 —— 否则开头会出现一个半空
  的桶,看起来像低谷,其实只是窗口起点
- 累计型指标的统计标签写作"日均"而非"平均",读者不必猜口径

fix(viz): 聚合后柱形/面积图掩盖了变化
- 柱形与面积都以延展量编码大小,必须从 0 起;而一年的月均步数都在
  9,590~12,701 之间,画出来几乎一样高,恰恰看不见要看的变化
- 聚合视图改用折线:折线编码位置而非延展量,非零轴是正当的。
  改后 y 轴自动落在 9350~12750,走势清晰可读

fix(health): 运动记录按日期筛选时 500
- get_activities 复用了按 date 列过滤的子句,但 activities 表只有
  start_time,报 "Unknown column 'date'"。此前唯一的调用方不传
  日期,所以一直没暴露,每日数据页一传就炸
- 上界改用次日零点的开区间:SQLite 按字符串比较,而存储的分隔符
  可能是 'T'(0x54) 也可能是空格(0x20),写成 "end 23:59:59" 会让
  当天 18:30 的记录排在上界之后而被排除在自己那天之外

tests (+6, 共 305): 运动记录的单日范围、上下界闭合、仅起点/仅终点、
不传范围返回全部、端点级验证

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
ericwyuan
2026-08-23 22:33:59 +08:00
parent 9e5e77755e
commit bfda1cd017
9 changed files with 939 additions and 115 deletions

View File

@@ -9,6 +9,7 @@ import Recommendations from './pages/Recommendations';
import Settings from './pages/Settings';
import Sleep from './pages/Sleep';
import Achievements from './pages/Achievements';
import Daily from './pages/Daily';
import { FEATURES } from './features';
function App() {
@@ -25,6 +26,7 @@ function App() {
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/sync" element={<DataSync />} />
<Route path="/daily" element={<Daily />} />
<Route path="/trends" element={<Trends />} />
<Route path="/sleep" element={<Sleep />} />
<Route path="/achievements" element={<Achievements />} />