| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- import assert from 'node:assert/strict'
- import { readFile } from 'node:fs/promises'
- import test from 'node:test'
- import {
- ABNORMAL_TYPE_OPTIONS,
- buildAbnormalCsv,
- buildAbnormalExportQuery,
- buildAbnormalListQuery,
- computeAbnormalSummary,
- filterAbnormalDevices,
- normalizeAbnormalRow,
- resolveAbnormalTypes
- } from '../src/utils/waterDeviceAbnormalModel.js'
- // 独立构造的样例设备:三台异常来源各不相同,一台完全正常用于排除断言。
- const alarmDevice = {
- equipmentId: 'eq-alarm',
- equipmentCode: 'CN-GS-202609020001',
- equipmentName: '城南压力计',
- equipmentTypeName: '压力计',
- district: 0,
- alarmStatus: 1,
- onlineStatus: 1,
- currentStatus: 0,
- equipmentImage: '/profile/upload/alarm.png'
- }
- const offlineDevice = {
- equipmentId: 'eq-offline',
- equipmentCode: 'CB-GS-202609020002',
- equipmentName: '城北漏失监测器',
- equipmentTypeName: '漏失监测仪',
- district: 1,
- alarmStatus: 0,
- onlineStatus: 0,
- currentStatus: 0
- }
- const maintenanceDevice = {
- equipmentId: 'eq-maintenance',
- equipmentCode: 'TC-GS-202609020003',
- equipmentName: '太常流量计',
- equipmentTypeName: '超声波外夹流量计',
- district: 2,
- alarmStatus: 0,
- onlineStatus: 1,
- currentStatus: 1
- }
- const healthyDevice = {
- equipmentId: 'eq-healthy',
- equipmentCode: 'CN-GS-202609020004',
- equipmentName: '城南消火栓压力终端',
- equipmentTypeName: '消火栓压力监测终端',
- district: 0,
- alarmStatus: 0,
- onlineStatus: 1,
- currentStatus: 0
- }
- const allRows = [alarmDevice, offlineDevice, maintenanceDevice, healthyDevice]
- test('abnormal source covers alarm, offline and maintenance from the device status table', () => {
- assert.deepEqual(resolveAbnormalTypes(alarmDevice), ['alarm'])
- assert.deepEqual(resolveAbnormalTypes(offlineDevice), ['offline'])
- assert.deepEqual(resolveAbnormalTypes(maintenanceDevice), ['maintenance'])
- assert.deepEqual(resolveAbnormalTypes(healthyDevice), [])
- const bothSources = { ...offlineDevice, alarmStatus: 1, currentStatus: 1 }
- assert.deepEqual(resolveAbnormalTypes(bothSources), ['alarm', 'offline', 'maintenance'])
- })
- test('abnormal type filters keep only the matching abnormal sources', () => {
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'all' }).map(row => row.equipmentId),
- ['eq-alarm', 'eq-offline', 'eq-maintenance']
- )
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'alarm' }).map(row => row.equipmentId),
- ['eq-alarm']
- )
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'offline' }).map(row => row.equipmentId),
- ['eq-offline']
- )
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'maintenance' }).map(row => row.equipmentId),
- ['eq-maintenance']
- )
- // 离线且报警要求两个条件同时成立,单独离线不算命中
- assert.deepEqual(filterAbnormalDevices(allRows, { type: 'offline_alarm' }), [])
- assert.deepEqual(
- filterAbnormalDevices([{ ...alarmDevice, onlineStatus: 0 }, ...allRows], { type: 'offline_alarm' })
- .map(row => row.equipmentId),
- ['eq-alarm']
- )
- assert.ok(ABNORMAL_TYPE_OPTIONS.some(option => option.value === 'offline_alarm'))
- })
- test('keyword filter matches either the device code or the device name', () => {
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'all', keyword: 'TC-GS' }).map(row => row.equipmentId),
- ['eq-maintenance']
- )
- assert.deepEqual(
- filterAbnormalDevices(allRows, { type: 'all', keyword: '漏失' }).map(row => row.equipmentId),
- ['eq-offline']
- )
- assert.deepEqual(filterAbnormalDevices(allRows, { type: 'all', keyword: '不存在' }), [])
- })
- test('abnormal rows expose Chinese status text, reason and resolvable image url', () => {
- const row = normalizeAbnormalRow(alarmDevice)
- assert.equal(row.abnormalReasonText, '设备报警')
- assert.equal(row.alarmStatusText, '报警')
- assert.equal(row.onlineStatusText, '在线')
- assert.equal(row.currentStatusText, '在用')
- assert.equal(row.equipmentTypeName, '压力计')
- assert.ok(row.abnormalTypeLabels.includes('报警中'))
- assert.match(row.equipmentImageUrl, /\/profile\/upload\/alarm\.png$/u)
- const offlineRow = normalizeAbnormalRow(offlineDevice)
- assert.equal(offlineRow.abnormalReasonText, '设备离线')
- const maintenanceRow = normalizeAbnormalRow(maintenanceDevice)
- assert.equal(maintenanceRow.abnormalReasonText, '设备维修中')
- })
- test('abnormal summary counts each abnormal source independently', () => {
- const summary = computeAbnormalSummary([
- { ...alarmDevice, onlineStatus: 0 },
- offlineDevice,
- maintenanceDevice,
- healthyDevice
- ])
- assert.deepEqual(summary, { total: 3, alarm: 1, offline: 2, maintenance: 1, offlineAlarm: 1 })
- })
- test('list query is scoped to the water root type and fetches the whole filtered set', () => {
- assert.deepEqual(buildAbnormalListQuery({ total: 4 }), {
- rootTypeId: 1,
- pageNum: 1,
- pageSize: 4
- })
- assert.deepEqual(buildAbnormalListQuery({ total: 0, equipmentCode: 'TC-GS', equipmentName: '流量' }), {
- rootTypeId: 1,
- pageNum: 1,
- pageSize: 1,
- equipmentCode: 'TC-GS',
- equipmentName: '流量'
- })
- // 导出沿用同一筛选口径,但只按条件取数,不再带分页
- assert.deepEqual(buildAbnormalExportQuery({ equipmentCode: 'TC-GS' }), {
- rootTypeId: 1,
- pageNum: 1,
- pageSize: 1000,
- equipmentCode: 'TC-GS'
- })
- })
- test('csv export carries a BOM, Chinese headers and the filtered rows', () => {
- const csv = buildAbnormalCsv([normalizeAbnormalRow(alarmDevice)])
- assert.ok(csv.startsWith('\ufeff'), '导出内容需带 BOM 以便 Excel 正确识别中文')
- assert.match(csv, /设备编码/u)
- assert.match(csv, /异常原因/u)
- assert.match(csv, /CN-GS-202609020001/u)
- assert.match(csv, /设备报警/u)
- assert.equal(csv.split(/\r?\n/u).filter(Boolean).length, 2)
- })
- const routePath = 'src/views/subSystem/waterSupply/device/abnormal.vue'
- const pagePath = 'src/views/subSystem/waterSupply/components/WaterDeviceAbnormalPage.vue'
- test('abnormal management menu entry renders the dedicated abnormal board', async () => {
- const route = await readFile(routePath, 'utf8')
- assert.match(route, /WaterDeviceAbnormalPage/u)
- })
- test('abnormal board reads the unified equipment module within the water root type', async () => {
- const source = await readFile(pagePath, 'utf8')
- assert.match(source, /listEquipment\(/u)
- assert.match(source, /getEquipmentStatusStatistics\(/u)
- assert.match(source, /buildAbnormalListQuery\(/u)
- assert.match(source, /WATER_DEVICE_ROOT_TYPE_ID/u)
- // 取数范围由模型统一带上 rootTypeId,页面只传供水根类型常量
- assert.match(source, /getEquipmentStatusStatistics\(WATER_DEVICE_ROOT_TYPE_ID\)/u)
- // 供水专属的旧设备接口已下线,异常看板不得再引用
- assert.doesNotMatch(source, /waterSupply\/device\/(ledger|abnormal)/u)
- assert.doesNotMatch(source, /getDeviceLedgerStatistics/u)
- assert.doesNotMatch(source, /listAbnormalDevices/u)
- })
- test('abnormal board is a read-only board without dispatch or work-order creation', async () => {
- const source = await readFile(pagePath, 'utf8')
- assert.match(source, /异常类型/u)
- assert.match(source, /设备编码/u)
- assert.match(source, /设备名称/u)
- assert.match(source, /批量导出/u)
- assert.match(source, /<el-drawer/u)
- assert.match(source, /direction="rtl"/u)
- assert.match(source, /设备图片/u)
- assert.doesNotMatch(source, /派单/u)
- assert.doesNotMatch(source, /addWorkOrder|dispatchWorkOrder|createWorkOrder/u)
- assert.doesNotMatch(source, /updateEquipmentStatus/u)
- })
- test('abnormal detail drawer shows the three independent state sources and related order', async () => {
- const source = await readFile(pagePath, 'utf8')
- assert.match(source, /设备当前状态/u)
- assert.match(source, /设备报警状态/u)
- assert.match(source, /在线状态/u)
- assert.match(source, /关联未办结工单/u)
- assert.match(source, /orderStatusText/u)
- assert.match(
- source,
- /from '@\/views\/subSystem\/pipeNetwork\/ops\/workOrder\/js\/workOrderDict\.js'/u,
- '异常看板应复用统一工单字典'
- )
- assert.doesNotMatch(source, /function\s+workOrderStatusType/u)
- })
- test('abnormal board keeps the backend aggregate for the device indicator band', async () => {
- const source = await readFile(pagePath, 'utf8')
- const statsBlock = source.slice(
- source.indexOf('const abnormalMetrics = computed'),
- source.indexOf('const filteredRows = computed')
- )
- assert.ok(statsBlock, '缺少异常指标带计算块')
- assert.doesNotMatch(statsBlock, /page\.value\.records/u, '指标带不应基于当前分页行计数')
- assert.match(statsBlock, /statistics\.value\.total/u)
- assert.match(statsBlock, /statistics\.value\.offline/u)
- assert.match(statsBlock, /summary\.value\.maintenance/u)
- })
|