Files
iboard/docs/V0.2/需求文档.md
T

18 KiB
Raw Blame History

iBoard V0.2 需求文档 — 打卡 / 日历 / 清单

版本:V0.2 · 分支:dev-0.2 · 状态:待评审 · 日期:2026-10-22 目标项目:iboard(Tauri 2 + React 18 + antd-mobile + Express + Prisma/MariaDB,移动端优先) 基线版本:V0.1(dev-0.1)


1. 总体描述

在 iBoard 移动端新增一个「打卡」模块,用一个页面、三个视图解决两类日常需求:

  1. 周期性打卡(习惯养成):用户定义"每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次"的目标, 每次打卡后卡片上的环形进度按 已完成次数 / 目标次数 增长,达标时环形闭合并播放完成特效;
  2. 日历回看:以日历为载体,快速查看今天(要做什么)、昨天(做得怎样)、 明天(计划做什么),日期上以圆点/实心标记完成情况;
  3. 检查清单(场景化一次性事项):面向出差、回老家等临时场景,逐条勾选确认, 避免遗漏;清单可复用为模板,下次一键生成。

一句话:用环形进度做周期性打卡,用日历做时间维度的回看与计划,用清单覆盖临时性的检查场景。

1.1 本期范围

  • 单用户使用,数据按登录用户隔离(复用现有 JWT 鉴权)。
  • 移动端优先(MobileShell + 底部 TabBar 体系),桌面浏览器兼容可用即可。
  • 数据落库 MariaDB,通过现有 Express /api/* 提供接口。

1.2 不在本期范围(明确排除)

  • 多人协作 / 共享打卡 / 打卡排行榜。
  • 系统通知、推送提醒、定时提醒(V0.2 不引入通知能力)。
  • 复杂统计报表(周报/月报/趋势图),本期只提供完成率概览。
  • 桌面端专用布局、横屏适配。
  • 清单的周期性重复(如"每次出差都生成"),本期只做临时清单 + 模板手动复用。

2. 术语与概念定义

术语 定义
打卡项(Habit) 一个被跟踪的周期性目标,如"喝水 每天 8 次""健身 每周 3 次"
周期类型 day / week / month / year,决定进度统计窗口
目标次数 n 一个周期内需要完成的打卡次数,n ≥ 1
周期窗口 日=自然日;周=自然周(周一 00:00 至周日 23:59);月=自然月;年=自然年。时区统一 Asia/Shanghai
打卡记录(CheckIn) 一次打卡动作,归属到某个本地日历日(可补打到过去日期)
环形进度 卡片上的圆环组件,进度 = min(已完成次数 / n, 1);达标=闭合
清单(CheckList) 一次性检查清单,如"出差 · 10月杭州",含若干检查项
检查项(CheckItem) 清单中的单条待检查事项,勾选即完成
模板(Template) 可被复用的清单结构(一组检查项标题),用于一键生成新清单
日期状态 某日的打卡完成情况:全部完成 / 部分完成 / 未开始 / 超额完成

3. 需求点

编号规则:类型-模块-序号。类型:F=功能需求、U=交互与体验、D=数据、N=非功能、E=异常与边界。 模块:CK=打卡、CL=日历、LS=清单、SY=通用。

优先级:P0 = 本期必须有;P1 = 本期应该有;P2 = 有则更好(可延后)。

3.1 功能需求 — 打卡(F-CK)

编号 需求描述 验收要点 优先级
F-CK-01 支持新建打卡项:名称、周期类型、目标次数 n、颜色、图标、备注 表单校验:名称必填且 ≤ 20 字;n 为 ≥1 的整数 P0
F-CK-02 周期类型支持每天 n 次 / 每周 n 次 / 每月 n 次 / 每年 n 次四种 四种类型均可创建并正确统计 P0
F-CK-03 卡片以环形进度展示当前周期完成情况,环中心显示 已完成/n 进度 = 已完成次数 / n,上限 100% P0
F-CK-04 每天 1 次的打卡项:打卡后环形直接闭合(100%),卡片进入"已完成"态 一次点击即闭合,不出现 50% 之类的中间态 P0
F-CK-05 每周 n 次(及月/年):每打卡一次环形闭合对应百分比(如 n=3,打 1 次=33.3%、2 次=66.7%、3 次=100% 闭合) 进度随每次打卡实时刷新,带过渡动画 P0
F-CK-06 打卡主操作:点击卡片上的打卡按钮即完成一次打卡 单击即生效,响应 < 300ms(乐观更新) P0
F-CK-07 打卡完成特效:达标(进度达 100%)时播放特效——环形闭合动画 + 光晕/粒子扩散 + 轻量触感反馈 特效时长 ≤ 800ms,不阻塞后续操作 P0
F-CK-08 打卡记录需记录发生日期(本地日历日)与时间戳 支持按日回看,跨零点归属正确 P0
F-CK-09 支持撤销:可撤销本周期内最近一次打卡(主要是误触场景) 撤销后进度回退,记录物理删除或标记撤销 P1
F-CK-10 支持补打卡:对过去日期补记一次打卡,默认允许回溯 7 天 补打后对应日期的状态与进度同步更新 P1
F-CK-11 不允许提前打卡(未来日期不可打卡) 明天的项只展示为"计划",按钮置灰 P0
F-CK-12 展示连续完成天数/周期数(streak) 如"已连续 5 天",断签归零 P1
F-CK-13 打卡项可编辑(改名/改 n/改颜色/改周期)与停用(归档) 归档后不出现在今日待办,历史保留 P1
F-CK-14 修改目标次数 n 后,当前周期进度按新 n 重算展示,不改写历史记录 例如 n 由 3 改 5,已完成 3 次 → 显示 3/5(60%) P1
F-CK-15 超额打卡:周期内超过 n 仍可继续打卡,环形保持闭合,中心显示 n+k(如 3+1) 不清零、不报错 P2
F-CK-16 打卡项支持排序(拖动或置顶)与分组展示 今日列表按"未完成在前、已完成在后"默认排序 P2
F-CK-17 打卡时可附带备注(可选,纯文本) 列表详情中可查看 P2

3.2 功能需求 — 日历(F-CL)

编号 需求描述 验收要点 优先级
F-CL-01 提供月历视图:当月日历网格,可切换上/下月,点击日期查看当日详情 周一起始,周一~周日七列 P0
F-CL-02 今天视图(默认):列出今天应打卡的项及完成状态,可直接打卡 打开页面默认定位今天 P0
F-CL-03 昨天视图:展示昨天各打卡项的已完成结果(完成/未完成/超额),未完成项可进行补打 以回看为主,标记为"已完成的历史" P0
F-CL-04 明天视图:展示明天计划要打卡的项(按周期规则推算),只读,不可打卡 明确标注"计划",打卡按钮不可用 P0
F-CL-05 今天/昨天/明天快捷切换(顶部 Segmented 或左右滑动) 一键切换,切换有过渡动画 P0
F-CL-06 日期标记:月历中每个日期按完成情况标记——全部完成(实心)、部分完成(半填充)、未开始(空心/无标记)、超额完成(特殊标记) 一眼能看出当月坚持情况 P0
F-CL-07 日期详情:点击某日期,展示当天每个打卡项的"目标次数 / 已完成次数 / 状态" 过去日期可补打,未来日期只读 P0
F-CL-08 日历与打卡数据同源:打卡操作在日历视图与打卡列表视图中即时一致 任一处打卡,另一处刷新 P0
F-CL-09 月历顶部展示当月完成率(完成次数 / 应完成次数) 百分比展示 P1
F-CL-10 支持左右滑动手势切换月份 与按钮切换等价 P2
F-CL-11 月视图支持简单的完成热力(按完成率深浅着色) 可选展示,默认关闭 P2

3.3 功能需求 — 清单(F-LS)

编号 需求描述 验收要点 优先级
F-LS-01 支持新建检查清单:名称、场景标签(出差 / 回老家 / 自定义)、备注 名称必填,场景标签可空 P0
F-LS-02 清单内可增删改检查项,支持排序 单条 ≤ 40 字 P0
F-LS-03 勾选完成:点击检查项前的复选框即打勾,已完成项置灰 + 删除线;可取消勾选 单击切换,状态即时保存 P0
F-LS-04 清单头部显示进度:已完成 / 总数 + 进度条;全部完成时显示完成态 全部勾完触发完成提示 P0
F-LS-05 清单全部完成时播放完成特效(与 F-CK-07 同一套特效组件) 复用,风格统一 P1
F-LS-06 模板:系统预置常用模板(出差、回老家),一键基于模板生成新清单 至少 2 个预置模板 P1
F-LS-07 另存为模板:用户可将自己的清单保存为自定义模板,后续复用 自定义模板可删除 P1
F-LS-08 复用历史清单:从历史清单复制生成一份新清单(勾选状态重置) 原清单保持不变 P1
F-LS-09 清单归档/删除:完成的清单可归档(不出现在进行中列表),可查看历史 删除需二次确认 P1
F-LS-10 清单项支持备注/数量说明(如"充电器 ×1") 展示在标题下方,弱色小字 P2
F-LS-11 清单支持一键全部勾选 / 全部取消 批量操作入口 P2

3.4 功能需求 — 通用(F-SY)

编号 需求描述 验收要点 优先级
F-SY-01 底部 TabBar 新增**「打卡」页**(现有 4 个 Tab → 5 个),作为本模块唯一入口 图标风格与现有 TabIcon 一致 P0
F-SY-02 「打卡」页内以子 Tab(Segmented / SwipeTabs)切换 打卡 / 日历 / 清单 三个视图 复用现有 SwipeTabs 组件 P0
F-SY-03 所有接口复用现有鉴权(authRequired,JWT),数据按 userId 隔离 未登录跳转 /login P0
F-SY-04 后端新增 REST 路由(建议 /api/checkin/*、/api/checklist/*),挂载到 server/index.js 与现有路由风格一致 P0
F-SY-05 时间处理统一 Asia/Shanghai,前端与后端对"日历日"的判定一致 跨零点打卡归属当天 P0
F-SY-06 打卡/勾选操作采用乐观更新,接口失败时回滚并 Toast 提示 失败可见、状态不失真 P1
F-SY-07 写接口幂等/防抖:同一打卡项 1 秒内重复点击只记一次 前端防抖 + 后端唯一约束双保险 P1
F-SY-08 页面可见性变化时刷新数据(复用 useRefreshOnVisible) 从后台切回自动同步 P1

3.5 交互与体验需求(U)

编号 需求描述 验收要点 优先级
U-01 环形进度组件:SVG 圆环,背景环 + 进度环,进度变化带 400ms ease-out 动画 组件可复用(打卡卡片、清单进度共用) P0
U-02 完成特效:达标瞬间环形闭合 + 光晕/粒子扩散,约 600ms;可重复观看不烦扰 不阻塞操作,动画结束自动清理 P0
U-03 触感反馈:完成打卡时轻量震动(Web Vibration API / Tauri),可在设置中关闭 默认开启,关闭后仅视觉反馈 P1
U-04 移动端适配:375~430px 视口,复用 globals.css 的 .card 体系,安全区与底部 TabBar 不遮挡内容 真机无横向滚动、无遮挡 P0
U-05 空态 / 加载态 / 错误态三态齐全(首次使用引导"创建第一个打卡项") 空态有明确 CTA P0
U-06 打卡按钮足够大(点击热区 ≥ 44×44),符合移动端触控规范 误触率低 P1
U-07 动效性能降级:低端设备或 prefers-reduced-motion 时关闭粒子特效,仅保留进度动画 不卡顿 P1
U-08 深浅色:跟随现有浅色主题,卡片/分割线风格与首页、监控页一致 视觉统一 P1
U-09 清单勾选有明确的勾选动画(勾形描边动画) 反馈即时 P2

3.6 数据需求(D)

编号 需求描述 验收要点 优先级
D-01 新增 Prisma 模型:Habit(打卡项)、CheckIn(打卡记录) 字段见附录 A P0
D-02 新增 Prisma 模型:CheckList(清单)、CheckListItem(检查项)、CheckListTemplate(模板,P1) 字段见附录 A P0
D-03 Habit / CheckList 均带 userId 外键,删除用户级联删除 数据隔离 P0
D-04 CheckIn 建立 (habitId, date) 索引,支持按日期范围查询 日历月查询走索引 P0
D-05 提供迁移 SQL 并纳入 prisma migrate deploy 流程 与 V0.1 迁移流程一致 P0
D-06 软删除语义:Habit.archived、CheckList.archived,历史记录保留 不物理删除历史 P1

3.7 非功能需求(N)

编号 需求描述 验收要点 优先级
N-01 页面首屏数据加载 < 1.5s(内网/正常网络),交互响应 < 300ms(乐观更新) 真机可感知流畅 P0
N-02 兼容 Android WebView(Tauri 打包)与移动端浏览器,不依赖桌面专有 API 真机验证 P0
N-03 单用户数据量按"千级记录/年"设计,无需分表;月查询一次请求返回 日历切月无等待感 P1
N-04 所有接口 authRequired,越权访问返回 401 安全基线 P0
N-05 前端代码组织遵循现有约定:src/pages/checkin/* + src/components/* + server/routes/* 结构一致 P0

3.8 异常与边界需求(E)

编号 需求描述 处理约定 优先级
E-01 重复点击打卡 前端防抖 + 后端唯一约束,只记一次 P0
E-02 接口失败 / 断网 乐观更新回滚 + Toast 提示,可重试 P0
E-03 周期中途新建打卡项(如周三创建"每周 3 次") 本期按完整目标 n 计算,不按剩余天数折算 P1
E-04 每月 n 次在天数不同的月份 按自然月窗口,不折算 P0
E-05 每年 n 次跨年 按自然年窗口,年末清零 P0
E-06 删除打卡项 归档(软删除)为主;硬删除需二次确认并提示"历史记录将一并删除" P1
E-07 清单项全部删除后清单进度为 0/0 显示空清单,不显示 100% P1
E-08 跨零点打卡(23:59 → 00:00) 以提交时刻的本地日历日归属 P0

4. 优先级汇总(本期交付范围)

  • P0(必须):F-CK-0108、F-CK-11;F-CL-0108;F-LS-0104;F-SY-0105;U-01、U-02、U-04、U-05;D-01~05;N-01、N-02、N-04、N-05;E-01、E-02、E-04、E-05、E-08。
  • P1(应该):F-CK-09、10、1214;F-CL-09;F-LS-0509;F-SY-0608;U-03、U-0608;D-06;N-03;E-03、E-06、E-07。
  • P2(可选/延后):F-CK-15~17;F-CL-10、11;F-LS-10、11;U-09。

5. 里程碑建议

里程碑 内容
M1 数据与接口 Prisma 模型 + 迁移;server/routes/checkin.js、checklist.js;周期进度计算与日期查询
M2 打卡视图 环形进度组件 + 打卡卡片列表 + 打卡/撤销/补打 + 完成特效
M3 日历视图 月历网格 + 今天/昨天/明天三视图 + 日期标记 + 日期详情
M4 清单视图 清单 CRUD + 勾选交互 + 进度条 + 模板与复用
M5 联调打磨 真机验证、动效降级、空态/错误态、性能与跨天边界

6. 待确认问题(评审时决策)

# 问题 建议
1 是否新增第 5 个底部 Tab「打卡」,还是放在首页入口? 建议新增 Tab,入口最清晰
2 周起始日是周一还是周日? 建议周一(国内习惯)
3 是否允许"明天提前打卡"? 建议不允许(F-CK-11),保持统计口径干净
4 删除打卡项时历史是否保留? 建议默认归档保留,硬删除才清历史
5 预置模板具体包含哪些项? 评审时列出"出差/回老家"各 8~12 条初始项
6 完成特效是否可全局关闭? 建议在"我"页提供开关(默认开)

附录 A:核心实体草案(供设计阶段参考,非最终表结构)

// 打卡项
model Habit {
  id        Int      @id @default(autoincrement())
  userId    Int
  name      String
  period    String   // day | week | month | year
  target    Int      @default(1)      // 周期内目标次数 n
  color     String   @default("#1677ff")
  icon      String   @default("")
  note      String   @default("")
  archived  Boolean  @default(false)
  sortOrder Int      @default(0)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  records   CheckIn[]

  @@index([userId, archived])
}

// 打卡记录(一次打卡 = 一条;同日多次为多条)
model CheckIn {
  id        Int      @id @default(autoincrement())
  habitId   Int
  habit     Habit    @relation(fields: [habitId], references: [id], onDelete: Cascade)
  userId    Int
  date      String                    // 本地日历日 "YYYY-MM-DD" (Asia/Shanghai)
  seq       Int      @default(1)      // 当日第几次 (1..n, 超额继续累加)
  note      String   @default("")
  source    String   @default("user") // user | backfill(补打)
  createdAt DateTime @default(now())

  @@unique([habitId, date, seq])
  @@index([userId, date])
  @@index([habitId, date])
}

// 检查清单
model CheckList {
  id        Int             @id @default(autoincrement())
  userId    Int
  title     String
  scene     String   @default("")     // 出差 | 回老家 | 自定义
  note      String   @default("")
  archived  Boolean  @default(false)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  items     CheckListItem[]

  @@index([userId, archived])
}

// 检查项
model CheckListItem {
  id        Int       @id @default(autoincrement())
  listId    Int
  list      CheckList @relation(fields: [listId], references: [id], onDelete: Cascade)
  title     String
  note      String    @default("")
  done      Boolean   @default(false)
  doneAt    DateTime?
  sortOrder Int       @default(0)
  createdAt DateTime  @default(now())
  updatedAt DateTime @updatedAt

  @@index([listId, sortOrder])
}

// 清单模板(P1)
model CheckListTemplate {
  id        Int      @id @default(autoincrement())
  userId    Int?                      // null = 系统预置
  name      String
  scene     String   @default("")
  items     Json                      // [{ title, note }]
  builtin   Boolean  @default(false)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

说明:周期进度由 CheckIn 按周期窗口现场聚合(不冗余计数字段),避免并发与修改 n 后的一致性问题。