27-27-undefined.md 9.2 KB

27: 前端执行票 —— 监测点台账分母渲染 undefined

Type: task Status: claimed Label: ready-for-agent Category: frontend-execution

来源

票 18 真实浏览器取证(2026-10-10 15:57,DATA_SOURCE.REAL 运行监测模式)。

现象

监测点台账面板在真实空态下渲染:

已列出 0 个点位 · 本端点序列共 undefined 条(含未关联设备)

分母取到了 undefined 并被直接渲染到界面。界面出现字面量 undefined,违反「缺失不冒充值」的既有纪律(domain/fields.js 与 sectionState.js 都要求缺失回占位符而非直接呈现)。

注意本条与「面板无数据」是两回事:面板处于真实空态是正确的(13 个地图对象坐标全部 UNVERIFIED、按契约不得上图),但把 undefined 显示给用户是缺陷。

范围

  1. 定位台账分母的取值链:确认为何在真实响应形状下得到 undefined(响应缺该字段,还是前端取错路径)。
  2. 缺失一律回占位符或如实 0,不得渲染 undefined/null/NaN 等字面量。
  3. 同类面板一并自查(其余读数位是否也存在缺失直出风险)。

验收

浏览器实测:监测点台账在真实空态下显示可读文案(不含 undefined);有数据时计数正确;契约测试覆盖缺失字段场景。

关联

发现于票 18;本票是从 票 22 复核中分离出的真实前端缺陷(票 22 本身已撤销)。

进度

  • 2026-10-13 诊断完成(本票无 needs-triage,按阶段闸门仍先做诊断):根因定位并离线复现。
  • 2026-10-13 实施 + 契约测试 + 同类自查。

诊断:根因(已复现,非推测)

取值链:fetchMonitoringDistribution() → toMonitoringDistribution() → monitoringPointLedgerPanel()。

  1. 真实空态下 section.data 是 null。services/monitoringApi.js 的 isEmpty 判据是 「点位 0 且未关联设备 0 且 pagination.total === 0」——真实空态三者都成立,于是 sectionFromResult() 走 isEmpty 分支回 section(EMPTY, { mode, freshness }), 不带 data(domain/sectionState.js 第 141–143 行)。store 照搬 (stores/monitoringStore.js 第 184 行 { ...distribution, loaded: true }),外壳再传 section.data(CockpitShell.vue 第 593 行)——台账视图拿到的就是 null。
  2. 视图层的守卫写错了类型。domain/monitoringPanels.js 第 750–753 行:

    pageNote: pagination.total === null ? '' : `… 本端点序列共 ${pagination.total} 条 …`
    

    data 为 null 时 current = {},pagination = {},所以 pagination.total 是 undefined(不是 null)——守卫 === null 不成立,模板串把 undefined 直出。 作者意图本就是「没有 total 就不出这句」,缺陷只是守卫漏了 undefined 这一态。

  3. 渲染无条件:components/PanelBlocks.vue 第 44 行 <p v-if="view.pageNote"> 在 hasContent 分支之前,所以空态/失败态也照画——这正是用户在空态看到那句话的原因。

复现(离线,2026-10-13):

A EMPTY  pageNote = "已列出 0 个点位 · 本端点序列共 undefined 条(含未关联设备)"
C OK     pageNote = "已列出 1 个点位 · 本端点序列共 8 条(含未关联设备) · 还有下一页"

同类自查(范围第 3 条):对 domain/panelViews.js 注册的全部 16 块面板,喂 null / {} / 缺 pagination / total: null 四种载荷 × EMPTY/SUCCESS/STALE/FAILED 四态,递归扫描视图对象里每个字符串是否含 undefined/NaN/null 字面量—— 唯一命中就是本票的台账 pageNote,其余读数位(在线率、处置率、valueText 系列、 coordinate.js 汇总)都走了占位符或 numberish 收口。守卫已固化为回归脚本 verify/t27-ledger-undefined.mjs。

实施

源码一处(src/modules/cockpit/domain/monitoringPanels.js,+8/−2):把台账分母先过 numberish() 收口,再判 total === null。

const total = numberish(pagination.total)
…
pageNote: total === null ? '' : `已列出 ${cards.length} 个点位 · 本端点序列共 ${total} 条…`

为什么用 numberish 而不是补一个 pagination.total === undefined 判断:仓库已有 「数字/数字串 → 数字,认不出回 null」的唯一口径(domain/panelBlocks.js 第 112 行), 后端 BigDecimal 在线上是字符串(既有事实),补类型判断会同时漏掉 ''/NaN/字符串等 其余缺失形态,等于把同一条纪律再抄一份。缺失时整句不出是作者原意,不是新增文案。

未改的两处,如实记录理由:

  • components/PanelBlocks.vue 第 44 行的渲染位置(hasContent 之前)不改:空态 不画这句话是由 pageNote 为空保证的,与渲染位置无关;挪动它会改变失败态/过期态的 其它既有行为,超出本票范围。
  • 探针在绕过适配层直接喂裸行时另抓到两处(management 的 key = "UNASSIGNED|undefined"、operationOverview 对 ...item.statusReasons 抛 not iterable),但两处都经真实适配层不可达——toManagementInformation() 已把 industry 归一为 ''(第 202 行),toAbnormalEquipment() 已把 statusReasons 归一为数组(第 311 行)。本票是「真实响应渲染」缺陷票,不为合成形状扩张范围; 已在此记录,供将来若改适配层时回看。

验收自查(对抗式)

验收标准 结果
真实空态显示可读文案(不含 undefined) ✅ 端到端走真实消费链(fetchMonitoringDistribution → sectionFromResult → 视图),pageNote === '';修复前实测为 …共 undefined 条…
有数据时计数正确 ✅ 同一脚本钉住 已列出 1 个点位 · 本端点序列共 8 条(含未关联设备) · 还有下一页,且 hasMore === true
契约测试覆盖缺失字段场景 ✅ 缺 pagination / total: null / data: null / {} 四形态,另加 BigDecimal 字符串分母('13' → 共 13 条,不被误判为缺失)
同类面板一并自查(范围 3) ✅ 16 块面板 × 5 载荷 × 4 态普查,零泄漏
变异测试(守卫有牙) ✅ 把守卫改回 pagination.total === null 后 3 项转红(含端到端与普查),还原后 8/8 全绿

证据:node .scratch/cockpit-real-data-wayfinding/verify/t27-ledger-undefined.mjs → 8/8 passed。

未做的验证(如实声明):按 AGENTS.md「修改代码后不要自动运行构建或调试命令」, 未运行 npm run build/dev,未做真实浏览器走查。因此本票的浏览器实测项 (真实 DATA_SOURCE.REAL 环境下的画面取证)尚未执行——离线端到端已复现同一消费链 并锁定语义,但按票面验收第 1 条的字面要求(浏览器实测),需一次真机确认才能宣称完全达标。

回归:t15-time-district.mjs 11/11 通过。t18-monitoring-panels.mjs 与 t20-overview-panels.mjs/t05-adapter.mjs 存在与本票无关的既存失败,已逐一核实: t18 因 filterOptions.js 在 HEAD 上已无 REGION_MAP_GAP/hasRegionMapping/regionQuery 导出而模块加载即失败(语法级,与本票改动无因果关系);t20 的 6 项与 t05 的 1 项 全部落在 overviewStore.js,把我这处改动还原后失败项一字不差(已对照跑过), 故非本票引入。这三条既存红是别的会话留下的,需要各自的票去收,不并入本票。

Status: claimed Type: task Blocked by: Labels: ready-for-agent, frontend-execution, wayfinder:task

Comments

2026-10-13 —— 实施与离线验收完成,剩一项真机取证

源码修复、回归脚本、同类普查、变异验证全部完成(详见上方「实施」「验收自查」)。 node .scratch/cockpit-real-data-wayfinding/verify/t27-ledger-undefined.mjs → 8/8 passed; 把守卫改回缺陷原样则 3 项转红(守卫有牙)。

未关闭的理由:票面验收第 1 条的字面要求是「浏览器实测:监测点台账在真实空态下 显示可读文案(不含 undefined)」。按 AGENTS.md「在修改本项目代码后,不要自动运行构建 或调试命令」,本会话未启 yarn dev、未做真实浏览器走查,故该项尚未执行。 离线端到端已复现并锁死同一条消费链,但我不把「离线绿」冒充成「浏览器实测通过」。

下一步(二选一):

  1. 授权本会话启动 yarn dev + 浏览器,在 DATA_SOURCE.REAL 运行监测模式下取证 [data-panel-page-note] 不含 undefined,然后关闭本票;
  2. 或按既有惯例由「票 18 前端真实接口联调与阶段收口」的真机窗口一并复跑后关闭。

若选 1,请在回复中明确授权(项目规则要求用户在当前对话中显式要求才可跑构建/调试命令)。

Answer