# performance-records 页面数据来源排查

> 排查日期：2026-03-18
> 页面路径：pages/performance-records/performance-records

## 概览

| 分类 | 数量 | 说明 |
|------|------|------|
| A. Mock 数据 | 1 | 从 `mock-data.ts` 导入但实际未使用于渲染 |
| B. 硬编码数据（TS 内联） | 42 | `loadData()` 中手写的 dateGroups + 统计汇总，占页面数据主体 |
| C. 已对接 API | 0 | 无任何真实 API 调用 |
| D. 前端计算/派生数据 | 12 | `nameToAvatarColor()`、`formatMoney()`、`formatHours()`、`formatCount()`、月份切换逻辑 |
| E. 路由参数 | 0 | `onLoad()` 未读取 options |
| F. WXML 硬编码文案 | 14 | 标签文字、提示语、状态文案 |

---

## 一、Mock 数据（来自 mock-data.ts）

| # | 字段/变量 | 来源 | 说明 |
|---|-----------|------|------|
| 1 | `allRecords` | `mockPerformanceRecords`（mock-data.ts） | `import { mockPerformanceRecords }` 导入，赋值给 `allRecords`，但 **未参与任何渲染**；页面实际展示的 `dateGroups` 是 TS 内联硬编码，与此 mock 数据结构完全不同 |

> **注意**：`mockPerformanceRecords` 的类型 `PerformanceRecord`（含 `id/customerName/amount/date/type/category`）与页面实际渲染的 `RecordItem` 接口（含 `hours/timeRange/courseType/location/income` 等）字段不匹配，说明 mock 数据是早期占位，后续内联硬编码覆盖了实际展示逻辑。

---

## 二、硬编码数据（TS 内联）

### 2.1 Banner 区域（Page data 初始值）

| # | 字段 | 硬编码值 | 位置 |
|---|------|----------|------|
| 1 | `coachName` | `'小燕'` | data 初始化 |
| 2 | `coachLevel` | `'星级'` | data 初始化 |
| 3 | `storeName` | `'球会名称店'` | data 初始化 |

### 2.2 月份切换（Page data 初始值）

| # | 字段 | 硬编码值 | 位置 |
|---|------|----------|------|
| 4 | `currentYear` | `2026` | data 初始化 |
| 5 | `currentMonth` | `2` | data 初始化 |
| 6 | `monthLabel` | `'2026年2月'` | data 初始化 |
| 7 | `canGoPrev` | `true` | data 初始化 |
| 8 | `canGoNext` | `false` | data 初始化 |

### 2.3 统计概览（loadData 中 setData）

| # | 字段 | 硬编码值 | 位置 |
|---|------|----------|------|
| 9 | `totalCount` | `32` | loadData → setData |
| 10 | `totalHours` | `59.0` | loadData → setData |
| 11 | `totalIncome` | `4720` | loadData → setData |
| 12 | `hasMore` | `false` | loadData → setData |

### 2.4 dateGroups 内联数据（loadData 中，12 个日期分组，共 30 条记录）

每条记录包含以下硬编码字段：

| 字段 | 类型 | 示例值 | 说明 |
|------|------|--------|------|
| `id` | string | `'r1'` ~ `'r30'` | 记录唯一标识 |
| `customerName` | string | `'王先生'`、`'李女士'` 等 | 客户姓名 |
| `avatarChar` | string | `'王'`、`'李'` 等 | 头像首字 |
| `timeRange` | string | `'20:00-22:00'` | 服务时间段 |
| `hours` | number | `2.0`、`1.5`、`1.0` | 折算后课时 |
| `hoursRaw` | number? | `2.5`（部分记录有） | 折算前课时 |
| `courseType` | string | `'基础课'`/`'包厢课'`/`'打赏课'` | 课程类型文案 |
| `courseTypeClass` | string | `'tag-basic'`/`'tag-vip'`/`'tag-tip'` | 课程类型样式类 |
| `location` | string | `'3号台'`/`'VIP1号房'`/`'打赏'` | 服务位置 |
| `income` | number | `160`/`190`/`120`/`80` | 预估收入（元） |

日期分组级硬编码字段：

| 字段 | 类型 | 示例值 | 说明 |
|------|------|--------|------|
| `date` | string | `'2月7日'` ~ `'1月27日'` | 日期标签 |
| `totalHours` | number | `6.0`/`3.5`/`4.0` 等 | 当日总课时 |
| `totalIncome` | number | `510`/`280`/`320`/`350`/`470` | 当日总收入 |

**完整日期分组清单**（12 组，30 条记录）：

| # | date | 记录数 | totalHours | totalIncome | 记录 ID |
|---|------|--------|------------|-------------|---------|
| 13 | 2月7日 | 3 | 6.0 | 510 | r1, r2, r3 |
| 14 | 2月6日 | 2 | 3.5 | 280 | r4, r5 |
| 15 | 2月5日 | 2 | 4.0 | 320 | r6, r7 |
| 16 | 2月4日 | 2 | 4.0 | 350 | r8, r9 |
| 17 | 2月3日 | 2 | 3.5 | 280 | r10, r11 |
| 18 | 2月2日 | 2 | 4.0 | 350 | r12, r13 |
| 19 | 2月1日 | 6 | 6.0 | 510 | r14~r19 |
| 20 | 1月31日 | 3 | 5.5 | 470 | r20~r22 |
| 21 | 1月30日 | 2 | 3.5 | 280 | r23, r24 |
| 22 | 1月29日 | 2 | 4.0 | 320 | r25, r26 |
| 23 | 1月28日 | 2 | 4.0 | 350 | r27, r28 |
| 24 | 1月27日 | 2 | 4.0 | 350 | r29, r30 |

---

## 三、已对接 API

**无。** 页面当前 0 个 API 调用。

`loadData()` 使用 `setTimeout(500ms)` 模拟异步，内部注释标注：
```typescript
// TODO: 替换为真实 API，按月份请求
const allRecords = mockPerformanceRecords
```

---

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

| # | 字段 | 计算方式 | 依赖 |
|---|------|----------|------|
| 1 | `rec.avatarColor` | `nameToAvatarColor(name)` — 基于姓名首字 charCode 取模映射到 24 色板 | `utils/avatar-color.ts` |
| 2 | `totalCountLabel` | `formatCount(32, '笔')` → `'32笔'` | `utils/money.ts` |
| 3 | `totalHoursLabel` | `formatHours(59.0)` → `'59h'` | `utils/time.ts` |
| 4 | `totalHoursRawLabel` | `formatHours(63.5)` → `'63.5h'` | `utils/time.ts` |
| 5 | `totalIncomeLabel` | `formatMoney(4720)` → `'¥4,720'` | `utils/money.ts` |
| 6 | `group.totalHoursLabel` | `formatHours(n)` — 每个日期分组的课时格式化 | `utils/time.ts` |
| 7 | `group.totalIncomeLabel` | `formatMoney(n)` — 每个日期分组的收入格式化 | `utils/money.ts` |
| 8 | `fmt.hours(rec.hours)` | WXS `hours()` — WXML 中课时展示 | `utils/format.wxs` |
| 9 | `fmt.hours(rec.hoursRaw)` | WXS `hours()` — WXML 中折前课时展示 | `utils/format.wxs` |
| 10 | `fmt.money(rec.income)` | WXS `money()` — WXML 中收入展示 | `utils/format.wxs` |
| 11 | `monthLabel` | `switchMonth()` 中拼接 `` `${currentYear}年${currentMonth}月` `` | 月份切换逻辑 |
| 12 | `canGoNext` | `switchMonth()` 中与当前日期比较，不能超过当月 | 月份切换逻辑 |

---

## 五、路由参数

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

当前页面不知道：
- 当前登录助教是谁（`coachName`/`coachLevel` 硬编码）
- 所属门店（`storeName` 硬编码）
- 初始展示月份（硬编码 2026 年 2 月）

---

## 六、WXML 硬编码文案

| # | 文案 | 位置 | 说明 |
|---|------|------|------|
| 1 | `加载中...` | toast 加载浮层 | 加载状态提示 |
| 2 | `加载失败，请点击重试` | 错误态 | 错误提示文案 |
| 3 | `重试` | 错误态按钮 | 按钮文案 |
| 4 | `总记录` | 统计概览 stat-label | 统计标签 |
| 5 | `总业绩时长` | 统计概览 stat-label | 统计标签 |
| 6 | `预估`（×2 处） | 统计概览 stat-hint | 时长和收入后的提示 |
| 7 | `折前` | 统计概览 stat-hours-raw | 折前课时前缀 |
| 8 | `收入` | 统计概览 stat-label | 统计标签 |
| 9 | `暂无数据` | 空态 | 空状态提示 |
| 10 | `—` | 日期分隔线 dd-date | 日期后分隔符 |
| 11 | `·` | 日期分隔线 dd-stats | 课时与收入间分隔符 |
| 12 | `预估` | 日期分隔线 dd-stats | 日分组收入前缀 |
| 13 | `我的预估收入` | record-income | 记录行收入前缀 |
| 14 | `— 已加载全部记录 —` | list-end-hint | 列表底部提示 |

---

## 七、联调 TODO

### 7.1 需要对接的 API（按优先级）

| 优先级 | API | 用途 | 替换目标 |
|--------|-----|------|----------|
| P0 | 获取当前助教信息 | Banner 区域展示 | `coachName`、`coachLevel`、`storeName` 硬编码 |
| P0 | 业绩明细列表（按月分页） | 核心数据 | `loadData()` 中全部 `dateGroups` 内联硬编码 |
| P0 | 月度统计汇总 | 统计概览 | `totalCount`/`totalHours`/`totalIncome` 等硬编码 |

### 7.2 需要确认的数据结构

| 问题 | 说明 |
|------|------|
| `PerformanceRecord` vs `RecordItem` 接口不匹配 | mock-data.ts 中 `PerformanceRecord` 含 `amount/date/type/category`，页面实际渲染的 `RecordItem` 含 `hours/timeRange/courseType/location/income`，需与后端确认最终字段 |
| `hoursRaw`（折前课时）是否由后端返回 | 当前仅部分记录有此字段，需确认业务规则 |
| `courseType` 枚举值 | 当前有 `基础课`/`包厢课`/`打赏课` 三种，需确认是否完整 |
| `courseTypeClass` 样式映射 | 是前端根据 `courseType` 派生，还是后端直接返回 |
| 日期分组逻辑 | 由后端返回已分组数据，还是前端按日期聚合 |
| 分页机制 | 当前 `page`/`pageSize`/`hasMore` 已定义但未实际使用 |
| 月份切换 | 切换月份后应重新请求 API，当前 `loadData()` 未传入年月参数 |

### 7.3 需要清理的代码

| 项目 | 说明 |
|------|------|
| 删除 `import { mockPerformanceRecords }` | mock 数据导入 |
| 删除 `import type { PerformanceRecord }` | mock 类型导入 |
| 删除 `loadData()` 中 `setTimeout` 模拟 | 替换为真实 API 调用 |
| 删除 `loadData()` 中 12 个 dateGroups 内联数据 | 约 120 行硬编码 |
| 删除 `allRecords` 字段 | 当前赋值了 mock 但未使用 |
| 确认 `onLoad` 是否需要接收路由参数 | 如助教 ID、初始月份等 |

### 7.4 数据一致性问题

| 问题 | 详情 |
|------|------|
| 2月4日 `totalHours=4.0` 但 `totalIncome=350` | `totalIncomeLabel` 用 `formatMoney(320)` 格式化，但 `totalIncome` 赋值 `350`，数值不一致 |
| 2月2日 同上 | `totalIncomeLabel=formatMoney(320)` 但 `totalIncome=350` |
| 1月28日 同上 | `totalIncomeLabel=formatMoney(320)` 但 `totalIncome=350` |
| 1月27日 同上 | `totalIncomeLabel=formatMoney(320)` 但 `totalIncome=350` |
| 统计汇总与明细不匹配 | `totalCount=32` 但 dateGroups 中实际只有 30 条记录（r1~r30） |
| `totalHours=59.0` 未验证 | 各日期 totalHours 之和 = 6+3.5+4+4+3.5+4+6+5.5+3.5+4+4+4 = 52h ≠ 59h |
| `totalIncome=4720` 未验证 | 各日期 totalIncome 之和 = 510+280+320+350+280+350+510+470+280+320+350+350 = 4370 ≠ 4720 |

### 7.5 组件依赖

| 组件 | 来源 | 说明 |
|------|------|------|
| `coach-level-tag` | 自定义组件 `/components/coach-level-tag/` | 助教等级标签 |
| `ai-float-button` | 自定义组件 `/components/ai-float-button/` | AI 悬浮按钮 |
| `dev-fab` | 自定义组件 `/components/dev-fab/` | 开发调试按钮 |
| `t-icon` | TDesign `tdesign-miniprogram/icon` | 图标 |
| `t-loading` | TDesign `tdesign-miniprogram/loading` | 加载动画 |

### 7.6 静态资源

| 资源 | 路径 | 说明 |
|------|------|------|
| Banner 背景图 | `/assets/images/banner-bg-coral-aurora.svg` | 珊瑚极光渐变背景 |
| 助教头像 | `/assets/images/avatar-coach.png` | 默认助教头像 |
