Files
smt/docs/DEVELOPMENT.md
T

20 KiB
Raw Blame History

开发计划与任务拆分

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)

  • 底部弹出动画 (从底部滑入,半屏展示)
  • 明亮主题 UI (白色背景 + 翡翠绿主题色 #10B981)
  • 遮罩层点击关闭
  • 表单项布局

表单字段实现

  • 时间选择器

    • 日期选择 (picker mode="date")
    • 时间选择 (picker mode="time")
    • 默认当前时间
    • 自动拼接为 smoke_at 字段
  • 数量选择器 (仅抽烟模式显示)

    • 加减按钮 (+/-)
    • 数字输入框
    • 最小值 1
    • 默认值 1
  • 烟瘾等级选择 (仅抽烟模式显示)

    • 1-5 级按钮组
    • 选中态样式切换
    • 默认值 2
  • 备注输入框

    • 多行文本域 (textarea)
    • 最大长度 200 字符
    • 根据模式显示不同占位符
      • 抽烟: "记录抽烟原因..."
      • 忍住: "记录抵抗心得..."

组件逻辑

  • Props 定义

    {
      show: Boolean,      // 控制显示
      type: String        // 'smoke' | 'resisted'
    }
    
  • 数据初始化

    • 打开弹框时自动初始化当前时间
    • 根据 type 设置默认值
      • smoke: num=1, level=2
      • resisted: num=0, level=2
  • 提交逻辑

    {
      smoke_time: "2025-01-25",
      smoke_at: "2025-01-25 14:30:00",
      remark: "",
      level: 2,
      num: 1 // 或 0 (忍住时)
    }
    
  • 关闭动画

    • 延迟 300ms 触发 update:show
    • 平滑过渡效果

4.1.2 组件配置

easycom 自动导入 (pages.json)

  • 配置组件路径
    {
      "easycom": {
        "autoscan": true,
        "custom": {
          "^smoke-record-dialog$": "@/components/smoke-record-dialog/smoke-record-dialog.vue"
        }
      }
    }
    

组件文档

  • 创建 components/smoke-record-dialog/README.md
  • 包含使用示例、Props 说明、Events 说明
  • 创建 components/README.md 全局组件使用指南

4.1.3 API 集成

API 封装 (api/smoke.js)

  • 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

  • getLatestLogs(limit) - GET /api/v1/smoke/logs/latest

首页集成 (pages/index/index.vue)

  • 引入组件 (easycom 自动导入)

  • 双向绑定 show 状态

  • 区分两种模式

    <smoke-record-dialog 
      v-model:show="showDialog"
      :type="dialogType"
      @submit="handleSubmit"
    />
    
  • 提交处理

    • 调用 API 创建记录
    • 更新 dashboard store 数据
      • 抽烟: incrementTodayCount()
      • 抽烟: resetTimer()
    • 显示成功提示
    • 关闭弹框

性能优化

  • 组件懒加载 (easycom 按需加载)
  • 表单数据仅在打开时初始化
  • 提交后自动关闭

4.2 Day 2: 历史记录页

4.2.1 页面结构 (pages/logs/index.vue)

顶部导航

  • 导航栏标题 "历史记录"
  • 筛选 Tabs 组件
    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)

{
  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

    <smoke-record-dialog 
      v-model:show="showEditDialog"
      :type="editType"
      :record-id="editRecordId"
      mode="edit"
      @submit="handleUpdate"
    />
    
  • 预填充数据

  • 调用 updateLog API

  • 刷新列表

删除功能

  • uni.showModal 确认对话框
  • 调用 deleteLog API
  • 乐观更新 (先删除本地数据)
  • 失败时回滚

4.2.5 日期分组

分组逻辑

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)
}

渲染结构

<view class="date-group" v-for="group in groupedLogs" :key="group.date">
  <view class="date-header">{{ group.displayDate }}</view>
  <log-item 
    v-for="log in group.logs" 
    :key="log.id"
    :data="log"
    @edit="handleEdit"
    @delete="handleDelete"
  />
</view>

4.2.6 加载状态

下拉刷新

  • refresher-triggered 状态控制
  • 重置 page = 1
  • 清空现有数据
  • 调用 fetchLogs(true)
  • 完成后关闭刷新状态

上拉加载

  • onReachBottom 触发
  • 检查 hasMore 状态
  • page++
  • 调用 loadMore()
  • 追加数据到列表

加载骨架屏

  • 初次加载显示
  • 3-5 个卡片骨架
  • shimmer 动画效果

空状态

  • 无数据时显示
  • 空状态图标 + 文案
  • 引导按钮 "去记录"

4.2.7 浮动操作按钮

新增按钮

  • 固定在右下角
  • 圆形按钮 (96rpx)
  • 图标
  • 点击打开记录弹框
  • 阴影 + 缩放动画

样式

.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 验收标准

记录功能

  • 弹框动画流畅 (< 300ms)
  • 表单提交成功率 > 99%
  • 支持快速记录 (< 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 职责划分

// 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)

// 已实现
- Token 失效自动刷新
- 网络错误重试 (最多 3 )
- 错误码统一处理
- Toast 提示

// 需要注意
- 记录提交失败要保留用户输入
- 删除失败要回滚本地状态
- 编辑冲突要提示用户

乐观更新 vs 悲观更新

  • 删除操作: 乐观更新 (先删除,失败回滚)
  • 新增操作: 悲观更新 (成功后添加)
  • 编辑操作: 悲观更新 (成功后更新)

4.4.4 性能优化清单

组件层面

  • easycom 按需加载
  • 弹框懒加载 (打开时才初始化数据)
  • 虚拟列表 (历史记录 > 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步完成

记录

  • 支持快速记录
  • 支持编辑/删除
  • 数据实时同步

统计

  • 图表正常显示
  • 数据计算准确
  • 切换流畅

整体

  • 无明显卡顿
  • 无崩溃闪退
  • 视觉符合设计稿