输入文件:
- 租赁任务考核_2026年{1,2,3}月.xlsx (考核源数据)
- {1,2}月.xlsx (客户盈亏表)
- 车辆里程考核与奖金发放规则(V.1.2).docx
输出文件:
- 里程任务考核_{1,2,3}月核算.xlsx (月度核算结果)
- 里程任务考核_Q1汇总.xlsx (含车辆台账)
- 3月客户盈亏表(待填写).xlsx (模版)
.claude_memory: 项目记忆(规则/偏好/架构/测试车辆)
.claude_plans: 历次计划文件
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
48 lines
1.9 KiB
Markdown
48 lines
1.9 KiB
Markdown
# 修复区域图表"其他"重复问题
|
||
|
||
## Context
|
||
|
||
区域资产分布概览的"按城市"柱状图出现两个"其他"。原因:28 辆运营车辆的 `city` 和 `province` 均为 `null`(GPS 未上报),`resolveCity` 返回"其他",排进 Top 8。然后 Top 8 之外的城市又合并出另一个"其他"。
|
||
|
||
## 方案
|
||
|
||
不改 `resolveCity`(城市为空归"其他"是合理的)。修改 `/region-chart` 端点的 Top N 合并逻辑:**先把名为"其他"的条目从排序列表中移除,取 Top N 后,将剩余部分(包括原始的"其他")一起合并为最终的"其他"**。
|
||
|
||
**文件**: `src/server/routes/vehicles.ts` — `/region-chart` 端点
|
||
|
||
### 修改合并逻辑
|
||
|
||
```typescript
|
||
app.get('/region-chart', async (c) => {
|
||
const vehicles = await getVehicles();
|
||
const operating = vehicles.filter((v) => v.status === 'Operating');
|
||
const groupBy = c.req.query('groupBy') || 'region';
|
||
const top = Number(c.req.query('top')) || 8;
|
||
|
||
const counts = new Map<string, number>();
|
||
for (const v of operating) {
|
||
const key = groupBy === 'city' ? resolveCity(v.city, v.province) : mapMacroRegion(v.province, v.city);
|
||
counts.set(key, (counts.get(key) || 0) + 1);
|
||
}
|
||
|
||
// 分离"其他",对非"其他"排序取 Top N,剩余全部合入"其他"
|
||
const otherCount = counts.get('其他') || 0;
|
||
counts.delete('其他');
|
||
|
||
const sorted = Array.from(counts.entries())
|
||
.map(([name, value]) => ({ name, value }))
|
||
.sort((a, b) => b.value - a.value);
|
||
|
||
const result = sorted.slice(0, top);
|
||
const restTotal = sorted.slice(top).reduce((s, item) => s + item.value, 0) + otherCount;
|
||
if (restTotal > 0) result.push({ name: '其他', value: restTotal });
|
||
return c.json(result);
|
||
});
|
||
```
|
||
|
||
## 验证
|
||
|
||
1. `npx tsc --noEmit` 通过
|
||
2. `curl http://localhost:3000/api/vehicles/region-chart?groupBy=city&top=8` — 只有一个"其他"
|
||
3. 页面柱状图每个名称唯一
|