# 开发计划与任务拆分 ## 1. 开发阶段概览 ``` Phase 1: 基础框架搭建 (2天) ↓ Phase 2: 首页核心功能 (3天) ★ 优先保证首页体验 ↓ Phase 3: 记录与历史 (2天) ↓ Phase 4: 统计与图表 (2天) ↓ Phase 5: AI助手与个人中心 (3天) ↓ Phase 6: 优化与测试 (2天) ``` --- ## 2. Phase 1: 基础框架搭建 (2天) ### 2.1 项目初始化 - [ ] 创建 uni-app 项目 (Vue3 + JavaScript) - [ ] 配置路径别名 (@/) - [ ] 创建目录结构 ### 2.2 基础配置 - [ ] 配置 pages.json (页面路由 + TabBar) - [ ] 配置全局样式 (暗色主题变量) - [ ] 配置环境变量 (开发/生产 API 地址) ### 2.3 核心模块 - [ ] 封装 request.js (请求拦截、Token管理、错误处理) - [ ] 封装 storage.js (本地存储工具) - [ ] 配置 Pinia stores 结构 - [ ] 实现登录流程 (wx.login + 后端认证) ### 2.4 公共组件 - [ ] 创建 Loading 组件 - [ ] 创建 Skeleton 骨架屏组件 - [ ] 创建 Button 组件 (主按钮/次按钮样式) - [ ] 创建 Card 组件 (卡片容器) **交付物**:可运行的空白项目,登录功能正常 --- ## 3. Phase 2: 首页核心功能 (3天) ★ > ⚠️ 首页是用户最常访问的页面,必须保证 < 500ms 首屏渲染 ### 3.1 Day 1: 页面结构 + 数据层 #### API 封装 - [ ] `api/smoke.js`: getDashboard() - [ ] `api/smoke.js`: getNextSmokeTime() - [ ] `api/profile.js`: getProfile() #### Store 设计 - [ ] `stores/dashboard.js`: 看板数据 + 缓存逻辑 - [ ] `stores/user.js`: 用户信息 + 登录状态 #### 页面结构 - [ ] 首页骨架屏 - [ ] 页面布局 (header + timer + cards + buttons) ### 3.2 Day 2: 核心组件 #### 计时器组件 (TimerRing) - [ ] Canvas 绘制进度环 - [ ] 时间格式化 (HH:MM:SS) - [ ] requestAnimationFrame 优化 - [ ] 页面可见性处理 (切后台暂停) #### 统计卡片 - [ ] 今日已抽卡片 (X/目标, 较昨日±N) - [ ] 烟瘾发作已抵抗卡片 #### 快捷按钮 - [ ] 记录抽烟按钮 → 弹出记录表单 - [ ] 想抽忍住了按钮 → 快速提交 ### 3.3 Day 3: 交互 + 优化 #### AI 提示卡片 - [ ] 延迟加载 (300ms后) - [ ] 可关闭 (本地存储关闭状态) - [ ] 下次建议时间显示 #### 性能优化 - [ ] 并行请求优化 - [ ] 骨架屏过渡动画 - [ ] 首屏性能埋点 #### 新用户引导 - [ ] 检测 profile.exists - [ ] 跳转引导页逻辑 **交付物**:完整可用的首页,首屏 < 500ms --- ## 4. Phase 3: 记录与历史 (2天) ### 4.1 Day 1: 记录表单组件 #### 4.1.1 smoke-record-dialog 组件开发 **组件结构** (`components/smoke-record-dialog/smoke-record-dialog.vue`) - [x] 底部弹出动画 (从底部滑入,半屏展示) - [x] 明亮主题 UI (白色背景 + 翡翠绿主题色 #10B981) - [x] 遮罩层点击关闭 - [x] 表单项布局 **表单字段实现** - [x] 时间选择器 - 日期选择 (picker mode="date") - 时间选择 (picker mode="time") - 默认当前时间 - 自动拼接为 `smoke_at` 字段 - [x] 数量选择器 (仅抽烟模式显示) - 加减按钮 (+/-) - 数字输入框 - 最小值 1 - 默认值 1 - [x] 烟瘾等级选择 (仅抽烟模式显示) - 1-5 级按钮组 - 选中态样式切换 - 默认值 2 - [x] 备注输入框 - 多行文本域 (textarea) - 最大长度 200 字符 - 根据模式显示不同占位符 - 抽烟: "记录抽烟原因..." - 忍住: "记录抵抗心得..." **组件逻辑** - [x] Props 定义 ```javascript { show: Boolean, // 控制显示 type: String // 'smoke' | 'resisted' } ``` - [x] 数据初始化 - 打开弹框时自动初始化当前时间 - 根据 type 设置默认值 - smoke: num=1, level=2 - resisted: num=0, level=2 - [x] 提交逻辑 ```javascript { smoke_time: "2025-01-25", smoke_at: "2025-01-25 14:30:00", remark: "", level: 2, num: 1 // 或 0 (忍住时) } ``` - [x] 关闭动画 - 延迟 300ms 触发 update:show - 平滑过渡效果 #### 4.1.2 组件配置 **easycom 自动导入** (`pages.json`) - [x] 配置组件路径 ```json { "easycom": { "autoscan": true, "custom": { "^smoke-record-dialog$": "@/components/smoke-record-dialog/smoke-record-dialog.vue" } } } ``` **组件文档** - [x] 创建 `components/smoke-record-dialog/README.md` - [x] 包含使用示例、Props 说明、Events 说明 - [x] 创建 `components/README.md` 全局组件使用指南 #### 4.1.3 API 集成 **API 封装** (`api/smoke.js`) - [x] `createLog(data)` - POST /api/v1/smoke/logs - 支持抽烟记录 (num > 0) - 支持忍住记录 (num = 0, level = 0) - [ ] `updateLog(id, data)` - PUT /api/v1/smoke/logs/:id - [ ] `deleteLog(id)` - DELETE /api/v1/smoke/logs/:id - [x] `getLatestLogs(limit)` - GET /api/v1/smoke/logs/latest **首页集成** (`pages/index/index.vue`) - [x] 引入组件 (easycom 自动导入) - [x] 双向绑定 show 状态 - [x] 区分两种模式 ```vue ``` - [x] 提交处理 - 调用 API 创建记录 - 更新 dashboard store 数据 - 抽烟: incrementTodayCount() - 抽烟: resetTimer() - 显示成功提示 - 关闭弹框 **性能优化** - [x] 组件懒加载 (easycom 按需加载) - [x] 表单数据仅在打开时初始化 - [x] 提交后自动关闭 --- ### 4.2 Day 2: 历史记录页 #### 4.2.1 页面结构 (`pages/logs/index.vue`) **顶部导航** - [ ] 导航栏标题 "历史记录" - [ ] 筛选 Tabs 组件 ```javascript tabs: [ { id: 'all', name: '全部' }, { id: 'smoke', name: '已抽烟', filter: (item) => item.num > 0 }, { id: 'resisted', name: '已忍住', filter: (item) => item.num === 0 } ] ``` **列表容器** - [ ] scroll-view 组件 - [ ] 下拉刷新 (refresher-enabled) - [ ] 上拉加载更多 - [ ] 空状态提示 #### 4.2.2 数据层 **Store 设计** (`stores/logs.js`) ```javascript { state: { logs: [], // 记录列表 total: 0, // 总条数 page: 1, // 当前页 pageSize: 20, // 每页数量 hasMore: true, // 是否有更多 loading: false // 加载状态 }, getters: { groupedByDate: (state) => { // 按日期分组 // { '2025-01-25': [...], '2025-01-24': [...] } }, smokeCount: (state) => { // 抽烟记录数量 }, resistedCount: (state) => { // 忍住记录数量 } }, actions: { async fetchLogs(refresh = false), async loadMore(), async deleteLog(id), clearLogs() } } ``` **API 调用** - [ ] `getLogs({ page, page_size, start, end })` - [ ] `getLatestLogs(limit)` - 首次快速加载 - [ ] `deleteLog(id)` #### 4.2.3 记录卡片组件 (`components/log-item/log-item.vue`) **卡片布局** ``` ┌─────────────────────────────────────┐ │ 🚬 14:30 3 支 │ │ 压力大、工作繁忙 │ │ 距上次 2小时15分 │ └─────────────────────────────────────┘ ``` **字段显示** - [ ] 类型图标 - 🚬 抽烟 (num > 0) - 💪 忍住 (num = 0) - [ ] 时间显示 - 格式: HH:mm - 今天/昨天/具体日期 - [ ] 数量显示 (抽烟时) - `${num} 支` - 烟瘾等级 (1-5 级) - [ ] 备注内容 - 单行显示,超出省略 - 最多显示 50 字符 - [ ] 间隔时间 - 距上一条记录的时间差 - 格式: "X小时Y分" / "X分钟" **卡片样式** - [ ] 白色背景 - [ ] 圆角 16rpx - [ ] 阴影效果 - [ ] 抽烟/忍住不同边框色 - 抽烟: 红色左边框 - 忍住: 绿色左边框 #### 4.2.4 左滑操作 **左滑按钮** - [ ] 使用 uni-ui 的 uni-swipe-action - [ ] 编辑按钮 (蓝色) - 打开编辑弹框 - 预填充当前数据 - [ ] 删除按钮 (红色) - 显示确认对话框 - 调用删除 API - 更新列表数据 **编辑功能** - [ ] 复用 smoke-record-dialog 组件 - [ ] 添加 recordId 和 mode props ```vue ``` - [ ] 预填充数据 - [ ] 调用 updateLog API - [ ] 刷新列表 **删除功能** - [ ] uni.showModal 确认对话框 - [ ] 调用 deleteLog API - [ ] 乐观更新 (先删除本地数据) - [ ] 失败时回滚 #### 4.2.5 日期分组 **分组逻辑** ```javascript function groupByDate(logs) { const groups = {} logs.forEach(log => { const date = formatDate(log.smoke_time) if (!groups[date]) { groups[date] = { date, displayDate: getDisplayDate(date), // 今天/昨天/MM-DD logs: [] } } groups[date].logs.push(log) }) return Object.values(groups) } ``` **渲染结构** ```vue {{ group.displayDate }} ``` #### 4.2.6 加载状态 **下拉刷新** - [ ] refresher-triggered 状态控制 - [ ] 重置 page = 1 - [ ] 清空现有数据 - [ ] 调用 fetchLogs(true) - [ ] 完成后关闭刷新状态 **上拉加载** - [ ] onReachBottom 触发 - [ ] 检查 hasMore 状态 - [ ] page++ - [ ] 调用 loadMore() - [ ] 追加数据到列表 **加载骨架屏** - [ ] 初次加载显示 - [ ] 3-5 个卡片骨架 - [ ] shimmer 动画效果 **空状态** - [ ] 无数据时显示 - [ ] 空状态图标 + 文案 - [ ] 引导按钮 "去记录" #### 4.2.7 浮动操作按钮 **新增按钮** - [ ] 固定在右下角 - [ ] 圆形按钮 (96rpx) - [ ] ➕ 图标 - [ ] 点击打开记录弹框 - [ ] 阴影 + 缩放动画 **样式** ```css .fab { position: fixed; right: 32rpx; bottom: 120rpx; /* 避开 tabbar */ width: 96rpx; height: 96rpx; background: #10B981; border-radius: 50%; box-shadow: 0 8rpx 24rpx rgba(16, 185, 129, 0.4); } ``` #### 4.2.8 性能优化 **列表优化** - [ ] 虚拟列表 (数据量 > 100 时) - [ ] 分页加载 (每页 20 条) - [ ] 图片懒加载 (如有头像等) **缓存策略** - [ ] 首次加载使用 getLatestLogs (快速) - [ ] 后续使用分页接口 - [ ] 缓存最近 3 页数据 - [ ] 超过 5 分钟刷新缓存 **用户体验** - [ ] 乐观更新 (删除/编辑) - [ ] Loading 状态提示 - [ ] 错误处理 + 重试按钮 - [ ] 防抖处理 (下拉刷新/上拉加载) --- ### 4.3 验收标准 **记录功能** - [x] 弹框动画流畅 (< 300ms) - [x] 表单提交成功率 > 99% - [x] 支持快速记录 (< 3 步) - [ ] 编辑/删除操作正常 **历史记录页** - [ ] 列表加载 < 1s - [ ] 滚动流畅 (60fps) - [ ] 分组显示正确 - [ ] 筛选功能正常 - [ ] 下拉刷新/上拉加载正常 **数据同步** - [ ] 新增记录后首页数据实时更新 - [ ] 删除记录后列表实时更新 - [ ] 编辑记录后详情实时更新 **异常处理** - [ ] 网络异常提示 - [ ] 删除失败回滚 - [ ] 空状态正确显示 **交付物**: - ✅ smoke-record-dialog 组件 (已完成) - ✅ 组件文档和使用指南 (已完成) - ✅ 首页记录功能集成 (已完成) - [ ] 历史记录页完整功能 - [ ] log-item 组件 - [ ] logs store 状态管理 - [ ] 编辑/删除功能 --- ### 4.4 Phase 3 技术要点与最佳实践 #### 4.4.1 组件设计原则 **组件命名规范** - ✅ 使用 kebab-case: `smoke-record-dialog` - ✅ 目录结构: `components/smoke-record-dialog/smoke-record-dialog.vue` - ❌ 避免 PascalCase: `SmokeRecordDialog.vue` (微信小程序依赖分析问题) **组件 API 风格** - ✅ 使用 Options API (更好的兼容性) - ✅ 支持 v-model:show 双向绑定 - ✅ 使用 @submit 事件传递数据 - ✅ Props 类型校验完整 **组件复用性** - 通过 props.type 区分模式 ('smoke' / 'resisted') - 表单字段根据模式动态显示/隐藏 - 提交数据格式统一,由组件内部处理差异 #### 4.4.2 状态管理策略 **Store 职责划分** ```javascript // dashboard.js - 首页数据 { todayCount, // 今日抽烟数 minutesSinceLast, // 距上次时间 nextSmokeTime, // 下次建议时间 actions: { incrementTodayCount(), resetTimer() } } // logs.js - 历史记录 { logs, // 记录列表 page, total, hasMore, // 分页信息 actions: { fetchLogs(), loadMore(), deleteLog() } } // user.js - 用户信息 { user, // 用户基本信息 token, // 认证令牌 isLogin // 登录状态 } ``` **数据流向** ``` 用户操作 → 组件 emit → 页面处理 → 调用 API ↓ 更新 Store ← API 响应 ↓ 触发视图更新 ``` #### 4.4.3 API 错误处理 **统一错误处理** (`api/request.js`) ```javascript // 已实现 - Token 失效自动刷新 - 网络错误重试 (最多 3 次) - 错误码统一处理 - Toast 提示 // 需要注意 - 记录提交失败要保留用户输入 - 删除失败要回滚本地状态 - 编辑冲突要提示用户 ``` **乐观更新 vs 悲观更新** - 删除操作: 乐观更新 (先删除,失败回滚) - 新增操作: 悲观更新 (成功后添加) - 编辑操作: 悲观更新 (成功后更新) #### 4.4.4 性能优化清单 **组件层面** - [x] easycom 按需加载 - [x] 弹框懒加载 (打开时才初始化数据) - [ ] 虚拟列表 (历史记录 > 100 条) - [ ] 图片懒加载 **请求层面** - [ ] 并行请求 (多个接口同时调用) - [ ] 请求缓存 (5 分钟内复用) - [ ] 防抖处理 (快速点击) - [ ] 请求取消 (页面离开时) **渲染层面** - [ ] 分页加载 (每页 20 条) - [ ] 骨架屏过渡 - [ ] 平滑滚动 - [ ] 动画性能优化 (CSS transform) #### 4.4.5 用户体验优化 **反馈机制** - 提交成功: Toast 提示 + 自动关闭弹框 - 提交失败: Toast 提示 + 保留输入内容 - 删除确认: Modal 二次确认 - 加载状态: Loading + 禁用按钮 **快捷操作** - 首页快速记录按钮 (2 个按钮对应 2 种模式) - 历史记录页浮动新增按钮 - 左滑快速删除/编辑 - 下拉刷新快速更新 **引导提示** - 首次使用引导 - 空状态引导 - 错误状态引导 - 功能提示 #### 4.4.6 测试要点 **单元测试** - [ ] 组件 props 验证 - [ ] 表单数据初始化 - [ ] 日期分组逻辑 - [ ] 时间格式化函数 **集成测试** - [ ] 记录提交流程 - [ ] 编辑/删除流程 - [ ] 分页加载流程 - [ ] 筛选切换流程 **E2E 测试场景** ``` 场景 1: 快速记录抽烟 1. 点击"记录抽烟"按钮 2. 不修改默认值 3. 直接提交 4. 验证首页数据更新 场景 2: 详细记录 1. 点击"记录抽烟"按钮 2. 修改时间、数量、等级 3. 输入备注 4. 提交 5. 在历史记录页验证 场景 3: 编辑记录 1. 进入历史记录页 2. 左滑某条记录 3. 点击编辑 4. 修改内容 5. 提交 6. 验证更新成功 场景 4: 删除记录 1. 进入历史记录页 2. 左滑某条记录 3. 点击删除 4. 确认删除 5. 验证列表更新 ``` #### 4.4.7 常见问题与解决方案 **问题 1: 微信小程序组件依赖分析错误** ``` 错误: components/SmokeRecordDialog.js 已被代码依赖分析忽略 解决: 1. 使用 kebab-case 命名 2. 组件放在同名文件夹内 3. 配置 easycom 自动导入 4. 使用 Options API ``` **问题 2: 弹框关闭时数据未重置** ``` 原因: 组件销毁前未清理数据 解决: watch show 变化,false 时清理数据 ``` **问题 3: 列表滚动性能差** ``` 原因: 数据量大,DOM 节点过多 解决: 1. 虚拟列表 2. 分页加载 3. 使用 CSS transform 优化动画 ``` **问题 4: 删除后列表显示错误** ``` 原因: 本地状态未同步 解决: 乐观更新时正确处理索引 ``` #### 4.4.8 安全注意事项 **输入校验** - 时间范围验证 (不能是未来时间) - 数量范围验证 (1-99) - 备注长度限制 (200 字符) - XSS 防护 (转义用户输入) **权限控制** - 只能编辑/删除自己的记录 - Token 验证 - 接口鉴权 **数据保护** - 敏感信息脱敏 - 本地存储加密 - HTTPS 传输 --- ## 5. Phase 4: 统计与图表 (2天) ### 5.1 Day 1: 统计页基础 #### 时间范围切换 - [ ] 周/月/年 Tabs - [ ] 日期范围计算 - [ ] 数据请求 (`GET /dashboard?start=&end=`) #### 吸烟趋势图 - [ ] 集成 uCharts / ECharts - [ ] 柱状图组件封装 - [ ] 数据格式转换 #### 每周洞察卡片 - [ ] AI 分析展示 (异步加载) ### 5.2 Day 2: 详细指标 #### 健康与储蓄卡片 - [ ] 节省金额计算 + 环形进度 - [ ] 肺部功能恢复 + 环形进度 #### 成就卡片 - [ ] 连续记录天数 - [ ] 已拒绝次数 #### 趋势对比 - [ ] 周同比变化计算 - [ ] 日均吸烟量 **交付物**:完整的统计页,图表正常显示 --- ## 6. Phase 5: AI助手与个人中心 (3天) ### 6.1 Day 1: AI 助手页 #### 阶段进度卡片 - [ ] 阶段计算逻辑 - [ ] 进度条展示 - [ ] 天数倒计时 #### 每日 AI 分析 - [ ] 对话式 UI - [ ] `GET /ai/advice` 集成 - [ ] 会员/广告解锁判断 #### 今日目标 - [ ] 任务列表 - [ ] 完成状态切换 (本地存储) ### 6.2 Day 2: 个人中心页 #### 用户信息 - [ ] 头像 + 昵称展示 - [ ] 目标戒烟日期 - [ ] 连续天数 #### 设置项 - [ ] 目标设定入口 - [ ] AI 计划调整入口 - [ ] 通知设置 - [ ] 会员解锁 #### 基础设置 - [ ] 作息时间设置 - [ ] 每包价格设置 ### 6.3 Day 3: 新用户引导 #### 引导页面 - [ ] 分步表单 (5步) - [ ] 进度指示器 - [ ] 动画过渡 #### 数据收集 - [ ] 日均吸烟量 - [ ] 烟龄 - [ ] 抽烟/戒烟动机 - [ ] 作息时间 #### 提交流程 - [ ] `PUT /profile` 提交 - [ ] 完成后跳转首页 **交付物**:AI助手页、个人中心页、引导流程完整 --- ## 7. Phase 6: 优化与测试 (2天) ### 7.1 Day 1: 性能优化 #### 首页优化 - [ ] 首屏时间 < 500ms 验证 - [ ] 图片懒加载 - [ ] 组件按需加载 #### 缓存优化 - [ ] 请求缓存策略检查 - [ ] 本地存储清理策略 #### 包体积 - [ ] 依赖分析 - [ ] 无用代码移除 - [ ] 分包加载配置 ### 7.2 Day 2: 测试与修复 #### 功能测试 - [ ] 全流程测试 (新用户 → 日常使用) - [ ] 边界情况测试 - [ ] 网络异常测试 #### UI 适配 - [ ] 不同机型测试 - [ ] 安全区域适配 #### Bug 修复 - [ ] 测试问题修复 - [ ] 体验优化 **交付物**:可发布的小程序版本 --- ## 8. 开发优先级 按重要性和依赖关系排序: ``` P0 (必须完成): ├── 登录认证 ├── 首页看板 (计时器 + 今日统计) ├── 记录抽烟/忍住 ├── 历史记录查看 └── 新用户引导 P1 (核心功能): ├── 统计图表 (周视图) ├── 下次建议时间 ├── 个人设置 └── 基础 AI 建议 P2 (增强功能): ├── 月/年统计 ├── AI 时间节点 ├── 会员/广告解锁 └── 通知提醒 P3 (未来迭代): ├── 成就系统 ├── 社交分享 └── 数据导出 ``` --- ## 9. 技术风险与应对 | 风险 | 影响 | 应对措施 | |------|------|----------| | 首页加载慢 | 用户体验差 | 骨架屏 + 并行请求 + 缓存 | | 图表库体积大 | 包体积超标 | 按需引入 + 分包 | | AI 接口慢 | 等待时间长 | 异步加载 + Loading状态 | | 网络不稳定 | 数据丢失 | 离线缓存 + 重试机制 | | 微信审核不通过 | 延期上线 | 提前了解审核规范 | --- ## 10. 验收标准 ### 首页 - [ ] 首屏渲染 < 500ms - [ ] 计时器实时更新 - [ ] 记录操作 < 3步完成 ### 记录 - [ ] 支持快速记录 - [ ] 支持编辑/删除 - [ ] 数据实时同步 ### 统计 - [ ] 图表正常显示 - [ ] 数据计算准确 - [ ] 切换流畅 ### 整体 - [ ] 无明显卡顿 - [ ] 无崩溃闪退 - [ ] 视觉符合设计稿