# 驾驶舱长列表统一分页规范(2026-10-18) 来源:[票 57](../issues/57-untitled.md)(维护者 2026-10-17 反馈 5)。上位规格:[2026-10-17-monitoring-feedback.md](2026-10-17-monitoring-feedback.md) §5。 ## 目标与适用判据 凡**可能包含表格或卡片列表、且新增数据会无限拉长页面**的区域,统一分页行为: - 每页最多 10 条(`PAGE_SIZE = 10`),前端展示窗口恒 ≤10 条; - 控件三件:上一页、下一页、**页码直达**;只有一页时不画任何控件; - 先筛选/分类,后分页;条件变化回第 1 页;总数不由当前页反推。 **排除**(不是条目清单,或数量契约有界,详见盘点表):聚合图表(柱/环/堆叠/趋势/饼/维度柱)、KPI 行、键值行块、一张图顶部状态栏图例、地图重叠聚合 fan(渲染层)、契约有界小清单(管理信息片区×行业明细、预警分析重点识别)、地图摘要点位装载(渐进保护,见三分法)。 ## 统一行为(规范条文) 1. **页大小**:`PAGE_SIZE = 10`。任何接入区域的展示窗口每页 ≤10 条。 2. **控件**:`上一页|页码|下一页` + 计数行「第 X/Y 页 · 共 N 条」。页码直达=页码按钮:当前页不可点并标 `aria-current="page"`;总页数 >7 时收缩为「1 … c−1 c c+1 … Y」窗口(恒含首末页与省略号)。 3. **禁用态**:到边按钮 `disabled` 且**保留占位**(按钮消失会让计数行跳动,读起来像出错——票 46 既有语义)。 4. **一页不画控件**:`pageCount <= 1` 时整个 pager 不渲染(短列表不多出两个死按钮)。 5. **筛选先于分页**:分类 chips、全局筛选(行业/片区/设备类型/点位状态/范围)、模式切换(预警↔报警)、设备·点位切换、时间范围变化,都先作用于集合或请求参数,再分页。 6. **复位规则**:上述任何条件变化 → 页码回第 1 页。**新鲜度/定时刷新不复位页码**(用户停在第 3 页时,周期刷新不得把人弹回第 1 页);数据变少导致页码越界时由分页函数夹回最后一页。 7. **总数口径**:服务端分页读响应 `pagination.total`;前端集合分页读**过滤后**集合长度;渐进装载用「已装载 M · 共 N」双口径。任何区域不得用「当前页 × 页大小」反推总数。 8. **态语义**(沿用既有分区词汇,不另造一套): - 加载:`正在取数…`(分区 pending 语义);翻页请求进行中保持旧内容或同一占位,不闪空白; - 空态:「真没有」与「条件下没有」分文案(`当前没有记录` vs `当前分类下没有记录(换一个分类或点「全部」)`); - 失败:分区 FAILED/UNAVAILABLE 占位 + 分区状态标签;渐进装载下一批失败**不回滚已装载内容**(台账既有语义); - 竞态:沿用各 store 既有 token/纪元纪律;翻页请求迟到即作废,不落进新页码。 9. **无障碍**:pager `role="group"` + `aria-label="「列表名」分页"`;页码当前项 `aria-current="page"`;计数行是随页码更新的文本。 10. **文案纪律**:不把后端 `page`/`pageSize` 参数名暴露到界面文案(上位规格 §5 原文);只说「页」「条」。 11. **单源**:翻页窗口计算只有一份(`domain/pagination.js`,自 `equipmentDetailView.pagedRows` 升格,`HISTORY_PAGE_SIZE`/`pagedRows` 原名保留导出兼容),pager 控件只有一份(`PagerNav.vue`)。禁止任何组件重写翻页算术或另画一套 pager。 ## 三分法:服务端 / 前端集合 / 渐进装载 - **服务端页码分页**(翻页即请求,`page`+`pageSize=10` 进查询串): 预警处置·当前事件表(`/events/current`);预警处置·历史事件**单窗范围**(`/events/history-analysis` 近 7 天;逐事件行与 `page`/`pageSize` 契约为票 61 连同后端一并补齐)。 - **前端集合分页**(数据已一次装载,pager 只切窗口、零请求): 设备详情预警/报警/工单状态;点位摘要设备卡片与关联设备图层清单;运行概况设备台账(票 62 追加需求:原「异常设备清单」已撤下);基础设施·运行信息指标列表(票 64 补入,原误列排除);历史事件**多窗合并范围**(近 30/90 天——`historyWindows` 分窗合并后不存在「全局页」,回退已加载合并集合分页并在页脚如实说明;**跨窗全局分页**仍不在本图范围,需要时必须另立后端票重画契约)。 - **渐进装载 + 页码 UI**(数据链路保持自动渐进,界面只对已装载集合翻页): 监测点台账——装载与一张图点位数据同款自动渐进链(条件变化 → 新快照 → 自动连翻到 settled,沿用快照 token 与纪元纪律);UI 面板对已装载集合做统一页码分页(10/页、页码直达)。**票 62 已落地**:装载链在 `domain/ledgerLoader.js`,页大小由后端默认值决定(前端不硬编码),`snapshotAt` 由后端回显、缺失即停链;「加载下一页」按钮退役。 - **排除**:地图摘要点位装载保持自动渐进、无列表 UI(票 14/23 契约,**不得**误改为页码分页)。 ## 维护者裁定(2026-10-18;2026-10-19 追加第 3 条) 1. **监测点台账**:监测点数据保持自动渐进式加载(与地图中点位数据的加载方式一致),但 UI 页面中要能翻页——「加载下一页监测点」按钮随之退役。 2. **事件清单**:走服务端页码分页(翻页带 `page` 参数发请求);多窗合并范围的回退规则见三分法。 3. **验收与走查以真实数据为准,不为演示扩写 mock 样例**(票 64 引出):mock 样例数据量小 ⇒ 某些接入区域在 mock 下**不足一页、按条文 4 正确地不画控件**,这是预期行为而非缺陷,**不得**为此把 mock 样例补到 >10 条(扩了也没有演示价值,真实数据才是验收口径)。多页与边界行为一律在**闸门里用真实载荷或合成载荷证明**(往闸门灌数据 ≠ 改 mock 样例:票 62 的 mock 判据、票 64 的 t64 V 组都是这条路);真实观感归票 18 联调窗口。批次二~五执行票同样适用。 ## 全仓盘点 | 区域 | 代码接缝 | 数据源 · 现状 | 目标 | 批次 | |---|---|---|---|---| | 设备详情 · 预警信息 / 报警信息 | `EquipmentDetailSections.vue`(`data-event-list`)、`domain/equipmentDetailView.js` | 设备详情聚合两分区;已每页 10、先分类后翻、一页无控件 | 迁共享 pager,**补页码直达** | 一 | | 设备详情 · 工单状态 | 同上(`data-work-order-list`) | 设备详情聚合 workOrders;全量直渲 | 前端集合分页 10/页 | 一 | | 预警处置 · 当前事件表 | `EventListPane.vue`(`data-event-table`)、`stores/eventStore.js` | `/events/current`(后端默认 100、上限 1000);前端砍最新 12 条 | 服务端页码分页(`pageSize=10`);mode/筛选/范围变化回第 1 页 | 三 | | 预警处置 · 历史事件 | `domain/warningResponsePanels.js historyAnalysisPanel`(PanelBlocks list 块) | `/events/history-analysis` 分窗合并;`events.slice(0,12)` | 单窗(近 7 天)服务端分页;多窗(近 30/90 天)合并集合分页 + 页脚如实说明 | 三 | | 运行概况 · 设备台账(原「异常设备清单」) | `domain/monitoringPanels.js operationOverview`(list 块) | `/monitoring/overview` 的 `devices[]`(票 62 追加需求;行数 ≡ `counts.equipmentCount`) | 前端集合分页 10/页(页脚报设备台数) | 四 ✅ | | 运行监测 · 监测点台账 | `domain/monitoringPanels.js monitoringPointLedger`、`stores/monitoringStore.js loadMoreLedger`、PanelBlocks `data-ledger-more` | `/monitoring/distribution` 服务端分页 + 快照 token(票 23);「加载下一页」每次追加后端一页 ≈500 卡 | 装载保持自动渐进(同地图链路);UI 对已装载集合页码分页 10/页;按钮退役 | 四 | | 点位摘要 · 设备卡片 | `PointDetailCard.vue`(`data-device-cards`) | 设备详情端点 `devices[]`;全量直渲 | 前端集合分页 10/页 | 二 | | 点位摘要 · 关联设备图层清单 | `PointDetailCard.vue`(`data-device-layer-list`,票 42) | 地图快照 `deviceCodes`,**保留全部编码**(含待确认) | 前端集合分页 10/页(数据仍保留全部,窗口化渲染;互斥归类与差额口径不变) | 二 | | 基础设施 · 运行信息指标列表 | `domain/infrastructurePanels.js operationPanel` → `listBlock('运行指标', …, { paginate: true, pageScope })` | `/infrastructure/operating-information` 一次回全部行业指标;**实测 6772 条**(供水 2497/排水 3576/燃气 699),响应 **2.3 MB**;原全量直渲、块标题「指标占位」与实际内容不符 | 前端集合分页 10/页(**本票 57 曾误列为排除项,2026-10-19 由维护者反馈纠正**);块标题改为如实说法 —— **已由票 64 落地** | 五 ✅ | | 基础设施 · 管理信息片区×行业明细 | `managementPanel`(list 块) | `districtIndustry[]`,**实测 9 行**,组合数契约有界(片区×行业) | **排除**(有界小清单,实测 <10) | — | | 基础设施 · 应急保障三段 | `domain/infrastructurePanels.js emergencySectionBlock`(list 块) | `/infrastructure/emergency-support` 三段 items;**2026-10-22 实测 0+20+9=29 条**(防护目标 20 条已超一页) | 前端集合分页 10/页(一页时无控件)——**已由票 63 落地** | 五 ✅ | | 预警分析 · 重点识别 | `eventAnalysisPanel`(list 块) | 后端聚合重点,数量少 | **排除** | — | | 一张图 · 地图摘要点位装载 | `stores/mapStore.js`、`domain/mapSummary.js` | `/map/snapshot` 快照自动链 | **排除**:保持自动渐进装载,不改页码 | — | | 其余 | 图表块(bars/donut/stacked/trend/pie/dimbars)、KPI、键值行、状态栏图例、地图重叠聚合 fan | 非条目清单 | **排除** | — | ## 跳页(维护者 2026-10-19 追加) 页码直达按钮之外,pager 还必须提供**输入框跳页**: - 一个数字输入框 + 「跳转」按钮(或 Enter 提交),输入目标页号后直接跳到该页; - 输入非数字/0/超出 `pageCount` 时**夹回合法范围**(1..pageCount),不报错、不发请求、不清空列表; - 输入框占位提示当前页(`placeholder = 当前页`),`aria-label="跳转到指定页"`; - 总页数 ≤1 时整块 pager(含输入框)不渲染,与本规范第 4 条一致; - 跳页与「上一页/下一页/页码」共用同一个翻页出口,**不新增第二套页码状态**。 ## 实施批次 每批一张执行票(本票只收口规范与盘点,不实施): - **批次一**:共享分页基建(`domain/pagination.js` + `PagerNav.vue`,含**页码直达与输入框跳页**)+ 设备详情三清单接入(预警/报警迁移、工单状态新接入)。 - **批次二**:点位摘要两级清单(设备卡片、关联设备图层清单)分页。 - **批次三**:预警处置事件清单服务端页码分页(当前事件表 + 历史事件单窗/多窗规则 + mock 同构)。 - **批次四**:运行监测(运行概况**设备台账**前端集合分页;监测点台账自动渐进装载链 + 页码 UI、撤「加载下一页」按钮)。 - **批次五**:基础设施运行信息指标列表(6772 条,最高优先)✅ 票 64 + 应急保障三段接入(PanelBlocks list 块路径收尾)✅ 票 63。 执行票:见票 57 正文(批次五已按 2026-10-19 维护者反馈扩为两张:运行信息指标列表 + 应急保障)。 ## 非目标 - 不改后端只读契约与快照 token 语义(票 23);地图渐进装载不改页码。 - 不改排序口径(「前端不重排」立场不变;服务端分页按后端返回序渲染)。 - 不给聚合图表、KPI、键值行与契约有界小清单加分页。 - 按 AGENTS.md,实施票默认跳过 build/dev/浏览器验证,除非维护者当次明确授权。 ## 修订记录 - 2026-10-18 首版(票 57)。 - **2026-10-19 维护者 8 条反馈后的纠正**:① 补齐「输入框跳页」要求;② **撤销运行信息指标列表的排除**——实测 6772 条 / 2.3 MB,改列入批次五(原判断基于代码推断"单行业指标条数契约有界",被真实数据证伪);③ 应急保障盘点点亮实测量(8+0+20=28);④ 管理信息实测 9 行,维持排除并补实测依据。 - **2026-10-19 票 64 收口时的追加裁定**(见「维护者裁定」第 3 条):验收以真实数据为准,**不为演示扩写 mock 样例**;mock 下不足一页不画控件是预期行为,多页行为归闸门用真实/合成载荷证明。 - **2026-10-19 票 60(批次二)维护者当次两条裁定**:① 点位摘要里票 14 的「另有 N 台 · 未命中当前筛选」**展开子清单同样 ≤10/页**、独立第三套页码(判据一视同仁:它可能拉长页面;「另有 N 台」的 N 仍报全量,差额可追溯不变);② **计数行(条文 2/9)在 `PointDetailCard` 的三处 pager 一律省略**——`PagerNav` 新增 `show-count` 开关(默认 `true`,批次一既有消费方形态不变);该组件上方已各有权威总数(设备合计 KPI 读后端统计;清单标题「总数 X 台(已上图 Y)」只认当前快照),并排再画「共 N 条」就成了窗口口径的第二个总数。 - **2026-10-21 票 61(批次三)落地与两条当次裁定**:① 三分法的「历史事件单窗 = 服务端页码」此前**只有前端假设、真实契约不支持**(`/events/history-analysis` 只回小时桶、无逐事件行、也不接 `page/pageSize`)——维护者裁定**本票连同后端一起完成**:该端点新增 `events[]`(形状与 `events/current` 的 `EventItem` 同构)与 `pagination`,接 `page`/`pageSize`(默认 1/100、上限 1000)。前端的服务端页码分页因此有真实契约可依。② **多窗合并范围的窗口数如实报**:`historyWindows` 在「起止差恰为 30/90 天」时会切出**毫秒级尾窗**(实测 30 天 = 2 窗、90 天 = 4 窗),页脚口径写实际窗数(`已合并 N 个时间窗 · 已加载 M 条`),**不写死 3**、也不伪造全局页码;页码归属键(`pageScope`)用**请求侧范围键**(后端回显 `range.type=TREND_WINDOW` 不能当预设范围用)。 - **2026-10-22 票 62(批次四)落地与三条当次裁定**:① **渐进装载的页大小不归前端定**——台账装载链查询串**不带 `pageSize`**,由后端契约默认值说了算(前端硬编码 500 就是第二份口径,后端改默认值时前端会悄悄要错页);装载器只在响应里读回它用于读数。② **快照 token 只认响应回显**——`/monitoring/distribution` 此前 `page>1` 强制要 `snapshotAt` 却从不回显(前端只能猜时间戳),维护者裁定本票连同后端补齐回显(`data.snapshotAt`,与 `map-snapshot` 同形);装载器缺 token 时**停链报 `snapshot-token-missing`**,绝不拿猜的值去换 400。③ **周期刷新不重放全链**(票 33 教训):台账已 settled 时,新鲜度 tick 走 `reuseLedgerSnapshot()` **一个请求都不发**;未 settled 且未失败才补完剩余批次——「定时刷新不复位页码」(条文 6)在渐进装载上的对应形态就是「不重放、不重置已装载集合」。另记:**「加载下一页监测点」按钮与 `ledger-more` 接缝随本票退役**,台账的「还有没有下一批」由页脚 `已装载 M · 共 N 条(含未关联设备)` 如实说明。 - **2026-10-22 票 62 追加需求(维护者当日二次裁定):运行概况展示设备台账,不展示异常设备清单**。原状:运行概况借用 `/monitoring/situation` 的 `abnormalEquipment[]` 画「异常设备清单」——那张清单只装 WARNING/ALARM/EXPIRED/OFFLINE 四态设备(实测 8 台),**不是台账、条数也与设备总数对不上**。维护者要求按真实数据展示**9 台监测设备**的台账。裁定:**后端 `/monitoring/overview` 回出逐设备 `devices[]`**(该服务 `build()` 里本就组装了 `selectedDevices`,只用于算 `createdAt` 与 `counts.equipmentCount`,未回响应);前端运行概况改读这一份。**口径纪律**:台账行数**恒等于 `counts.equipmentCount`**(同一次遍历产出,不是第二个口径),筛选生效时两者同步收窄;每行 `deviceCode`/`deviceName`/`deviceType{code,name}`/`industry`/`monitoringState`/`monitoringPointCode`/`sampledAt`/`createdAt`,缺值一律 `null`(不拿编码冒充名称、不填 0);**异常口径不丢**(指标状态分布、预警/报警趋势、离线说明仍在),只是不再拿它当台账。分页口径不变:前端集合分页 10/页、页码直达,一页不画控件。 - **2026-10-22 票 63(批次五)落地与两条当次记录**:① **应急保障三段(危险源/防护目标/应急资源)接前端集合分页**,三段共用 `emergencySectionBlock` 一处声明 ⇒ 行为一致、不各写一份;**各自独立页码**:`pageScope` = `段码|条数|段状态`(标量键;条数也入键 ⇒ 换类别导致集合变短时页码槽跟着换、越界自然回第 1 页)。**实测校准**:2026-10-22 线上 `protectionTargets` **20 条**(两页,此前一屏 20 张卡)、`emergencyResources` 9 条、`hazardSources` 0 条(EMPTY),`totalObjectCount = 29` —— 与本规范首版记的「8+0+20=28」略有出入(现网增长,`totals` 口径随之变),**分页必要性因此由真实数据直接证明**,不必再靠 mock 灌数就「有没有增长空间」这一点争论。② **mock 静态样例仍不扩面**(维护者 2026-10-19 裁定第 3 条):`contractMocks` 的三段合计仅 4 条(1/1/2)⇒ 按条文 4 正确地不画控件,这是**预期行为**;多页行为由 t63 闸门用**真实量级载荷**(0/20/9)证明。排除项(管理信息「片区 × 行业明细」、预警分析「重点识别」)**未顺手接入**,t63 的 S4 守卫钉住这条边界。