279 lines
13 KiB
Markdown
279 lines
13 KiB
Markdown
# 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 字符码计算,非真实数据 |
|