# 实施计划：P13 小程序前端联调补齐与格式统一

## 概述

按照 P13 PRD 文档，对小程序全部页面进行联调补齐与格式统一。执行策略：先完成通用工具函数，再逐页面由子代理审查修改。每个页面审查三个维度：① 通用规则（G1~G4）② 数据格式统一标准 ③ 页面专属功能点。子代理并行最大数量为 2，每个子代理每次处理一个页面。

## 参考文档

- PRD：`docs/prd/specs/P13-miniapp-fe-polish.md`
- 展示规范：`docs/miniprogram-dev/design-system/DISPLAY-STANDARDS.md`、`DISPLAY-STANDARDS-2.md`
- API 契约：`docs/miniprogram-dev/API-contract.md`
- API 输出规范：`docs/miniprogram-dev/API-OUTPUT-SPEC.md`
- VI 设计系统：`docs/miniprogram-dev/design-system/VI-DESIGN-SYSTEM.md`

## 任务

### 第零阶段：通用工具函数准备

- [x] 0. 通用工具函数补充
  - [x] 0.1 新建 `apps/miniprogram/miniprogram/utils/storage-level.ts`
    - 导出 `formatStorageLevel(balance: number): string`
    - 阈值：`= 0` → "无"、`< 200` → "少"、`< 500` → "一般"、`< 1500` → "多"、`≥ 1500` → "非常多"
    - null/undefined 输入返回 "无"
    - _需求: REQ-G4_
  - [x] 0.2 修改 `apps/miniprogram/miniprogram/utils/money.ts`
    - 新增 `formatTrendValue(value: number | null | undefined): string`
    - 正数 → "+¥1,200"（千分位），负数 → "-¥800"，0/null/undefined → "--"
    - _需求: REQ-FMT-1_
  - [x] 0.3 修改 `apps/miniprogram/miniprogram/utils/time.ts`
    - 新增 `formatDateShort(date: string | Date | null | undefined): string` → "3月15日" 格式，空值 → "--"
    - 新增 `formatDateFull(date: string | Date | null | undefined): string` → "2026-03-15" 格式，空值 → "--"
    - 新增 `formatDays(days: number | null | undefined): string` → "3天" 格式，0/null/undefined → "--"
    - _需求: REQ-FMT-1_
  - [x] 0.4 修改 `apps/miniprogram/miniprogram/utils/format.wxs`
    - 补充 WXS 侧对应函数（如需要在 WXML 中直接调用）
    - _需求: REQ-FMT-1_

### 第一阶段：逐页面审查修改（子代理并行，最大 2 个）

每个页面任务包含三个审查维度，子代理必须按顺序逐一完成：
- A. 通用规则审查（G1~G4 中适用于该页面的规则）
- B. 数据格式统一审查（金额/计数/百分比/课时/时间/空值是否通过格式化函数处理）
- C. 页面专属功能点修改（PRD 中该页面的具体需求）

---

- [x] 1. 【子代理】task-list 页面审查修改
  - [x] 1.A 通用规则审查
    - [x] 1.A.1 G1 微信头像：确认 `avatarUrl` 从全局用户信息读取并赋值到 data，无头像时显示默认占位图
      - 检查 `task-list.ts` 中 `avatarUrl` 的赋值逻辑
      - 检查 `task-list.wxml` 中头像 image 组件的 src 绑定和默认图
      - _需求: REQ-G1, AC-G1.1, AC-G1.2_
    - [x] 1.A.2 G2 当月预估：确认收入标题根据 isCurrentMonth 切换"预估收入"/"我的收入"
      - 检查 TS 中是否有 `isCurrentMonth` 判断逻辑
      - 检查 WXML 中"预估"标签的条件渲染
      - _需求: REQ-G2, AC-G2.1_
  - [x] 1.B 数据格式统一审查
    - [x] 1.B.1 检查所有金额展示是否通过 `formatMoney` / WXS `money()` 处理
    - [x] 1.B.2 检查所有计数展示是否通过 `formatCount` / WXS `count()` 处理
    - [x] 1.B.3 检查所有空值是否通过 `safe()` 兜底为 "--"
    - [x] 1.B.4 检查课时展示是否通过 `formatHours` / WXS `hours()` 处理
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 1.C 页面专属功能点
    - [x] 1.C.1 T1.2 比同期数据：确认 `incomeTrend`/`incomeTrendDir` 从后端获取，使用 `formatTrendValue` 格式化展示，配合 ↑/↓ 箭头
      - _需求: REQ-T1.2, AC-T1.2_
    - [x] 1.C.2 T1.3 放弃原因：确认 `abandonReason` 从后端 task 对象获取，不再硬编码空字符串
      - _需求: REQ-T1.3, AC-T1.3_
    - [x] 1.C.3 T1.4 盖戳动画：修改为页面加载后始终播放，移除 `tierCompleted` 条件依赖
      - _需求: REQ-T1.4, AC-T1.4_

---

- [x] 2. 【子代理】performance 页面审查修改
  - [x] 2.A 通用规则审查
    - [x] 2.A.1 G1 微信头像：确认 banner 区域 `avatarUrl` 从全局用户信息读取，无头像时显示默认占位图
      - _需求: REQ-G1, AC-G1.1, AC-G1.2_
    - [x] 2.A.2 G2 当月预估：确认收入标题根据 isCurrentMonth 切换"预估收入"/"我的收入"，"预估"标签条件渲染
      - _需求: REQ-G2, AC-G2.1_
  - [x] 2.B 数据格式统一审查
    - [x] 2.B.1 检查所有金额展示是否通过格式化函数处理
    - [x] 2.B.2 检查所有课时展示是否通过格式化函数处理
    - [x] 2.B.3 检查所有百分比展示是否通过格式化函数处理
    - [x] 2.B.4 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 2.C 页面专属功能点
    - （无额外专属功能点，T2.1/T2.2 已由 G1/G2 覆盖）

---

- [x] 3. 【子代理】performance-records 页面审查修改
  - [x] 3.A 通用规则审查
    - [x] 3.A.1 G1 微信头像：确认 banner 区域 `avatarUrl` 从全局用户信息读取
      - _需求: REQ-G1, AC-G1.1_
    - [x] 3.A.2 G2 当月预估：确认收入标题根据 isCurrentMonth 切换
      - _需求: REQ-G2, AC-G2.1_
    - [x] 3.A.3 G3 绩效折算：确认 `hoursRaw !== hours` 时展示"折前 Xh"灰色小字，无差异时不显示
      - _需求: REQ-G3, AC-G3.1, AC-G3.2_
  - [x] 3.B 数据格式统一审查
    - [x] 3.B.1 检查所有金额/课时/计数/百分比展示是否通过格式化函数处理
    - [x] 3.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 3.C 页面专属功能点
    - [x] 3.C.1 T3.3 总笔数：确认使用后端返回的 `totalCount` 字段，不再用 `records.length` 计算
      - _需求: REQ-T3.3, AC-T3.3_

---

- [x] 4. 【子代理】task-detail 页面审查修改
  - [x] 4.A 通用规则审查
    - [x] 4.A.1 G4 储值等级：确认从 `detail.balance` 调用 `formatStorageLevel()` 计算等级文案并展示
      - _需求: REQ-G4, AC-G4.1, AC-G4.2_
  - [x] 4.B 数据格式统一审查
    - [x] 4.B.1 检查所有金额展示是否通过格式化函数处理
    - [x] 4.B.2 检查所有时间/日期展示是否通过格式化函数处理
    - [x] 4.B.3 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 4.C 页面专属功能点
    - [x] 4.C.1 T4.1 手机号码：修改 `onCopyPhone` 从 `this.data.detail.customerPhone` 获取，不再硬编码空字符串
      - _需求: REQ-T4.1, AC-T4.1_
    - [x] 4.C.2 T4.3 行动建议：确认后端返回 `actionSuggestions: string[]`，前端在维客线索下方渲染为卡片列表
      - _需求: REQ-T4.3, AC-T4.3_

---

- [x] 5. 【子代理】customer-service-records 页面审查修改
  - [x] 5.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 5.B 数据格式统一审查
    - [x] 5.B.1 检查所有金额/计数/时间展示是否通过格式化函数处理
    - [x] 5.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 5.C 页面专属功能点
    - [x] 5.C.1 T5.2 本月服务次数：补齐 `totalServiceCount` 的 TypeScript 类型定义，移除 `as any` 断言
      - _需求: REQ-T5.2, AC-T5.2_
    - [x] 5.C.2 T5.3 课程标签：修改 `getTypeLabel` 为基于 `courseType` 枚举的直接映射，移除 includes 硬编码匹配
      - _需求: REQ-T5.3, AC-T5.3_

---

- [x] 6. 【子代理】board-finance 页面审查修改
  - [x] 6.A 通用规则审查
    - [x] 6.A.1 G2 当月预估：确认本月时间筛选时，经营一览标题含"预估"字样
      - _需求: REQ-G2, AC-G2.2_
  - [x] 6.B 数据格式统一审查
    - [x] 6.B.1 检查所有金额展示是否通过格式化函数处理
    - [x] 6.B.2 检查所有百分比/计数展示是否通过格式化函数处理
    - [x] 6.B.3 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 6.C 页面专属功能点
    - [x] 6.C.1 T6.1 AI 智能洞察：修改 WXML 移除硬编码文案，改为从后端 `aiInsights: Array<{ icon: string; text: string }>` 动态渲染
      - _需求: REQ-T6.1, AC-T6.1_

---

- [x] 7. 【子代理】board-customer 页面审查修改
  - [x] 7.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 7.B 数据格式统一审查
    - [x] 7.B.1 检查所有金额/计数/百分比展示是否通过格式化函数处理
    - [x] 7.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 7.C 页面专属功能点
    - （T7.1 爱心 icon 已实现，无额外修改）

---

- [x] 8. 【子代理】board-coach 页面审查修改
  - [x] 8.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 8.B 数据格式统一审查
    - [x] 8.B.1 检查所有金额/计数/课时展示是否通过格式化函数处理
    - [x] 8.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 8.C 页面专属功能点
    - （T9.1 级别 Icon 已实现，无额外修改）

---

- [x] 9. 【子代理】coach-detail 页面审查修改
  - [x] 9.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 9.B 数据格式统一审查
    - [x] 9.B.1 检查所有金额/课时/计数展示是否通过格式化函数处理
    - [x] 9.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 9.C 页面专属功能点
    - [x] 9.C.1 T10.1 任务执行统计：修改 `taskStats` 从后端 API 获取，移除硬编码 `{ recall: 24, callback: 14 }`
      - _需求: REQ-T10.1, AC-T10.1_

---

- [x] 10. 【子代理】customer-detail 页面审查修改
  - [x] 10.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 10.B 数据格式统一审查
    - [x] 10.B.1 检查所有金额/计数/时间展示是否通过格式化函数处理
    - [x] 10.B.2 检查所有空值是否兜底为 "--"
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 10.C 页面专属功能点
    - （T8.1 电话已实现，无额外修改）


---

- [x] 11. 【子代理】chat 页面审查修改
  - [x] 11.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 11.B 数据格式统一审查
    - [x] 11.B.1 检查所有时间展示是否通过格式化函数处理（消息时间、IM 时间）
    - [x] 11.B.2 检查所有空值是否兜底
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 11.C 页面专属功能点
    - （RNS1.4 已完成 chat 改造，无额外 P13 功能点）

---

- [x] 12. 【子代理】chat-history 页面审查修改
  - [x] 12.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 12.B 数据格式统一审查
    - [x] 12.B.1 检查所有时间展示是否通过格式化函数处理
    - [x] 12.B.2 检查所有空值是否兜底
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 12.C 页面专属功能点
    - （RNS1.4 已完成 chat-history 改造，无额外 P13 功能点）

---

- [x] 13. 【子代理】notes 页面审查修改
  - [x] 13.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 13.B 数据格式统一审查
    - [x] 13.B.1 检查所有时间展示是否通过格式化函数处理
    - [x] 13.B.2 检查所有空值是否兜底
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 13.C 页面专属功能点
    - （无额外 P13 功能点）

---

- [x] 14. 【子代理】my-profile 页面审查修改
  - [x] 14.A 通用规则审查
    - [x] 14.A.1 G1 微信头像：如页面展示头像，确认从全局用户信息读取
      - _需求: REQ-G1_
  - [x] 14.B 数据格式统一审查
    - [x] 14.B.1 检查所有展示数据是否通过格式化函数处理
    - [x] 14.B.2 检查所有空值是否兜底
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 14.C 页面专属功能点
    - （无额外 P13 功能点）

---

- [x] 15. 【子代理】login / apply / reviewing / no-permission 页面审查修改
  - [x] 15.A 通用规则审查
    - （无 G1~G4 适用项）
  - [x] 15.B 数据格式统一审查
    - [x] 15.B.1 检查所有展示数据是否通过格式化函数处理
    - [x] 15.B.2 检查所有空值是否兜底
    - _需求: REQ-FMT-2, REQ-FMT-3, AC-FMT_
  - [x] 15.C 页面专属功能点
    - （无额外 P13 功能点，这四个页面为认证流程页，数据展示极少）

### 第二阶段：收尾验证

- [x] 16. services/api.ts 接口字段对齐审查
  - [x] 16.1 确认 `fetchMe()` 返回类型包含 `avatarUrl`、`nickName`、`role`、`storeName`
  - [x] 16.2 确认 task 相关接口返回类型包含 `abandonReason`、`customerPhone`、`actionSuggestions`
  - [x] 16.3 确认 performance-records 接口返回类型包含 `totalCount`、`hoursRaw`
  - [x] 16.4 确认 customer 接口返回类型包含 `totalServiceCount`（无 `as any`）
  - [x] 16.5 确认 coach-detail 接口返回类型包含 `taskStats`
  - [x] 16.6 确认 board-finance 接口返回类型包含 `aiInsights`、`isEstimated`
  - _需求: 全部 REQ-T* 接口字段对齐_

- [x] 17. 最终检查点
  - 确认所有 15 个页面任务（任务 1~15）均已完成
  - 确认通用工具函数（任务 0）已就绪
  - 确认 services/api.ts 类型定义完整（任务 16）
  - 确认无硬编码 mock 数据残留
  - 确认所有空值展示为 "--"

## 执行规则

### 子代理调度
- 最大并行数：2 个子代理
- 每个子代理每次处理 1 个页面
- 子代理必须按 A → B → C 顺序审查每个页面
- 子代理需读取页面的 `.ts`、`.wxml`、`.wxss` 文件，以及相关的 WXS 文件

### 子代理审查清单（每个页面通用）

**A. 通用规则审查**
- G1：页面是否有 banner/头像区域？→ 检查 avatarUrl 来源
- G2：页面是否展示收入/金额标题？→ 检查 isCurrentMonth 判断
- G3：页面是否展示课时？→ 检查 hoursRaw vs hours 条件渲染
- G4：页面是否展示储值信息？→ 检查 formatStorageLevel 调用

**B. 数据格式统一审查**
- 金额：是否通过 `formatMoney` / WXS `money()` 处理？
- 计数：是否通过 `formatCount` / WXS `count()` 处理？
- 百分比：是否通过 `formatPercent` / WXS `percent()` 处理？
- 课时：是否通过 `formatHours` / WXS `hours()` 处理？
- 时间：是否通过 `formatRelativeTime` / `formatDeadline` / `formatIMTime` 处理？
- 空值：所有 null/undefined/0 是否通过 `safe()` 兜底为 "--"？
- 趋势值：是否通过 `formatTrendValue` 处理？

**C. 页面专属功能点**
- 对照 PRD 第二节该页面的功能点表格逐项检查
- 已标记 ✅ 的功能点仅做确认，不修改
- 未完成的功能点执行修改

### 参考文件清单（子代理必读）
- 目标页面的 `.ts` + `.wxml` + `.wxss` 文件
- `apps/miniprogram/miniprogram/utils/money.ts` — 金额格式化
- `apps/miniprogram/miniprogram/utils/time.ts` — 时间格式化
- `apps/miniprogram/miniprogram/utils/format.wxs` — WXS 格式化
- `apps/miniprogram/miniprogram/utils/storage-level.ts` — 储值等级（任务 0 新建）
- `apps/miniprogram/miniprogram/services/api.ts` — 接口定义
- `docs/prd/specs/P13-miniapp-fe-polish.md` — PRD 原文
- `docs/miniprogram-dev/design-system/DISPLAY-STANDARDS.md` — 展示规范
