# P13：小程序前端 — 联调补齐与格式统一 — miniapp-fe-polish

> 优先级：P13（依赖 P6~P9 前端页面 + P3 认证 + P4 核心业务 + 后端接口）
> 预估工作量：中
> 生成日期：2026-03-20

---

## 背景

小程序前端页面已完成 H5 原型还原（P6~P9），但在 MOCK 数据排查中发现多处功能点未对接真实数据或逻辑缺失。本 SPEC 统一收敛这些遗漏项，确保每个页面在联调后数据展示完整、格式统一。

---

## 一、通用规则（跨页面）

### G1：微信头像与用户信息

**现状**：`fetchMe()` 接口已定义但返回空 mock；`task-list` 声明了 `avatarUrl` 但未赋值；`performance`/`performance-records` 无 `avatarUrl` 字段。

**需求**：
- 登录成功后，从后端获取用户信息（微信头像 URL、微信昵称、角色、门店名称）
- 后端接口 `GET /api/xcx/me` 返回 `{ avatarUrl, nickName, role, storeName }`
- 头像来源：微信登录时由后端通过 `code2Session` + `getUserInfo` 获取并存储，前端不直接调用 `wx.getUserProfile`
- 所有含 banner 的页面（task-list、performance、performance-records）统一从全局用户信息读取 `avatarUrl`

**验收标准**：
- AC-G1.1：三个 banner 页面展示微信头像，无头像时显示默认占位图
- AC-G1.2：用户昵称、角色、门店名称正确展示

### G2：当月预估判断

**现状**：WXML 硬编码"预估"文案，TS 无当月判断逻辑。

**需求**：
- 当查看的数据月份 = 当前自然月时，标题显示"我的预估收入"，金额旁显示"预估"标签
- 当查看的数据月份 < 当前自然月时，标题显示"我的收入"，无"预估"标签
- 判断逻辑：`isCurrentMonth = (year === nowYear && month === nowMonth)`
- 影响页面：performance、performance-records、board-finance

**验收标准**：
- AC-G2.1：本月数据显示"预估"标签，历史月份不显示
- AC-G2.2：board-finance 本月时间筛选时，经营一览标题含"预估"字样

### G3：绩效折算（折前/折后）

**现状**：`performance-records` 接口定义了 `hoursRaw`（折前）和 `hours`（折后），WXML 有条件展示逻辑，但 `totalHoursRawLabel` 始终为空。

**需求**：
- "绩效折前"/"折前"指 DWS 层定义的绩效惩罚规则计算出的折算前课时
- 后端接口返回 `hours`（折后）和 `hoursRaw`（折前），当两者不同时前端展示"折前 Xh"
- 汇总统计同理：`totalHoursRaw` ≠ `totalHours` 时展示"折前 Xh"

**验收标准**：
- AC-G3.1：存在折算差异时，课时旁显示"折前 Xh"灰色小字
- AC-G3.2：无折算差异时不显示"折前"

### G4：储值等级显示规则

**现状**：`task-detail` 声明了 `storageLevel` 但无计算逻辑。

**需求**：
- 根据客户储值余额（balance）计算等级文案：
  - `= 0` → "无"
  - `< 200` → "少"
  - `< 500` → "一般"
  - `< 1500` → "多"
  - `≥ 1500` → "非常多"
- 计算在前端完成（后端返回 balance 数值），工具函数放 `utils/storage-level.ts`

**验收标准**：
- AC-G4.1：task-detail 储值区域根据 balance 正确显示等级文案
- AC-G4.2：balance 为 0 时显示"无"

---

## 二、各页面功能点

### P1：task-list（任务列表）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T1.1 | banner 头像 | `avatarUrl` 声明但未赋值 | 见 G1 |
| T1.2 | 比同期数据 | PerfData 有 `incomeTrend`/`incomeTrendDir`，但值为空 | 后端返回与上月同期的差值（数值，非百分比），如 `+¥1,200` / `-¥800`；前端展示数值 + ↑/↓ 箭头 |
| T1.3 | 放弃原因 | `abandonReason` 硬编码空字符串 | 从后端 task 对象的 `abandonReason` 字段获取，展示放弃时填写的备注文本 |
| T1.4 | 盖戳动画 | ✅ 已实现 | 确认：任何情况下页面加载后盖戳动画都会播放（当前仅 `tierCompleted` 时触发），需改为始终播放 |

**验收标准**：
- AC-T1.2：比同期显示为数值差（如 `+¥1,200`），非百分比
- AC-T1.3：已放弃任务卡片展示放弃备注
- AC-T1.4：页面加载后盖戳动画始终播放，不依赖 `tierCompleted`

### P2：performance（绩效总览）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T2.1 | banner 头像 | 无 `avatarUrl` 字段 | 见 G1 |
| T2.2 | 预估/实际收入 | 硬编码"预估" | 见 G2 |

### P3：performance-records（绩效明细）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T3.1 | banner 头像 | 无 `avatarUrl` 字段 | 见 G1 |
| T3.2 | 预估/实际收入 | 硬编码"预估" | 见 G2 |
| T3.3 | 总笔数 | 前端 `records.length` 计算 | 后端接口返回 `totalCount` 字段，前端直接使用（分页场景下前端计数不准确） |
| T3.4 | 绩效折前 | 接口有字段但 label 始终为空 | 见 G3 |

### P4：task-detail（任务详情）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T4.1 | 手机号码 | `onCopyPhone` 中 `phone = ''` 硬编码 | 从 `this.data.detail` 获取客户手机号（后端 task 详情接口返回 `customerPhone`） |
| T4.2 | 储值显示规则 | `storageLevel` 无计算逻辑 | 见 G4，从 detail 的 balance 字段计算 |
| T4.3 | 行动建议 | 仅有"问问助手"跳转 chat | 后端 task 详情接口返回 `actionSuggestions: string[]`（AI 生成的行动建议列表），前端在维客线索下方展示为卡片列表 |
| T4.4 | 备注打星 | ✅ 已实现 | star-rating 组件 + note-modal 爱心/台球双维度评分已完整 |

### P5：customer-service-records（客户服务记录）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T5.1 | 客户名称 | ✅ 已实现 | 从 fetchCustomerDetail 获取 |
| T5.2 | 本月服务次数 | 从 detail 取 `totalServiceCount`，类型断言 `as any` | 后端 `GET /api/xcx/customers/:id` 返回 `totalServiceCount` 字段，前端类型定义补齐 |
| T5.3 | 课程标签 | getTypeLabel 基于 includes 硬编码匹配 | 后端返回 `courseType` 枚举（basic/vip/incentive/recharge/snooker/group），前端直接映射，不再 includes 猜测 |

### P6：board-finance（财务看板）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T6.1 | AI 智能洞察 | WXML 硬编码 3 行文案 | 后端接口返回 `aiInsights: Array<{ icon: string; text: string }>`，前端动态渲染 |
| T6.2 | 环比箭头 | ✅ 已实现 | compareEnabled 开关 + ↑/↓ 箭头 + 颜色区分 |
| T6.3 | 本月"预估" | 无预估标记 | 见 G2 |

### P7：board-customer（客户看板）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T7.1 | 爱心 icon | ✅ 已实现 | heart-icon 组件 + getHeartEmoji 映射 |

### P8：customer-detail（客户详情）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T8.1 | 电话 | ✅ 已实现 | onTogglePhone + onCopyPhone 完整 |

### P9：board-coach（助教看板）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T9.1 | 级别 Icon | ✅ 已实现 | coach-level-tag 组件 + LEVEL_CLASS 映射 |

### P10：coach-detail（助教详情）

| 编号 | 功能点 | 现状 | 需求 |
|------|--------|------|------|
| T10.1 | 任务执行数量统计 | `taskStats` 硬编码 `{ recall: 24, callback: 14 }` | 后端接口返回 `taskStats: { recall: number; callback: number }`，前端从 API 获取 |

---

## 三、数据格式统一标准

### 已有格式化工具函数(但并没有进行全部调用)

| 类型 | TS 函数 | WXS 函数 | 格式示例 | 缺省值 |
|------|---------|----------|----------|--------|
| 金额 | `formatMoney(value)` | `money(value)` | ¥12,680 / -¥368 / ¥0 | -- |
| 计数 | `formatCount(value, unit)` | `count(value, unit)` | 18笔 / 3次 / 12人 | -- |
| 百分比 | `formatPercent(value)` | `percent(value)` | 12.5% / 0% | -- |
| 课时 | `formatHours(hours)` | `hours(value)` | 86h / 72.5h / 0h | -- |
| 相对时间 | `formatRelativeTime(value)` | — | 刚刚 / 3分钟前 / 2天前 / 03-10 | -- |
| 截止日期 | `formatDeadline(deadline)` | — | 逾期3天 / 今天到期 / 还剩5天 / 03-15 | -- |
| IM 时间 | `formatIMTime(value)` | — | 14:30 / 03-10 14:30 | （空字符串） |
| 爱心 | `getHeartEmoji(score)` | — | 💖 / 🧡 / 💛 / 💙 | 💙 |
| 星级 | `scoreToHalfStar(score)` | — | 4.5 / 3.0 | 0 |
| 空值兜底 | — | `safe(val)` | （原值） | -- |

### 需要补充的格式化

| 类型 | 建议函数名 | 格式示例 | 缺省值 | 说明 |
|------|-----------|----------|--------|------|
| 日期（短） | `formatDateShort(date)` | 3月15日 / 03-15 | -- | 用于服务记录、充值日期等 |
| 日期（完整） | `formatDateFull(date)` | 2026-03-15 | -- | 用于历史数据、导出 |
| 天数 | `formatDays(days)` | 3天 / 15天 | -- | 用于"N天前到店"、逾期天数 |
| 储值等级 | `formatStorageLevel(balance)` | 无/少/一般/多/非常多 | 无 | 见 G4 |
| 同比差值 | `formatTrendValue(value)` | +¥1,200 / -¥800 | -- | 用于"比同期"展示 |

### 接口端格式对齐原则

1. 后端返回原始数值（number），前端负责格式化展示
2. 金额单位统一为"元"（整数），前端加 ¥ 前缀和千分位
3. 课时单位统一为"小时"（number），前端加 h 后缀
4. 日期统一 ISO 8601 格式（`YYYY-MM-DDTHH:mm:ss`），前端按场景格式化
5. 百分比后端返回 0-100 的 number，前端加 % 后缀
6. 所有 null/undefined/0 值，前端统一展示为 `--`（通过 format 函数兜底）

---

## 四、实施优先级

### 第一批（阻塞联调）
- G1 微信头像（影响 3 个页面 banner）
- T1.3 放弃原因（后端字段直通）
- T4.1 手机号码（后端字段直通）
- T3.3 总笔数（后端字段直通）
- T10.1 任务执行统计（后端字段直通）

### 第二批（业务逻辑）
- G2 当月预估判断（纯前端逻辑）
- G3 绩效折算展示（前端条件渲染）
- G4 储值等级规则（前端计算）
- T1.2 比同期数据（需后端新增字段）
- T1.4 盖戳动画始终播放（前端逻辑调整）
- T5.3 课程标签枚举化（前后端对齐）

### 第三批（增强体验）
- T4.3 行动建议（需后端 AI 接口）
- T6.1 AI 智能洞察（需后端 AI 接口）
- 格式化工具函数补充

---

## 五、涉及文件清单

### 前端
- `apps/miniprogram/miniprogram/services/api.ts`
- `apps/miniprogram/miniprogram/utils/money.ts`
- `apps/miniprogram/miniprogram/utils/time.ts`
- `apps/miniprogram/miniprogram/utils/storage-level.ts`（新建）
- `apps/miniprogram/miniprogram/pages/task-list/task-list.ts`
- `apps/miniprogram/miniprogram/pages/performance/performance.ts`
- `apps/miniprogram/miniprogram/pages/performance-records/performance-records.ts`
- `apps/miniprogram/miniprogram/pages/task-detail/task-detail.ts`
- `apps/miniprogram/miniprogram/pages/customer-service-records/customer-service-records.ts`
- `apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts`
- `apps/miniprogram/miniprogram/pages/coach-detail/coach-detail.ts`

### 后端（接口变更）
- `GET /api/xcx/me` — 补充 avatarUrl 字段
- `GET /api/xcx/tasks` — performance 补充 `trendValue`（同比差值）
- `GET /api/xcx/tasks/:id` — 补充 `customerPhone`、`actionSuggestions`
- `GET /api/xcx/customers/:id` — 类型定义补充 `totalServiceCount`
- `GET /api/xcx/performance/records` — 补充 `totalCount`、`hoursRaw`
- `GET /api/xcx/coaches/:id` — 补充 `taskStats`
- `GET /api/xcx/board/finance` — 补充 `aiInsights`、`isEstimated`
