Files
Neo-ZQYY/docs/miniprogram-dev/api-audit/coach-detail.md

273 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# coach-detail 页面数据来源排查
> 排查日期2026-03-18
> 页面路径pages/coach-detail/coach-detail
## 概览
| 分类 | 数量 | 说明 |
|------|------|------|
| A. Mock 数据(页面内联) | 7 组 | 页面 .ts 文件顶部定义的大量 mock 常量 |
| A. Mock 数据mock-data.ts | 1 处 | 仅引用 `mockCoaches` 做 ID→名称匹配 |
| B. 硬编码数据 | 12 处 | perfCards 文案/sub、tierNodes、动画参数、avatar 路径等 |
| C. 已对接 API | 0 | 全部数据均为 mock无真实 API 调用 |
| D. 前端计算/派生 | 8 处 | perfCards 组装、incomeTotal、perfPercent、进度条参数等 |
| E. 路由参数 | 1 个 | `options.id``coachId` |
| F. WXML 硬编码文案 | 22 处 | 各 section 标题、按钮文案、空态提示等 |
---
## 一、Mock 数据
### A1. 页面内联 Mockcoach-detail.ts 顶部常量)
| 变量名 | 类型 | 字段数 | 用途 | 联调替换目标 |
|--------|------|--------|------|-------------|
| `mockCoachDetail` | `CoachDetail` | 12 个顶层字段 | 助教基本信息 + 绩效 + 收入 + 备注 | `GET /api/xcx/coaches/:id` |
| `mockVisibleTasks` | `TaskItem[]` | 6 条 | 当前可见任务列表 | `GET /api/xcx/coaches/:id/tasks` |
| `mockHiddenTasks` | `TaskItem[]` | 3 条 | 折叠区任务列表 | 同上(分页/分组) |
| `mockAbandonedTasks` | `AbandonedTask[]` | 2 条 | 已放弃任务 | 同上status=abandoned |
| `mockTopCustomers` | `TopCustomer[]` | 20 条 | 客户关系 TOP20 | `GET /api/xcx/coaches/:id/top-customers` |
| `mockServiceRecords` | `ServiceRecord[]` | 4 条 | 近期服务明细 | `GET /api/xcx/coaches/:id/service-records` |
| `mockHistoryMonths` | `HistoryMonth[]` | 5 条 | 历史月度汇总 | `GET /api/xcx/coaches/:id/history` |
#### mockCoachDetail 字段明细
| 字段路径 | Mock 值 | 说明 |
|----------|---------|------|
| `id` | `'coach-001'` | 助教 ID |
| `name` | `'小燕'` | 助教姓名 |
| `avatar` | `'/assets/images/avatar-coach.png'` | 头像(硬编码路径) |
| `level` | `'星级'` | 助教等级 |
| `skills` | `['中🎱', '🎯斯诺克']` | 技能标签 |
| `workYears` | `3` | 工龄 |
| `customerCount` | `68` | 客户数 |
| `hireDate` | `'2023-03-15'` | 入职日期 |
| `performance.monthlyHours` | `87.5` | 本月定档业绩(小时) |
| `performance.monthlySalary` | `6950` | 本月工资(预估) |
| `performance.customerBalance` | `86200` | 客源储值余额 |
| `performance.tasksCompleted` | `38` | 本月任务完成数 |
| `performance.perfCurrent` | `80` | 绩效当前值 |
| `performance.perfTarget` | `100` | 绩效目标值 |
| `income.thisMonth[]` | 4 条 | 本月收入明细(基础课时费/激励课时费/充值提成/酒水提成) |
| `income.lastMonth[]` | 4 条 | 上月收入明细 |
| `notes[]` | 3 条 | 备注记录 |
#### mockVisibleTasks / mockHiddenTasks 字段明细
| 字段 | 说明 |
|------|------|
| `typeLabel` | 任务类型文案(高优先召回/优先召回/关系构建/客户回访) |
| `typeClass` | 样式类名 |
| `customerName` | 客户姓名 |
| `noteCount` | 备注数量 |
| `pinned` | 是否置顶 |
| `notes[]` | 备注列表pinned/text/date |
#### mockAbandonedTasks 字段明细
| 字段 | 说明 |
|------|------|
| `customerName` | 客户姓名 |
| `reason` | 放弃原因(客户拒绝/超时未响应) |
#### mockTopCustomers 字段明细20 条)
| 字段 | 说明 |
|------|------|
| `id` | 客户 ID |
| `name` | 客户姓名 |
| `initial` | 姓氏首字(头像占位) |
| `avatarGradient` | 头像渐变色 key |
| `heartEmoji` | 爱心 emoji/💛/🤍) |
| `score` | 亲密度评分 |
| `scoreColor` | 评分颜色 key |
| `serviceCount` | 服务次数 |
| `balance` | 储值余额 |
| `consume` | 消费金额 |
#### mockServiceRecords 字段明细4 条)
| 字段 | 说明 |
|------|------|
| `customerId` | 客户 ID可选 |
| `customerName` | 客户姓名 |
| `initial` | 姓氏首字 |
| `avatarGradient` | 头像渐变色 key |
| `type` | 服务类型(基础课/激励课) |
| `typeClass` | 样式类名 |
| `table` | 台号 |
| `duration` | 时长 |
| `income` | 收入 |
| `date` | 日期 |
| `perfHours` | 定档绩效时长(可选) |
#### mockHistoryMonths 字段明细5 条)
| 字段 | 说明 |
|------|------|
| `month` | 月份标签(本月/上月/4月/3月/2月 |
| `estimated` | 是否预估 |
| `customers` | 服务客户数 |
| `hours` | 业绩时长 |
| `salary` | 工资 |
| `callbackDone` | 回访完成数 |
| `recallDone` | 召回完成数 |
### A2. 外部 Mock 引用mock-data.ts
| 引用 | 用途 | 代码位置 |
|------|------|----------|
| `mockCoaches` | `loadData()` 中通过 `mockCoaches.find(c => c.id === id)` 匹配助教 ID`id``name` 覆盖 mockCoachDetail | `coach-detail.ts` L197-198 |
> `mockCoaches` 来自 `utils/mock-data.ts`,类型为 `CoachCard[]`,含 3 条记录coach-001/002/003
---
## 二、硬编码数据
| 位置 | 硬编码内容 | 说明 |
|------|-----------|------|
| `loadData()` perfCards[0].sub | `'折算前 89.0h'` | 应由 API 返回折算前时长 |
| `loadData()` perfCards[1].sub | `'含预估部分'` | 固定文案 |
| `loadData()` perfCards[2].sub | `` `${detail.customerCount}位客户合计` `` | 模板拼接customerCount 来自 mock |
| `loadData()` perfCards[3].sub | `'覆盖 22 位客户'` | 应由 API 返回覆盖客户数 |
| `loadData()` tierNodes | `[0, 100, 130, 160, 190, 220]` | 绩效档位节点,注释标注"Mock实际由接口返回" |
| `loadData()` maxHours | `220` | 进度条最大值,应与 tierNodes 联动 |
| `data.taskStats` | `{ recall: 24, callback: 14 }` | 任务统计,应由 API 返回 |
| 动画常量 SHINE_SPEED | `70` | 进度条动画速度 |
| 动画常量 SPARK_DELAY_MS | `-150` | 火花延迟 |
| 动画常量 SPARK_DUR_MS | `1400` | 火花持续时间 |
| 动画常量 NEXT_LOOP_DELAY_MS | `400` | 下一轮延迟 |
| 动画常量 SHINE_WIDTH_RPX / TRACK_WIDTH_RPX | `120 / 634` | 进度条尺寸参数UI 常量,非业务数据) |
---
## 三、已对接 API
**无。** 当前页面 0 个真实 API 调用。
`loadData()` 使用 `setTimeout(500ms)` 模拟异步加载,内部全部使用 mock 数据。
代码中有两处 TODO 注释标记了待对接接口:
1. `// TODO: 替换为真实 API 调用 GET /api/coaches/:id` — `loadData()` 内
2. `// TODO: 替换为真实 API 调用 POST /api/xcx/notes` — `onNoteConfirm()` 内
---
## 四、前端计算/派生数据
| 字段 | 计算逻辑 | 依赖数据 |
|------|----------|----------|
| `perfCards[]` | 由 `detail.performance` 各字段组装为展示卡片数组 | `performance.*` (mock) |
| `perfGap` | `perfTarget - perfCurrent` | `performance.perfTarget/perfCurrent` (mock) |
| `perfPercent` | `Math.min(Math.round(perfCurrent/perfTarget*100), 100)` | 同上 |
| `pbFilledPct` | `Math.min(100, Math.round(totalHours/maxHours*1000)/10)` | `performance.monthlyHours` + 硬编码 `maxHours=220` |
| `pbCurrentTier` | 遍历 tierNodes 找到当前所在档位 | `performance.monthlyHours` + 硬编码 `tierNodes` |
| `pbTicks[]` | `buildTicks(tierNodes, maxHours)` — 计算每个档位的 left 百分比 | 硬编码 `tierNodes` + `maxHours` |
| `incomeTotal` | `items.reduce()` 累加当前 tab 的收入金额 | `detail.income.thisMonth/lastMonth` (mock) |
| `currentIncome` | 根据 `incomeTab` 切换本月/上月数据 | `detail.income.*` (mock) |
| `sortedNotes` | `sortByTimestamp(detail.notes, 'timestamp')` 按时间降序排列 | `detail.notes` (mock) |
| `pbShineDurMs` | `calcShineDur(filledPct)` — 根据填充百分比计算动画时长 | `pbFilledPct` (派生) |
| `pbClampedSparkPct` | `Math.max(0, Math.min(100, pbFilledPct))` | `pbFilledPct` (派生) |
| 新备注 `newNote` | `onNoteConfirm()` 中用 `Date.now()` 生成 ID 和时间 | 用户输入 `content` + 当前时间 |
---
## 五、路由参数
| 参数 | 来源 | 用途 |
|------|------|------|
| `options.id` | `onLoad(options)` 从页面路由 query 获取 | 赋值给 `coachId`,传入 `loadData(id)` 用于匹配 mockCoaches |
---
## 六、WXML 硬编码文案
| 文案 | 位置 | 说明 |
|------|------|------|
| `加载中...` | 加载态 toast | 状态提示 |
| `未找到助教信息` | 空态 | 状态提示 |
| `加载失败` | 错误态 | 状态提示 |
| `点击重试` | 错误态按钮 | 操作文案 |
| `绩效概览` | section 标题 | 固定文案 |
| `绩效档位进度` | 进度条标题 | 固定文案 |
| `距下一档还差 {{perfGap}}h` | 进度条提示 | 模板 + 派生数据 |
| `收入明细` | section 标题 | 固定文案 |
| `本月` / `上月` | 收入 Tab | 固定文案 |
| `预估` | 收入 Tab 标签 | 固定文案 |
| `合计(预估)` / `合计` | 收入合计行 | 条件文案 |
| `任务执行` | section 标题 | 固定文案 |
| `本月完成` | 任务统计标签 | 固定文案 |
| `回访` / `召回` + `` | 任务统计 | 固定文案 |
| `收起 ↑` / `展开全部 ↓` | 任务折叠按钮 | 条件文案 |
| `客户关系 TOP20` | section 标题 | 固定文案 |
| `近60天` | 客户关系副标题 | 固定文案(应由 API 返回时间范围) |
| `近期服务明细` | section 标题 | 固定文案 |
| `查看更多服务记录 →` | 服务明细底部 | 操作文案 |
| `更多信息` | section 标题 | 固定文案 |
| `入职日期` | 更多信息标签 | 固定文案 |
| `备注记录` + `共 {{sortedNotes.length}} 条` | section 标题 | 固定 + 动态 |
| `暂无备注` | 备注空态 | 状态提示 |
| `问问助手` | 底部按钮 | 操作文案 |
| `备注` | 底部按钮 | 操作文案 |
| `备注已保存` | Toast 提示 | 操作反馈 |
| `页面跳转失败` | 多处 navigateTo fail | 错误提示 |
| 历史表头:`月份` / `服务客户` / `访/召完成` / `业绩时长` / `工资` | 历史月度表格 | 固定文案 |
---
## 七、引用组件
| 组件 | 路径 | 用途 |
|------|------|------|
| `coach-level-tag` | `/components/coach-level-tag/` | 助教等级标签(接收 `level` prop |
| `perf-progress-bar` | `/components/perf-progress-bar/` | 绩效进度条(接收多个动画参数 prop |
| `note-modal` | `/components/note-modal/` | 备注弹窗(接收 `visible`/`customerName`emit `confirm`/`cancel` |
| `dev-fab` | `/components/dev-fab/` | 开发调试浮动按钮 |
| `t-loading` | TDesign | 加载动画 |
| `t-icon` | TDesign | 图标 |
| `t-tag` | TDesign | 标签(已注册但 WXML 中未使用) |
---
## 八、引用工具函数
| 函数 | 来源 | 用途 |
|------|------|------|
| `sortByTimestamp` | `utils/sort.ts` | 对备注列表按 `timestamp` 字段降序排序 |
---
## 九、联调 TODO 清单
### 优先级 P0 — 核心数据
| # | 待对接接口 | 替换目标 | 涉及字段 |
|---|-----------|----------|----------|
| 1 | `GET /api/xcx/coaches/:id` | `mockCoachDetail` 全部字段 | 基本信息、绩效、收入、备注 |
| 2 | `GET /api/xcx/coaches/:id/tasks` | `mockVisibleTasks` + `mockHiddenTasks` + `mockAbandonedTasks` | 任务列表(含备注) |
| 3 | `GET /api/xcx/coaches/:id/top-customers` | `mockTopCustomers` | 客户关系 TOP20 |
| 4 | `GET /api/xcx/coaches/:id/service-records` | `mockServiceRecords` | 近期服务明细 |
| 5 | `GET /api/xcx/coaches/:id/history` | `mockHistoryMonths` | 历史月度汇总 |
| 6 | `POST /api/xcx/notes` | `onNoteConfirm()` 内联构造 | 新增备注 |
### 优先级 P1 — 硬编码需接口化
| # | 硬编码项 | 当前值 | 建议 |
|---|---------|--------|------|
| 7 | `tierNodes` | `[0,100,130,160,190,220]` | 由接口 #1 返回绩效档位配置 |
| 8 | `taskStats` | `{recall:24, callback:14}` | 由接口 #2 返回任务统计汇总 |
| 9 | perfCards[0].sub `'折算前 89.0h'` | 硬编码 | 由接口 #1 返回折算前时长 |
| 10 | perfCards[3].sub `'覆盖 22 位客户'` | 硬编码 | 由接口 #1 返回覆盖客户数 |
| 11 | WXML `近60天` | 硬编码 | 由接口 #3 返回统计时间范围 |
### 优先级 P2 — 清理项
| # | 项目 | 说明 |
|---|------|------|
| 12 | 删除页面内联 mock 常量 | 7 个 mock 变量 + 接口类型定义迁移到 types |
| 13 | 移除 `import { mockCoaches }` | 联调后不再需要 mock-data.ts 依赖 |
| 14 | 移除 `setTimeout(500ms)` 模拟延迟 | 替换为真实异步请求 |
| 15 | `t-tag` 组件注册但未使用 | 从 JSON 中移除或确认是否需要 |