/** * T19 预警处置模式面板纯逻辑检查。 * 按仓库约定为零依赖 Node 断言脚本;本文件只描述验收闸门,不启动应用。 */ import { readFileSync } from 'node:fs' import { EVENT_RANGES, WARNING_RESPONSE_PANEL_IDS, eventAnalysisPanel, historyAnalysisPanel, responseEfficiencyPanel, toEventAnalysis, toEventHistory, toHandlingEfficiency, } from '../../../src/modules/cockpit/domain/warningResponsePanels.js' import { section, SECTION_STATE } from '../../../src/modules/cockpit/domain/sectionState.js' import { panelsForMode } from '../../../src/modules/cockpit/domain/panelCatalog.js' import { hasPanelView, implementedPanelIds, panelView } from '../../../src/modules/cockpit/domain/panelViews.js' import { eventListSwitchView } from '../../../src/modules/cockpit/domain/eventList.js' const checks = [] function check(name, pass, evidence = '') { checks.push(pass) console.log(`${pass ? 'PASS' : 'FAIL'} ${name}${evidence ? ` — ${evidence}` : ''}`) } const ready = (data) => ({ ...section(SECTION_STATE.SUCCESS, { data }), loaded: true }) const historyPayload = { mode: 'WARNING', range: { type: 'LAST_7_DAYS', startTime: '2026-08-26', endTime: '2026-09-02' }, trend: [{ bucket: '2026-09-01', count: 4 }], sections: { events: { state: 'SUCCESS', data: [{ eventNo: 'H-1', eventName: '燃气预警', level: 1, status: 'ACTIVE', eventTime: 'T', deviceCode: 'D-1' }] } }, } check('A1-four-warning-panels-match-catalog', panelsForMode('warningResponse').join(',') === 'currentEvents,historyAnalysis,responseEfficiency,eventAnalysis') check('A2-three-aggregates-registered', WARNING_RESPONSE_PANEL_IDS.every(hasPanelView) && hasPanelView('currentEvents') === false, `implemented=${implementedPanelIds().length}`) check('A3-range-options-cover-7-30-90', EVENT_RANGES.map((item) => item.key).join(',') === '7d,30d,90d') const history = toEventHistory(historyPayload) check('B1-history-adapter-keeps-events-and-range', history.events.length === 1 && history.events[0].code === 'H-1' && history.range.key === '7d') const efficiency = toHandlingEfficiency({ handlingEfficiency: { value: 0.5, totalEvents: 10, handledEvents: 5 }, averageResponseSeconds: 60, averageCloseSeconds: 3600, // 票 20:键为后端 0–7(共 8 个码,6 是「延期中」)。 workOrderStates: { 0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8 }, }) check('B2-efficiency-adapter-keeps-rate-durations-and-eight-states', efficiency.handlingRate === 0.5 && efficiency.responseDuration.value === 60 && efficiency.closeDuration.value === 3600 && efficiency.workOrderStates.length === 8) const analysis = toEventAnalysis({ dimensions: { types: [{ code: 'GAS', count: 2 }], levels: [{ level: 1, count: 3 }], industries: [{ code: 'GAS', count: 2 }], regions: [{ code: '城区A', count: 2 }] }, highlights: [{ title: '重点', detail: '高等级', count: 3 }] }) check('B3-analysis-adapter-keeps-multiple-dimensions', analysis.types.length === 1 && analysis.levels[0].label === '1级' && analysis.industries.length === 1 && analysis.regions.length === 1 && analysis.highlights.length === 1) const currentSection = ready(history) const historyView = historyAnalysisPanel(history, currentSection) const efficiencyView = responseEfficiencyPanel(efficiency, ready(efficiency)) const analysisView = eventAnalysisPanel(analysis, ready(analysis)) check('C1-history-view-has-trend-and-event-list', historyView.blocks.some((block) => block.kind === 'trend') && historyView.blocks.some((block) => block.kind === 'list')) check('C2-efficiency-view-has-rate-durations-and-state-bars', efficiencyView.blocks[0].items.some((item) => item.key === 'handlingRate') && efficiencyView.blocks.some((block) => block.label === '工单状态分布')) check('C3-analysis-view-has-four-distributions-and-highlights', analysisView.blocks.filter((block) => block.kind === 'bars').length === 4 && analysisView.blocks.some((block) => block.label === '重点识别')) const emptyView = historyAnalysisPanel(null, { ...section(SECTION_STATE.EMPTY), loaded: true }) check('C4-empty-panel-is-explicit', emptyView.hasContent === false && emptyView.placeholder.includes('没有')) const staleView = historyAnalysisPanel(history, { ...section(SECTION_STATE.STALE, { data: history, retained: true }), loaded: true }) check('C5-stale-panel-retains-content', staleView.hasContent === true) check('D1-mode-view-is-single-source', eventListSwitchView('WARNING').codeLabel !== eventListSwitchView('ALARM').codeLabel && eventListSwitchView('WARNING').statusLabel !== eventListSwitchView('ALARM').statusLabel) check('D2-panel-view-dispatches-warning-builders', panelView('historyAnalysis', history, currentSection).name === 'historyAnalysis' && panelView('responseEfficiency', efficiency, ready(efficiency)).name === 'responseEfficiency' && panelView('eventAnalysis', analysis, ready(analysis)).name === 'eventAnalysis') const moduleRoot = new URL('../../../src/modules/cockpit/', import.meta.url) const read = (path) => readFileSync(new URL(path, moduleRoot), 'utf8') const shell = read('CockpitShell.vue') const pane = read('components/WarningResponsePane.vue') check('E1-shared-mode-and-range-controls-are-visible', pane.includes('eventStore.setMode') && pane.includes('eventStore.setRange') && shell.includes('WarningResponsePane')) check('E2-components-do-not-build-api-paths', !pane.includes('/events/') && !pane.includes('fetch(')) check('E3-current-event-location-remains-detail-flow', read('components/EventDetailCard.vue').includes('定位关联设备') && read('stores/eventStore.js').includes('planEventLocate')) const passed = checks.filter(Boolean).length console.log(`\\nT19 warning-response: ${passed}/${checks.length}`) if (passed !== checks.length) process.exitCode = 1