Files
Neo-ZQYY/docs/miniprogram-dev/api-audit/task-list.md

279 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# task-list 页面数据来源排查
> 排查日期2026-03-18
> 页面路径pages/task-list/task-list
> 引用文件:`utils/mock-data.ts`、`utils/money.ts`、`utils/time.ts`、`utils/request.ts`(未使用)
## 概览
| 分类 | 数量 | 说明 |
|------|------|------|
| Mock 数据 | 28 个字段 | 全部任务列表 + 业绩进度卡片均来自 mock |
| 硬编码数据 | 12 个字段 | 用户信息、头像、动画参数、AI 建议文案等 |
| 已对接 API | 0 个接口 | 页面当前无任何真实 API 调用 |
| 前端计算/派生 | 14 个字段 | enrichTask 派生字段 + 进度条动画计算 |
| 路由参数 | 0 个 | onLoad 未读取 options |
| WXML 硬编码文案 | 5 处 | 标题、分组标签等 UI 文案 |
---
## 一、Mock 数据
### 1.1 来自 mock-data.ts
#### 1.1.1 `mockTasks` — 任务列表主数据
| 字段 | 类型 | 说明 | 联调替换 API |
|------|------|------|-------------|
| `id` | `string` | 任务 ID | `GET /api/xcx/tasks` |
| `customerName` | `string` | 客户姓名 | 同上 |
| `customerAvatar` | `string` | 客户头像 URL | 同上 |
| `taskType` | `TaskType` | 任务类型枚举:`callback` / `priority_recall` / `relationship` / `high_priority` | 同上 |
| `taskTypeLabel` | `string` | 任务类型中文标签 | 同上(或前端根据 taskType 映射) |
| `deadline` | `string` | 截止日期 ISO 字符串 | 同上 |
| `heartScore` | `number` | 爱心评分 0-10 | 同上 |
| `hobbies` | `string[]` | 客户爱好标签 | 同上 |
| `isPinned` | `boolean` | 是否置顶 | 同上 |
| `hasNote` | `boolean` | 是否有备注 | 同上 |
| `status` | `'pending' \| 'completed' \| 'abandoned'` | 任务状态 | 同上 |
- 导入方式:`import { mockTasks } from '../../utils/mock-data'`task-list.ts L2
- `loadData()` 中直接展开 `mockTasks` 并追加了一条内联 mock 任务 `task-007`(见 1.2 节)
#### 1.1.2 `mockPerformance` — 绩效概览数据
| 字段 | 类型 | 当前 Mock 值 | 联调替换 API |
|------|------|-------------|-------------|
| `monthlyIncome` | `number` | `12680` | `GET /api/xcx/performance/summary` |
| `incomeChange` | `number` | `15.3` | 同上 |
| `currentTier` | `string` | `'银牌助教'` | 同上 |
| `nextTierGap` | `number` | `3320` | 同上 |
| `todayServiceCount` | `number` | `4` | 同上 |
| `weekServiceCount` | `number` | `18` | 同上 |
| `monthServiceCount` | `number` | `67` | 同上 |
- 导入方式:`import { mockPerformance } from '../../utils/mock-data'`task-list.ts L2
- 当前仅用 `mockPerformance.currentTier` 赋值给 `bannerTitle`L233
### 1.2 页面内联 Mock
#### 1.2.1 `task-007` 内联任务loadData 内,约 L213-L225
```typescript
{
id: 'task-007',
customerName: '孙丽',
customerAvatar: '/assets/images/avatar-default.png',
taskType: 'callback',
taskTypeLabel: '客户回访',
deadline: '2026-03-06',
heartScore: 3.5,
hobbies: [],
isPinned: false,
hasNote: false,
status: 'abandoned',
}
```
- 联调时需删除,改为 API 返回的完整任务列表
#### 1.2.2 `buildPerfData()` — 业绩进度卡片构造函数(约 L130-L160
| 字段 | Mock 值 | 说明 | 联调替换 API |
|------|---------|------|-------------|
| `nextTierHours` | `100` | 下一档位小时数 | `GET /api/xcx/performance/progress` |
| `remainHours` | `12.5` | 距下一档剩余小时 | 同上 |
| `currentTier` | `1` | 当前档位序号 | 同上 |
| `tierProgress` | `58` | 当前段内进度% | 同上(或前端计算) |
| `filledPct` | `39.8`87.5/220×100 | 总进度条百分比 | 同上(或前端计算) |
| `ticks` | `buildTicks([0,100,130,160,190,220], 220)` | 刻度数组 | 同上(档位节点由接口返回) |
| `basicHours` | `'77.5'` | 基础课时 | 同上 |
| `bonusHours` | `'12'` | 激励课时 | 同上 |
| `totalHours` | `'87.5'` | 总课时 | 同上 |
| `tierCompleted` | `true` | 是否达标 | 同上 |
| `bonusMoney` | `'800'` | 达标奖金(元) | 同上 |
| `incomeMonth` | `'2月'` | 收入月份 | 同上 |
| `prevMonth` | `'1月'` | 对比月份 | 同上 |
| `incomeFormatted` | `'6,206'` | 预计收入格式化 | 同上 |
| `incomeTrend` | `'↓368'` | 收入趋势文案 | 同上 |
| `incomeTrendDir` | `'down'` | 趋势方向 | 同上 |
#### 1.2.3 `enrichTask()` 内联 Mock 逻辑(约 L100-L125
| 派生字段 | Mock 算法 | 说明 |
|----------|-----------|------|
| `lastVisitDays` | `(id.charCodeAt(last) % 15) + 1` | 伪随机天数,联调时应由 API 返回 |
| `balanceLabel` | `formatMoney((id.charCodeAt(last) * 137) % 5000 + 200)` | 伪随机余额,联调时应由 API 返回 |
| `aiSuggestion` | 从 5 条硬编码文案中按 id 取模选取 | 联调时应由 AI 接口返回 |
5 条硬编码 AI 建议文案:
1. `'建议推荐斯诺克进阶课程,提升客户粘性'`
2. `'客户近期消费下降,建议电话关怀了解原因'`
3. `'适合推荐周末球友赛活动,增强社交体验'`
4. `'高价值客户建议维护关系并推荐VIP权益'`
5. `'新客户首次体验后未续费,建议跟进意向'`
#### 1.2.4 `loadData()` 中的 600ms 模拟延迟
```typescript
setTimeout(() => { /* 全部数据构造逻辑 */ }, 600)
```
- 联调时替换为真实 API 请求
---
## 二、硬编码数据
| 字段 | 当前值 | 应改为 | 风险 | 所在位置 |
|------|--------|--------|------|----------|
| `userName` | `'小燕'` | `globalData.userInfo.name` 或登录 API | 高 | data 初始化 |
| `userRole` | `'助教'` | `globalData.userInfo.role` 或登录 API | 高 | data 初始化 |
| `storeName` | `'广州朗朗桌球'` | `globalData.storeInfo.name` 或登录 API | 高 | data 初始化 |
| `avatarUrl` | `'/assets/images/avatar-coach.png'` | `globalData.userInfo.avatar` 或登录 API | 中 | data 初始化 |
| `DETAIL_ROUTE` | `'/pages/task-detail/task-detail'` | 保持硬编码(路由常量) | 低 | 常量定义 |
| `tierNodes` | `[0, 100, 130, 160, 190, 220]` | 业绩进度 API 返回 | 高 | `buildPerfData()` + `_applyDebugHours()` |
| `maxHours` | `220` | 业绩进度 API 返回 | 高 | `buildPerfData()` |
| `total` | `87.5` | 业绩进度 API 返回 | 高 | `buildPerfData()` |
| `aiColor` | 随机选取 `['red','orange','yellow','blue','indigo','purple']` | 可保持前端随机(纯 UI | 低 | `onLoad()` / `onShow()` |
| `abandonReason` | `'客户已转至其他门店'` | API 返回(放弃原因由用户输入) | 中 | `enrichTask()` |
| `suggestions[]` | 5 条固定文案 | AI 建议 API 返回 | 高 | `enrichTask()` |
| `hasMore` | `true` → 触底后 `false` | 分页 API 返回 `has_next` | 中 | `loadData()` / `onReachBottom()` |
---
## 三、已对接 API
**当前页面无任何真实 API 调用。**
- `utils/request.ts` 已导出 `request()` 函数(支持 token 自动附加、401 刷新重试)
- task-list.ts 未 import `request`,所有数据均来自 mock
---
## 四、前端计算/派生数据
### 4.1 任务分组loadData 内)
| 派生字段 | 计算逻辑 | 依赖源数据 |
|----------|----------|-----------|
| `pinnedTasks` | `enriched.filter(t => t.isPinned && !t.isAbandoned)` | `allTasks` |
| `normalTasks` | `enriched.filter(t => !t.isPinned && !t.isAbandoned && t.status === 'pending')` | `allTasks` |
| `abandonedTasks` | `enriched.filter(t => t.isAbandoned)` | `allTasks` |
| `taskCount` | `pinnedTasks.length + normalTasks.length + abandonedTasks.length` | 三组任务 |
| `pageState` | `totalCount > 0 ? 'normal' : 'empty'` | `taskCount` |
### 4.2 enrichTask 派生字段
| 派生字段 | 计算逻辑 | 依赖 |
|----------|----------|------|
| `isAbandoned` | `task.status === 'abandoned'` | `task.status` |
| `deadlineLabel` | `formatDeadline(task.deadline).text` | `task.deadline` + `utils/time.ts` |
| `deadlineStyle` | `formatDeadline(task.deadline).style` | `task.deadline` + `utils/time.ts` |
| `balanceLabel` | `formatMoney(balanceSeedNum)` | Mock 种子值 + `utils/money.ts`(联调后改为 API 余额) |
### 4.3 进度条动画计算
| 派生字段 | 计算逻辑 | 依赖 |
|----------|----------|------|
| `filledPct` | `Math.min(100, (total / 220) * 100)` | `total``maxHours` |
| `clampedSparkPct` | `Math.max(0, Math.min(100, filledPct))` | `filledPct` |
| `shineDurMs` | `calcShineDur(filledPct)` — 基于 `SHINE_SPEED` 和进度百分比 | `filledPct``SHINE_SPEED` |
| `ticks[]` | `buildTicks(tierNodes, maxHours)` — 计算每个刻度的 `left` 百分比位置 | `tierNodes``maxHours` |
### 4.4 调试面板计算(`_applyDebugHours`
| 派生字段 | 说明 |
|----------|------|
| `currentTier` | 遍历 tiers 数组确定当前档位 |
| `tierProgress` | 当前段内进度百分比 |
| `remainHours` | `nextTierHours - total` |
| `tierCompleted` | `total >= 220` |
> 调试面板仅开发阶段使用,联调时可保留或移除。
---
## 五、路由参数
`onLoad()` 未读取任何 `options` 参数。页面作为 TabBar 页面,不接收路由参数。
---
## 六、WXML 中的硬编码文案
| 文案 | 位置 | 类型 | 说明 |
|------|------|------|------|
| `'今日 客户维护'` | section-header | UI 文案 | 可保持硬编码,或由 API 返回标题 |
| `'📌 置顶'` | group-label--pinned | UI 文案 | 保持硬编码 |
| `'正常任务'` | group-label--normal | UI 文案 | 保持硬编码 |
| `'已放弃'` | group-label--abandoned | UI 文案 | 保持硬编码 |
| `'暂无待办任务'` | state-empty | UI 文案 | 保持硬编码 |
| `'加载失败,请重试'` | state-error | UI 文案 | 保持硬编码 |
| `'没有更多了'` | load-more | UI 文案 | 保持硬编码 |
| `'最近到店:{{...}}天前 · 余额:{{...}}'` | card-row-2 | 模板文案 | 保持硬编码(数据部分由变量填充) |
| `'放弃原因:{{...}}'` | card-row-abandon | 模板文案 | 保持硬编码 |
| `'基础课 \| 激励课 \| 全部'` | hours-label | UI 文案 | 保持硬编码 |
> 以上均为 UI 展示文案,非业务数据硬编码,联调时无需替换。
---
## 七、联调 TODO
### 高优先P0 — 页面核心功能)
- [ ] 替换 `mockTasks` + 内联 `task-007``GET /api/xcx/tasks` API 调用
- [ ] 替换 `buildPerfData()``GET /api/xcx/performance/progress` API 调用
- [ ]`globalData` 或登录 API 获取 `userName``userRole``storeName``avatarUrl`
- [ ] 替换 `enrichTask()` 中的 mock 派生字段(`lastVisitDays``balanceLabel`)为 API 返回字段
- [ ] 替换 `enrichTask()` 中的 `aiSuggestion` 硬编码文案为 AI 建议 API
- [ ] 替换 `mockPerformance` 引用为真实绩效 API
- [ ] 档位节点 `tierNodes``maxHours` 改为 API 返回
### 中优先P1 — 交互完整性)
- [ ] 实现分页加载:`onReachBottom` 对接分页 API`page` / `cursor` 参数)
- [ ] `onCtxPin` 置顶操作对接 `POST /api/xcx/tasks/{id}/pin` API
- [ ] `onCtxAbandon` / `onAbandonConfirm` 放弃操作对接 `POST /api/xcx/tasks/{id}/abandon` API
- [ ] `onCtxCancelAbandon` 取消放弃对接 `POST /api/xcx/tasks/{id}/cancel-abandon` API
- [ ] `onNoteConfirm` 备注保存对接 `POST /api/xcx/tasks/{id}/notes` API
- [ ] `loadData` 中移除 `setTimeout 600ms` 模拟延迟
### 低优先P2 — 可后续处理)
- [ ] 移除 `enrichTask()` 函数(字段由 API 直接返回)
- [ ] 移除 `buildPerfData()` 函数(数据由 API 直接返回)
- [ ] 移除 `buildTicks()` 函数(或保留为前端工具函数,接收 API 返回的 tierNodes
- [ ] 评估是否保留调试面板(`showDebugPanel` 相关逻辑)
- [ ] 清理 `mock-data.ts` 中 task-list 不再使用的导出
---
## 八、依赖组件清单
| 组件 | 路径 | 数据来源 |
|------|------|----------|
| `perf-progress-bar` | `/components/perf-progress-bar/` | 接收 `perfData.*` 属性(全部 mock |
| `heart-icon` | `/components/heart-icon/` | 接收 `item.heartScore`mock |
| `ai-inline-icon` | `/components/ai-inline-icon/` | 接收 `aiColor`(前端随机) |
| `ai-float-button` | `/components/ai-float-button/` | 无数据依赖 |
| `note-modal` | `/components/note-modal/` | 接收 `noteTarget.customerName`mock |
| `abandon-modal` | `/components/abandon-modal/` | 接收 `abandonTarget.customerName`mock |
| `dev-fab` | `/components/dev-fab/` | 无数据依赖(开发工具) |
| `t-icon` | TDesign 组件 | 无业务数据依赖 |
---
## 九、风险总结
| 风险项 | 等级 | 说明 |
|--------|------|------|
| 全页面零 API 调用 | 🔴 高 | 联调时需一次性替换所有数据源,工作量集中 |
| 用户信息硬编码 | 🔴 高 | `userName`/`userRole`/`storeName` 写死,多用户场景必崩 |
| AI 建议文案固定 | 🔴 高 | 5 条文案轮转,联调时需对接 AI 服务 |
| 业绩进度全量 mock | 🔴 高 | 档位、课时、收入全部硬编码,数值与真实数据无关 |
| 分页未实现 | 🟡 中 | `onReachBottom` 仅显示"没有更多了",无真实分页 |
| 操作无持久化 | 🟡 中 | 置顶/放弃/备注仅修改本地 data刷新即丢失 |
| `enrichTask` 伪随机 | 🟡 中 | `lastVisitDays`/`balanceLabel` 由 id 字符码计算,非真实数据 |