# board-coach 页面数据来源排查

> 排查日期：2026-03-18
> 页面路径：pages/board-coach/board-coach
> 排查范围：board-coach.ts / board-coach.wxml / board-coach.wxss / board-coach.json + 引用工具函数

## 概览

| 分类 | 数量 | 说明 |
|------|------|------|
| A. Mock 数据 | 1 组（6 条记录，含 ~20 个字段/条） | 页面内联 `MOCK_COACHES` 数组，未引用 `utils/mock-data.ts` |
| B. 硬编码数据 | 6 组常量 + 3 个默认值 | 排序/技能/时间选项、等级/技能样式映射、默认筛选值 |
| C. 已对接 API | 0 | 全部数据来自 Mock，无任何 `wx.request` / service 调用 |
| D. 前端计算/派生数据 | 10 个派生字段 | `loadData()` 中通过 `formatMoney`/`formatHours`/`formatCount` 生成展示标签 |
| E. 路由参数 | 0 | `onLoad()` 未读取任何 query 参数 |
| F. WXML 硬编码文案 | 14 处 | Tab 标签、状态提示、维度标签等 |

---

## 一、Mock 数据

### 1.1 页面内联 Mock：`MOCK_COACHES`（board-coach.ts L82-L161）

页面顶部定义了 `const MOCK_COACHES: CoachItem[]`，包含 6 位助教的完整数据。`loadData()` 通过 `setTimeout(400ms)` 模拟异步加载后直接使用该数组。

**未引用** `utils/mock-data.ts` 中的 `mockCoaches`（该文件有独立的 `CoachCard` 接口和 3 条记录，字段结构不同）。

| Mock 字段 | 类型 | 示例值 | 联调时应替换为 |
|-----------|------|--------|---------------|
| `id` | string | `'c1'` | API 返回的助教 ID |
| `name` | string | `'小燕'` | 助教姓名 |
| `initial` | string | `'小'` | 前端根据 name 首字计算，或 API 返回头像 URL |
| `avatarGradient` | string | `'blue'` | API 返回头像 URL 后可移除，改用真实头像 |
| `level` | string | `'star'` | 助教等级（API：`star/senior/middle/junior`） |
| `skills` | Array | `[{text:'🎱', cls:'skill--chinese'}]` | API 返回技能列表，前端映射 cls |
| `topCustomers` | string[] | `['💖 王先生','💖 李女士']` | API 返回 top 客户列表（含亲密度标记） |
| `perfHours` | number | `86.2` | 定档业绩课时（API 聚合） |
| `perfHoursBefore` | number? | `92.0` | 折前课时（API 聚合） |
| `perfGap` | string? | `'距升档 13.8h'` | 可由前端计算或 API 直接返回 |
| `perfReached` | boolean | `false` | 是否已达标（API 或前端判断） |
| `salary` | number | `12680` | 预估工资（API 聚合） |
| `salaryPerfHours` | number | `86.2` | 工资维度-定档课时 |
| `salaryPerfBefore` | number? | `92.0` | 工资维度-折前课时 |
| `svAmount` | number | `45200` | 客源储值金额（API 聚合） |
| `svCustomerCount` | number | `18` | 储值客户数 |
| `svConsume` | number | `8600` | 储值消耗金额 |
| `taskRecall` | number | `18` | 召回任务完成数 |
| `taskCallback` | number | `14` | 回访任务完成数 |

**联调替换目标 API**（待后端提供）：

```
GET /api/v1/board/coach/list
Query: sort={selectedSort}&skill={selectedSkill}&time={selectedTime}
Response: { coaches: CoachItem[] }
```

---

## 二、硬编码数据

### 2.1 筛选选项常量（board-coach.ts L14-L50）

| 常量名 | 字段数 | 风险 | 说明 |
|--------|--------|------|------|
| `SORT_OPTIONS` | 6 项 | 🟡 中 | 排序维度选项，value/text 写死。若后端新增排序维度需同步修改 |
| `SKILL_OPTIONS` | 5 项 | 🟡 中 | 技能筛选选项，含 emoji。若门店技能类型可配置则需改为 API 获取 |
| `TIME_OPTIONS` | 6 项 | 🟢 低 | 时间范围选项，纯前端逻辑，变动概率低 |
| `SORT_TO_DIM` | 6 项映射 | 🟢 低 | 排序值→维度类型映射，纯前端 UI 逻辑 |

### 2.2 样式映射常量（board-coach.ts L53-L68）

| 常量名 | 风险 | 说明 |
|--------|------|------|
| `LEVEL_CLASS` | 🟢 低 | 等级→CSS 类映射，含中英文 key 兼容。纯前端样式逻辑 |
| `SKILL_CLASS` | 🟢 低 | 技能 emoji→CSS 类映射。纯前端样式逻辑 |

### 2.3 data 对象默认值（board-coach.ts L165-L180）

| 字段 | 硬编码值 | 风险 | 说明 |
|------|----------|------|------|
| `selectedSort` | `'perf_desc'` | 🟢 低 | 默认排序，合理默认值 |
| `selectedSkill` | `'all'` | 🟢 低 | 默认不限技能 |
| `selectedTime` | `'month'` | 🟢 低 | 默认本月 |
| `dimType` | `'perf'` | 🟢 低 | 默认维度类型，与 selectedSort 联动 |
| `filterBarVisible` | `true` | 🟢 低 | 筛选栏初始可见 |
| `pageState` | `'loading'` | 🟢 低 | 页面初始状态 |
| `activeTab` | `'coach'` | 🟢 低 | 当前 Tab 标识 |

### 2.4 `loadData()` 中的模拟延迟（board-coach.ts L195）

```typescript
setTimeout(() => { ... }, 400)  // 🔴 高风险：联调时必须替换为真实 API 调用
```

### 2.5 `onPullDownRefresh` 中的延迟（board-coach.ts L192）

```typescript
setTimeout(() => wx.stopPullDownRefresh(), 500)  // 🟡 中风险：联调后应在 API 回调中停止
```

---

## 三、已对接 API

**无。** 当前页面 0 个 API 调用，全部数据来自页面内联 Mock。

页面中存在的导航跳转（非数据 API）：
- `wx.switchTab({ url: '/pages/board-finance/board-finance' })` — Tab 切换
- `wx.navigateTo({ url: '/pages/board-customer/board-customer' })` — Tab 切换
- `wx.navigateTo({ url: '/pages/coach-detail/coach-detail?id=' + id })` — 助教详情跳转

---

## 四、前端计算/派生数据

### 4.1 `loadData()` 中的格式化派生字段（board-coach.ts L197-L208）

| 派生字段 | 源字段 | 格式化函数 | 输出示例 |
|----------|--------|-----------|----------|
| `perfHoursLabel` | `perfHours` | `formatHours()` | `'86.2h'` |
| `perfHoursBeforeLabel` | `perfHoursBefore` | `formatHours()` | `'92h'` |
| `salaryLabel` | `salary` | `formatMoney()` | `'¥12,680'` |
| `salaryPerfHoursLabel` | `salaryPerfHours` | `formatHours()` | `'86.2h'` |
| `salaryPerfBeforeLabel` | `salaryPerfBefore` | `formatHours()` | `'92h'` |
| `svAmountLabel` | `svAmount` | `formatMoney()` | `'¥45,200'` |
| `svCustomerCountLabel` | `svCustomerCount` | `formatCount(n,'人')` | `'18人'` |
| `svConsumeLabel` | `svConsume` | `formatMoney()` | `'¥8,600'` |
| `taskRecallLabel` | `taskRecall` | `formatCount(n,'次')` | `'18次'` |
| `taskCallbackLabel` | `taskCallback` | `formatCount(n,'次')` | `'14次'` |

### 4.2 维度类型切换（board-coach.ts L218-L222）

`onSortChange` 根据 `SORT_TO_DIM` 映射计算 `dimType`，控制 WXML 中四种卡片模板的条件渲染。

### 4.3 滚动隐藏/显示筛选栏（board-coach.ts L196-L215 `onPageScroll`）

纯前端交互逻辑：累积滚动距离超过阈值（上滑 12px / 下滑 24px）时切换 `filterBarVisible`。

---

## 五、路由参数

**无。** `onLoad()` 未接收任何 `options` 参数。

---

## 六、WXML 硬编码文案

| 位置 | 硬编码文案 | 风险 | 建议 |
|------|-----------|------|------|
| 加载态 | `加载中...` | 🟢 低 | 通用文案，可保留 |
| 空状态 | `暂无助教数据` | 🟢 低 | 可保留 |
| 错误态 | `加载失败` | 🟢 低 | 可保留 |
| 重试按钮 | `点击重试` | 🟢 低 | 可保留 |
| Tab-财务 | `财务` | 🟢 低 | 固定 Tab 文案 |
| Tab-客户 | `客户` | 🟢 低 | 固定 Tab 文案 |
| Tab-助教 | `助教` | 🟢 低 | 固定 Tab 文案 |
| 定档维度标签 | `定档` / `折前` | 🟢 低 | 业务术语，可保留 |
| 工资维度标签 | `预估` | 🟢 低 | 业务术语 |
| 储值维度标签 | `储值` | 🟢 低 | 业务术语 |
| 召回维度标签 | `召回` | 🟢 低 | 业务术语 |
| 达标标记 | `✅ 已达标` | 🟢 低 | 可保留 |
| 工资底部 | `定档` / `折前` | 🟢 低 | 同上 |
| 储值底部 | `客户` / `消耗` / `\|` 分隔符 | 🟢 低 | 业务术语 |
| 任务底部 | `回访` | 🟢 低 | 业务术语 |
| 导航栏标题 | `助教看板`（board-coach.json） | 🟢 低 | 固定页面标题 |

---

## 七、引用组件清单

| 组件 | 来源 | 数据依赖 |
|------|------|----------|
| `coach-level-tag` | 自定义组件 | 接收 `level` 属性 |
| `filter-dropdown` | 自定义组件 | 接收 `label`/`options`/`value`，触发 `change` 事件 |
| `ai-float-button` | 自定义组件 | 无数据依赖 |
| `board-tab-bar` | 自定义 Tab Bar | 接收 `active="board"` |
| `t-loading` / `t-empty` | TDesign 组件 | 无业务数据依赖 |
| `dev-fab` | 开发调试组件 | `wx:if="{{false}}"` 已禁用 |

---

## 八、联调 TODO

### 🔴 P0 — 必须完成

- [ ] **替换 `MOCK_COACHES` 为 API 调用**：移除页面内联 Mock 数组，`loadData()` 改为调用后端接口
  - 需后端提供：助教列表接口（含排序/技能/时间筛选参数）
  - 接口需返回：四种维度的全部字段（perf/salary/sv/task）
  - 错误处理：API 失败时 `setData({ pageState: 'error' })`
- [ ] **移除 `setTimeout(400ms)` 模拟延迟**：替换为真实异步请求
- [ ] **`onPullDownRefresh` 改为 API 回调中停止**：`wx.stopPullDownRefresh()` 应在请求完成后调用

### 🟡 P1 — 建议完成

- [ ] **筛选联动 API**：当前 `onSortChange`/`onSkillChange`/`onTimeChange` 仅更新本地状态，未触发重新请求。联调时需在筛选变更后重新调用 API
- [ ] **头像方案确认**：当前使用 `initial`（首字）+ `avatarGradient`（渐变色）模拟头像。确认是否改为真实头像 URL
- [ ] **`perfGap` 字段来源确认**：当前为 Mock 字符串（如 `'距升档 13.8h'`），确认由后端计算还是前端根据阈值计算
- [ ] **`topCustomers` 格式确认**：当前含 emoji 亲密度标记（💖/💛），确认 API 返回格式

### 🟢 P2 — 可后续优化

- [ ] **`SKILL_OPTIONS` 是否需要动态获取**：若门店技能类型可配置，应改为 API 获取
- [ ] **`SORT_OPTIONS` / `TIME_OPTIONS` 是否需要后端控制**：当前写死，若需动态调整需改为配置化
- [ ] **清理 `utils/mock-data.ts` 中的 `CoachCard` 接口**：与页面内联 `CoachItem` 接口不一致，联调完成后统一或移除
- [ ] **`levelClass` 字段**：当前在 Mock 中预计算，联调后应由前端根据 `level` 字段通过 `LEVEL_CLASS` 映射生成
