UI 列维度一致性 — 同一行各列必须看同一对象¶
核心:一行表格 / 卡片里所有列的数据必须来自同一维度的同一对象。 混搭「row 字段」+「URL 聚合」+「全局统计」必然产生视觉矛盾。
反模式(红信号)¶
data-view.tsx 官网列表 ISSUE-0075 现场:
{
field: 'scrape_status',
renderCell: ({ row }) => row.scrape_status === 0 ? '待采集' : '已完成',
// ✓ 来自 row 字段
},
{
field: 'http',
renderCell: ({ row }) => {
const log = websiteLogMap.get(row.website); // ❌ 来自 URL 维度聚合
return log?.error?.startsWith('mstage:success') ? '✓已采' : '-';
},
},
用户看到的:「待采集 + ✓已采」— 这一行的 row.scrape_status=0,但同 URL 的另一行已经采过、写了 page-log。
症状特征:列与列的「事实」彼此矛盾,但单独看每个列又都对。
正模式(绿信号)¶
方案 1:列只看 row 字段,不查"全局"¶
{ field: 'scrape_status', renderCell: ({row}) => mapStatus(row.scrape_status) },
{ field: 'http_status', renderCell: ({row}) => row.http_status }, // ← 也存 row 上
把"全局"信息预先写到 row 字段里(比如 batchDedupe 阶段把 page-log 摘要写回 MapTaskData)。
方案 2:列名 / tooltip 明确"维度跃迁"¶
不能避免跨维度时(性能 / 设计原因),用 UI 文案明示:
{
field: 'http',
headerName: '该网址采集情况', // ← 列名暗示这是 URL 维度
renderCell: ({row}) => {
const log = websiteLogMap.get(row.website);
return <Tooltip title="同 URL 任意一次抓取记录(不一定是本行)">...</Tooltip>;
},
}
方案 3:分组聚合时优先选最强代表¶
如果是去重视图(按 URL group N 行 → 显示 1 行),按 score 选 row 字段最"充实"的那行作代表:
const score = r => (emails?.length>0 ? 2 : 0) + (scrape_status===2 ? 1 : 0);
// 同 URL 多行取 score 最高的,自然避开 status=0 当代表
详见 group-by-best-representative。
决策表:你要加 UI 列时¶
| 数据源 | row 字段 | URL 维度聚合 | 全局 KPI |
|---|---|---|---|
| 这一行的属性 | ✅ 直接用 | ❌ 不要混 | ❌ 不要混 |
| URL 的属性(含其他 row) | ⚠️ 加 tooltip 说明 | ✅ 但加 headerName 暗示 | ❌ |
| 全局聚合 | ❌ 不该出现在表格列 | ❌ | ✅ 顶部 KPI bar |
检查清单(写 / 改 UI 列时必问)¶
[ ] 这个列 renderCell 的所有数据源都是 row 自身字段吗?
[ ] 如果用了 row 之外的 Map / 聚合 / Service —— 是不是「URL 维度」「全局维度」?
[ ] 跟同一行其他列对比:会不会出现"列 A 说 X、列 B 说反 X"的视觉矛盾?
[ ] 如果会 —— 用方案 1(写回 row 字段)还是方案 2(列名 + tooltip 明示)?
[ ] 如果是去重视图:分组取代表是否按 score 选(见 group-by-best-representative)?
当前合规处(已审)¶
| 文件 | 实践 | 备注 |
|---|---|---|
data-view.tsx websiteColumns |
方案 3(score 选代表)+ batchDedupeByUrl 写回 row | v0.10.114 修 |
local-table.tsx 商家列表 |
方案 1(cell-renderers 都用 row 字段) | ✅ |
教训¶
- 加新 UI 列时,第一时间问"数据来自哪个维度" — 维度混搭看似"信息更丰富",实际是 bug 温床
- 维度跃迁要在 UI 上明示 — 列名 / tooltip / icon 都行,但不能让用户"理论上能推理出"才不矛盾
- ISSUE-0075 之前没人察觉 — 因为同 URL 多行的场景只在用户量上去后才暴露