|
|
@@ -52,6 +52,10 @@ import {
|
|
|
} from './panelBlocks.js'
|
|
|
// 票 67:运行信息三项能力的唯一口径来源(category 合计、行业范围与降级说明)。
|
|
|
import { OPERATING_SOURCE_NOTE, operatingCategoryCards, operatingScope } from './operatingMetricCaliber.js'
|
|
|
+// 票 100:台账行的派生字段惰性物化(6771 条指标 / 每页 10 条,没必要全算)。
|
|
|
+import { lazyFields } from './lazyFields.js'
|
|
|
+// 票 100:`missing` 只按**领域对象的自有形状**收,不做全树深扫(见 `panelMissingFromSections`)。
|
|
|
+import { collectDeclaredMissing, panelMissingFromSections } from './panelBlocks.js'
|
|
|
|
|
|
// 票 18:这些工具已搬到 `panelBlocks.js`,这里**原样转出**,票 17 的既有导入点不受影响。
|
|
|
export {
|
|
|
@@ -206,6 +210,16 @@ export function toOperatingInformation(raw) {
|
|
|
// 实测后端对 MANHOLE 的 label 直接回英文码,所以先过前端行业词表;
|
|
|
// 认不出的码才用后端 label,最后才回码 —— 接口词汇不上大屏(票 05/11 纪律)。
|
|
|
const label = industryLabel(code) || industry.optionalStr('label') || code || ''
|
|
|
+ // 票 67:指标带上所属行业 —— 全部行业时清单逐行说明身份,卡片也能按行业解释来源。
|
|
|
+ const metrics = (block.raw.metrics || []).map((metric) => ({
|
|
|
+ ...toOperatingMetric(metric),
|
|
|
+ industryCode: code,
|
|
|
+ industryLabel: label,
|
|
|
+ }))
|
|
|
+ // 票 100:指标级的 `missing` 账本在**适配期**上收到行业块(逐条指标的缺失路径
|
|
|
+ // 去重后并进 `blockMissing`)。这样面板出口不必再对整棵载荷做深扫
|
|
|
+ // (6771 条 × 递归实测 ≈ 100 ms),而界面读到的仍是同一份去重清单。
|
|
|
+ const blockMissing = reader.missing.concat(collectDeclaredMissing(metrics))
|
|
|
return {
|
|
|
code,
|
|
|
label,
|
|
|
@@ -213,12 +227,8 @@ export function toOperatingInformation(raw) {
|
|
|
// 认不出的原因码回空串(接口词汇不上大屏,票 05/11 的教训)。
|
|
|
stateText: '',
|
|
|
abnormalMetricCount: numberish(block.raw.abnormalMetricCount),
|
|
|
- // 票 67:指标带上所属行业 —— 全部行业时清单逐行说明身份,卡片也能按行业解释来源。
|
|
|
- metrics: (block.raw.metrics || []).map((metric) => ({
|
|
|
- ...toOperatingMetric(metric),
|
|
|
- industryCode: code,
|
|
|
- industryLabel: label,
|
|
|
- })),
|
|
|
+ metrics,
|
|
|
+ missing: blockMissing,
|
|
|
}
|
|
|
}),
|
|
|
excluded: {
|
|
|
@@ -496,6 +506,70 @@ const EMERGENCY_DETAIL_CATEGORY = Object.freeze({
|
|
|
* @returns {Array} 五张 KPI 卡(管段独占一行)
|
|
|
*/
|
|
|
|
|
|
+/**
|
|
|
+ * 运行信息「设施台账」的**一行**(票 100 惰性化)。
|
|
|
+ *
|
|
|
+ * 为什么要惰性:本端点一次回 6771 条指标,而清单块每页只画 10 条(`PAGE_SIZE`)。
|
|
|
+ * 旧实现把每一条都整套物化(含 `identityPayload: { ...metric }` 深拷贝、meta 数组、
|
|
|
+ * tone 文案),真机实测整体 ≈ 92–100 ms,其中深拷贝占 ≈ 65 ms —— 面板视图 miss 一次
|
|
|
+ * (切模式 / 换筛选 / 30s 刷新)就是单帧 350–400 ms 的卡顿(维护者报的"特别卡"残余)。
|
|
|
+ * 派生字段只在**被画出来**时才需要,所以这里只固化标量/身份字段,
|
|
|
+ * 其余交给 `lazyFields`:第一次读才算,未渲染的行零成本。
|
|
|
+ *
|
|
|
+ * 字段语义一字不改(惰性只改"什么时候算",不改"算什么")—— t67b 的 rows 断言照常成立。
|
|
|
+ *
|
|
|
+ * @param {object} metric `toOperatingMetric()` 交出的指标
|
|
|
+ * @param {number} index 该行业块内序号(只用于保证块内行键唯一)
|
|
|
+ * @param {object} scope `operatingScope()` 的判定结果(`allView` / `code`)
|
|
|
+ * @returns {object} 面板清单行
|
|
|
+ */
|
|
|
+function operatingLedgerRow(metric, index, scope) {
|
|
|
+ const category = facilityDetailCategory(metric)
|
|
|
+ // 身份字段是**点击分派**的判据(`cardActionFor` 直接读),必须现在就定下来:
|
|
|
+ // 它是标量/布尔,成本可忽略,而且不物化会造成"卡片能不能点"随渲染时机变化。
|
|
|
+ const row = {
|
|
|
+ // 票 64:键必须块内唯一(真实载荷里同一 metricCode 整块碰撞,cardAction 是 keyed v-for)。
|
|
|
+ // 票 67:全部行业时同一 metricCode 跨行业重复,行业也编进键。
|
|
|
+ key: `${metric.industryCode || scope.code || ''}|${metric.code || ''}|${index}`,
|
|
|
+ identityKind: CARD_ACTION.FACILITY,
|
|
|
+ facilityLocate: Boolean(metric.facilityId && category),
|
|
|
+ identityCode: category ? metric.facilityId || '' : '',
|
|
|
+ identityCategory: category,
|
|
|
+ identityReason: category
|
|
|
+ ? (metric.facilityId ? '' : '缺少设施主键,无法展开')
|
|
|
+ : '设施类别未发布,无法展开详情',
|
|
|
+ // 票 50:指标行**不产出定位点**(`points` 恒空)。`pointCodes` 实为源设施编码(票 49 裁定),
|
|
|
+ // 不进一张图对象序列,画按钮就是「不可达做成常态」+ 撞名假阳性(PS001/PS002 会飞向
|
|
|
+ // 不相干的排水监测点)。「没有可定位对象就不画按钮」由这里的构造保证。
|
|
|
+ points: [],
|
|
|
+ }
|
|
|
+ // 下面这些都是纯派生(只由这一条 metric 决定),所以可以安全惰性化。
|
|
|
+ return lazyFields(row, {
|
|
|
+ // 票 73 裁定 A:标题带设施编码且**编码在前**(同页十行同名;编码放后面会被省略号切掉尾号)。
|
|
|
+ title: () => metricTitle(metric),
|
|
|
+ // 维护者 2026-10-29:卡片状态与详情**同一份**设施状态(正常/维护中/异常),
|
|
|
+ // 不再各显示各的。后端没给状态时退回负荷态标签(不编造设施状态)。
|
|
|
+ tone: () => (metric.facilityStatus ? facilityStatusTone(metric.facilityStatus) : loadTone(metric.loadState)),
|
|
|
+ toneLabel: () => (metric.facilityStatus
|
|
|
+ ? facilityStatusText(metric.facilityStatus) || codeText(metric.loadState)
|
|
|
+ : codeText(metric.loadState)),
|
|
|
+ meta: () => [
|
|
|
+ // 票 67:全部行业时逐行补行业名,否则清单看不出这一行属于哪个行业。
|
|
|
+ scope.allView ? metric.industryLabel || '' : '',
|
|
|
+ `当前 ${numberText(metric.value, metric.unit)}`,
|
|
|
+ `能力 ${numberText(metric.capacityValue, metric.unit)}`,
|
|
|
+ `负荷 ${ratioText(metric.loadRatio)}`,
|
|
|
+ codeText(metric.loadState),
|
|
|
+ codeText(metric.freshness),
|
|
|
+ metric.abnormal ? '异常' : '',
|
|
|
+ ].filter(Boolean),
|
|
|
+ // 详情预填:只有**点开**这张卡时才被 `cardActionFor` 之后的 handler 取走,
|
|
|
+ // 没点过的行永远不构造。`entityDetailStore.show()` 自己会再浅拷一份,所以这里
|
|
|
+ // 不必担心共享引用被下游改写。
|
|
|
+ identityPayload: () => ({ ...metric, category, industry: metric.industryCode || scope.code, status: metric.facilityStatus }),
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 运行信息「设施数量」读数的来源与口径(维护者 2026-10-29 改版后的话术)。
|
|
|
*
|
|
|
@@ -563,49 +637,11 @@ export function operationPanel(data, section, extra) {
|
|
|
// 这本来就是**按设施逐条**的清单(一条指标对应一个设施),叫台账比叫指标准确,
|
|
|
// 也与上方「管段/水厂/水源地/用水户/泵站」那组设施读数同一套词。
|
|
|
'设施台账',
|
|
|
- metrics.map((metric, index) => ({
|
|
|
- // 票 64 顺带修的键缺陷(分页把它从「理论问题」变成「每帧都碰的问题」):
|
|
|
- // 旧键 `${scope.code}|${metric.code}` 在真实载荷里**整块碰撞**——燃气 699 条
|
|
|
- // 全是同一个 metricCode(太常片区燃气长度),699 行共用一个键,
|
|
|
- // 而清单卡是 keyed v-for(`:key="row.key"`)。实测翻页内容仍正确,
|
|
|
- // 但节点身份已经不存在(换筛选/刷新时靠下标兜底重排),所以加序号后缀
|
|
|
- // 保证**块内每行身份唯一**;行序由后端给、前端不重排(票 10/11 立场),
|
|
|
- // 所以序号在同一条件下稳定。
|
|
|
- // 票 67:全部行业时同一 metricCode 会跨行业重复,行业也编进键;
|
|
|
- // 单行业时它是常量,不破坏"同一条件下稳定"。
|
|
|
- key: `${metric.industryCode || scope.code || ''}|${metric.code || ''}|${index}`,
|
|
|
- // 票 73 裁定 A:标题带设施编码且**编码在前**(同页十行同名;编码放后面会被省略号切掉尾号)。
|
|
|
- title: metricTitle(metric),
|
|
|
- identityKind: CARD_ACTION.FACILITY,
|
|
|
- facilityLocate: Boolean(metric.facilityId && facilityDetailCategory(metric)),
|
|
|
- identityCode: facilityDetailCategory(metric) ? metric.facilityId || '' : '',
|
|
|
- identityCategory: facilityDetailCategory(metric),
|
|
|
- identityPayload: { ...metric, category: facilityDetailCategory(metric), industry: metric.industryCode || scope.code, status: metric.facilityStatus },
|
|
|
- identityReason: facilityDetailCategory(metric)
|
|
|
- ? (metric.facilityId ? '' : '缺少设施主键,无法展开')
|
|
|
- : '设施类别未发布,无法展开详情',
|
|
|
- // 维护者 2026-10-29:卡片状态与详情**同一份**设施状态(正常/维护中/异常),
|
|
|
- // 不再各显示各的。后端没给状态时退回负荷态标签(不编造设施状态)。
|
|
|
- tone: metric.facilityStatus ? facilityStatusTone(metric.facilityStatus) : loadTone(metric.loadState),
|
|
|
- toneLabel: metric.facilityStatus
|
|
|
- ? facilityStatusText(metric.facilityStatus) || codeText(metric.loadState)
|
|
|
- : codeText(metric.loadState),
|
|
|
- meta: [
|
|
|
- // 票 67:全部行业时逐行补行业名,否则清单看不出这一行属于哪个行业。
|
|
|
- scope.allView ? metric.industryLabel || '' : '',
|
|
|
- `当前 ${numberText(metric.value, metric.unit)}`,
|
|
|
- `能力 ${numberText(metric.capacityValue, metric.unit)}`,
|
|
|
- `负荷 ${ratioText(metric.loadRatio)}`,
|
|
|
- codeText(metric.loadState),
|
|
|
- codeText(metric.freshness),
|
|
|
- metric.abnormal ? '异常' : '',
|
|
|
- ].filter(Boolean),
|
|
|
- // 票 50:指标行**不产出定位点**(`points` 恒空)。`pointCodes` 实为源设施编码(票 49 裁定),
|
|
|
- // 不进一张图对象序列,画按钮就是「不可达做成常态」+ 撞名假阳性(PS001/PS002 会飞向
|
|
|
- // 不相干的排水监测点)。「没有可定位对象就不画按钮」由这里的构造保证(应急保障三段
|
|
|
- // 的既有形态),不再靠渲染层过滤。
|
|
|
- points: [],
|
|
|
- })),
|
|
|
+ // 票 100:行**惰性物化**。真实载荷 6771 条、每页只画 10 条(`PAGE_SIZE`),
|
|
|
+ // 旧实现把每条的派生字段(identityPayload 深拷贝 / meta / tone 文案)全算一遍
|
|
|
+ // ⇒ 单次视图构建 ≈ 92–100 ms,面板 miss 一次就是单帧 350–400 ms 的卡顿。
|
|
|
+ // 现在只固化标量身份字段,派生字段第一次被读才计算(见 `operatingLedgerRow`)。
|
|
|
+ metrics.map((metric, index) => operatingLedgerRow(metric, index, scope)),
|
|
|
'当前行业没有设施台账',
|
|
|
// 票 64:本块接入统一分页(规范条文 1/2/4 + 跳页节)。**为什么这里必须声明**:
|
|
|
// 同一个 list 渲染分支服务多处清单,其中应急保障三段另有专票(63);
|
|
|
@@ -636,7 +672,9 @@ export function operationPanel(data, section, extra) {
|
|
|
// 管段/设施构件拾取走既有「重点对象局部增强」(pipeEnhancement,票 32),本票不动那条链路。
|
|
|
locate: [],
|
|
|
locateNote: '指标关联的是设施编码(水厂/泵站/管网/排水设施),不在一张图的点位序列里,本面板没有可定位对象;管段与设施构件请在图上拾取(地下管线详情视图)',
|
|
|
- missing: panelMissingNote(current),
|
|
|
+ // 票 100:只读领域对象自己的 `missing` 账本(分区 + 各行业块,指标级已在适配期上收),
|
|
|
+ // 不再对整棵载荷做深扫 —— 6771 条指标深扫实测 ≈ 100 ms,扫的仍是同一份清单。
|
|
|
+ missing: panelMissingFromSections([current, ...(current.industries || [])]),
|
|
|
placeholder: sectionPlaceholder(section),
|
|
|
}
|
|
|
}
|