跳转至

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 多行的场景只在用户量上去后才暴露