diff --git a/src/pages/vimp/composables/query/use-device-center-query.ts b/src/pages/vimp/composables/query/use-device-center-query.ts index 5db29e9..0b27ead 100644 --- a/src/pages/vimp/composables/query/use-device-center-query.ts +++ b/src/pages/vimp/composables/query/use-device-center-query.ts @@ -55,8 +55,8 @@ export const useDeviceCenterQuery = () => { const alarmSites: VimpStation[] = []; const cameraBuiltSitesSet = new Set(); const alarmBuiltSitesSet = new Set(); - const siteCamerasMap = new Map(); - const siteAlarmsMap = new Map(); + const siteCodeToCamerasMap = new Map(); + const siteCodeToAlarmsMap = new Map(); for (const siteFromApi of sitesFromApi ?? []) { const channels = await catalogChannelApi(siteFromApi.code, { signal }); @@ -76,10 +76,10 @@ export const useDeviceCenterQuery = () => { }); cameraBuiltSitesSet.add(siteCode); } - if (!siteCamerasMap.has(siteCode)) { - siteCamerasMap.set(siteCode, []); + if (!siteCodeToCamerasMap.has(siteCode)) { + siteCodeToCamerasMap.set(siteCode, []); } - siteCamerasMap.get(siteCode)!.push(channel); + siteCodeToCamerasMap.get(siteCode)!.push(channel); } else if (isAlarm) { if (!alarmBuiltSitesSet.has(siteCode)) { alarmSites.push({ @@ -89,23 +89,23 @@ export const useDeviceCenterQuery = () => { }); alarmBuiltSitesSet.add(siteCode); } - if (!siteAlarmsMap.has(siteCode)) { - siteAlarmsMap.set(siteCode, []); + if (!siteCodeToAlarmsMap.has(siteCode)) { + siteCodeToAlarmsMap.set(siteCode, []); } - siteAlarmsMap.get(siteCode)!.push(channel); + siteCodeToAlarmsMap.get(siteCode)!.push(channel); } }); } console.log(new Date().toLocaleString()); console.log(cameraSites); - console.log(siteCamerasMap); + console.log(siteCodeToCamerasMap); console.log(alarmSites); - console.log(siteAlarmsMap); + console.log(siteCodeToAlarmsMap); cameraStore.buildLineTabPanes({ sites: cameraSites, - siteCamerasMap, + siteCodeToCamerasMap: siteCodeToCamerasMap, codeLines, codeSites, codeStationAreas, @@ -116,7 +116,7 @@ export const useDeviceCenterQuery = () => { alarmStore.buildLineTabPanes({ sites: alarmSites, - siteAlarmsMap, + siteCodeToAlarmsMap, codeLines, codeSites, codeStationAreas, diff --git a/src/pages/vimp/stores/alarm.ts b/src/pages/vimp/stores/alarm.ts index 4143e65..7fc35a1 100644 --- a/src/pages/vimp/stores/alarm.ts +++ b/src/pages/vimp/stores/alarm.ts @@ -7,7 +7,7 @@ import { SirenIcon } from 'lucide-vue-next'; interface BuildLineTabPanesParams { sites: VimpStation[]; - siteAlarmsMap: Map; + siteCodeToAlarmsMap: Map; codeLines: CodeLines; codeSites: CodeSites; codeStationAreas: CodeArea[]; @@ -20,7 +20,7 @@ export const useAlarmStore = defineStore('vimp-alarm-store', () => { const lineTabPanes = shallowRef([]); const buildLineTabPanes = (params: BuildLineTabPanesParams) => { - const { sites, siteAlarmsMap, codeLines, codeSites, codeStationAreas, codeParkingAreas, codeOccAreas, codeTrainAreas } = params; + const { sites, siteCodeToAlarmsMap, codeLines, codeSites, codeStationAreas, codeParkingAreas, codeOccAreas, codeTrainAreas } = params; // 线路TabPane const _lineTabPanes: AlarmLineTabPane[] = []; @@ -52,7 +52,7 @@ export const useAlarmStore = defineStore('vimp-alarm-store', () => { _lineTabPanes.find((lineTabPane) => lineTabPane.lineCode === lineCode)?.alarmTree.push(siteNode); // 获取所有警报器 - const alarms = siteAlarmsMap.get(siteCode); + const alarms = siteCodeToAlarmsMap.get(siteCode); if (!alarms || alarms.length === 0) continue; // 遍历警报器 diff --git a/src/pages/vimp/stores/camera.ts b/src/pages/vimp/stores/camera.ts index ddd2591..ec504f2 100644 --- a/src/pages/vimp/stores/camera.ts +++ b/src/pages/vimp/stores/camera.ts @@ -9,7 +9,7 @@ import HemiPtzCamera from '../components/icon/hemi-ptz-camera.vue'; interface BuildLineTabPanesParams { sites: VimpStation[]; - siteCamerasMap: Map; + siteCodeToCamerasMap: Map; codeLines: CodeLines; codeSites: CodeSites; codeStationAreas: CodeArea[]; @@ -18,146 +18,52 @@ interface BuildLineTabPanesParams { codeTrainAreas: CodeArea[]; } +const buildMainAreaNodeKey = (siteCode: string, mainAreaCode: string) => `${siteCode}${mainAreaCode}`; +const buildSubAreaNodeKey = (siteCode: string, areaCode: string) => `${siteCode}${areaCode}`; +const renderCameraNodePrefix = (cameraType: string) => { + if (cameraType === '004') return h(NIcon, () => h(PtzCamera)); + if (cameraType === '005') return h(NIcon, () => h(HemiPtzCamera)); + if (cameraType === '006') return h(NIcon, () => h(BulletCamera)); + return undefined; +}; + export const useCameraStore = defineStore('vimp-camera-store', () => { const lineTabPanes = shallowRef([]); const buildLineTabPanes = (params: BuildLineTabPanesParams) => { - const { sites, siteCamerasMap, codeLines, codeSites, codeStationAreas, codeParkingAreas, codeOccAreas, codeTrainAreas } = params; + const { sites, siteCodeToCamerasMap, codeLines, codeSites, codeStationAreas, codeParkingAreas, codeOccAreas, codeTrainAreas } = params; - // 线路TabPane - const _lineTabPanes: CameraLineTabPane[] = []; - - // 遍历所有站点 - for (const site of sites) { - const lineCode = site.code.substring(0, 3); - const lineName = codeLines[lineCode]?.name ?? ''; - if (!_lineTabPanes.some((lineNode) => lineNode.lineCode === lineCode)) { - _lineTabPanes.push({ - lineCode, - lineName, - cameraTree: [], - }); + const findMainArea = (siteType: CodeSites[string]['type'] | undefined, mainAreaCode: string) => { + if (siteType === 'station') { + return codeStationAreas.find((area) => area.code === mainAreaCode); } - - const siteCode = site.code; - const siteName = codeSites[siteCode]?.name; - if (!siteName) continue; - - // 构造站点节点 - const siteNode: CameraSiteNodeOption = { - key: siteCode, - label: siteName, - children: [], - stats: { online: 0, offline: 0, total: 0 }, - online: site.online, - }; - _lineTabPanes.find((lineTabPane) => lineTabPane.lineCode === lineCode)?.cameraTree.push(siteNode); - - // 获取所有摄像机 - const cameras = siteCamerasMap.get(siteCode); - if (!cameras || cameras.length === 0) continue; - - // 遍历摄像机 - for (const camera of cameras) { - // 计算相关编码 - const { code: cameraGbCode, name: cameraName } = camera; - const cameraSiteCode = cameraGbCode.substring(0, 6); - const cameraSiteType = codeSites[cameraSiteCode]?.type; - const cameraAreaCode = cameraGbCode.substring(6, 11); - const cameraMainAreaCode = cameraAreaCode.slice(0, cameraSiteType === 'train' ? 3 : 2); - - // 构造车站/基地/OCC/车次区域 - let siteArea: CodeArea | undefined = undefined; - if (cameraSiteType === 'station') { - siteArea = codeStationAreas.find((area) => area.code === cameraMainAreaCode); - } else if (cameraSiteType === 'parking') { - siteArea = codeParkingAreas.find((area) => area.code === cameraMainAreaCode); - } else if (cameraSiteType === 'occ') { - siteArea = codeOccAreas.find((area) => area.code === cameraMainAreaCode); - } else if (cameraSiteType === 'train') { - siteArea = codeTrainAreas.find((area) => area.code === cameraMainAreaCode); - } else { - continue; - } - if (!siteArea) continue; // 如果还是未找到区域,则跳过该摄像机 - - // 构造1级区域节点 - if (!siteNode.children?.find((areaNode) => areaNode.key === `${cameraSiteCode}${cameraMainAreaCode}`)) { - const mainAreaNode: CameraMainAreaNodeOption = { - key: `${cameraSiteCode}${cameraMainAreaCode}`, - label: siteArea.name, - children: [], - stats: { online: 0, offline: 0, total: 0 }, - site: site, - }; - siteNode.children?.push(mainAreaNode); - } - const targetMainAreaNode = siteNode.children?.find((areaNode) => areaNode.key === `${cameraSiteCode}${cameraMainAreaCode}`); - if (!targetMainAreaNode) continue; // 如果1级区域节点不存在,则跳过该摄像机 - - // 构造2级区域节点 - if (!targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`)) { - let subArea: CodeArea['subs'][number] | undefined = undefined; - if (cameraSiteType === 'station') { - subArea = codeStationAreas.find((area) => area.code === cameraMainAreaCode)?.subs.find((subArea) => subArea.code === cameraAreaCode); - } else if (cameraSiteType === 'parking') { - subArea = codeParkingAreas.find((area) => area.code === cameraMainAreaCode)?.subs.find((subArea) => subArea.code === cameraAreaCode); - } else if (cameraSiteType === 'occ') { - subArea = codeOccAreas.find((area) => area.code === cameraMainAreaCode)?.subs.find((subArea) => subArea.code === cameraAreaCode); - } else if (cameraSiteType === 'train') { - subArea = codeTrainAreas.find((area) => area.code === cameraMainAreaCode)?.subs.find((subArea) => subArea.code === cameraAreaCode); - } else { - continue; - } - if (!subArea) continue; // 如果还是未找到2级区域,则跳过该摄像机 - - const subAreaNode: CameraSubAreaNodeOption = { - key: `${cameraSiteCode}${cameraAreaCode}`, - label: subArea.name, - children: [], - stats: { online: 0, offline: 0, total: 0 }, - site: site, - }; - targetMainAreaNode.children?.push(subAreaNode); - } - const subAreaNode = targetMainAreaNode.children?.find((subAreaNode) => subAreaNode.key === `${cameraSiteCode}${cameraAreaCode}`); - if (!subAreaNode) continue; // 如果2级区域节点不存在,则跳过该摄像机 - - // 构造摄像机节点 - const cameraType = camera.code.substring(11, 14); - const cameraNode: CameraNodeOption = { - key: cameraGbCode, - label: cameraName, - type: cameraType, - camera: camera, - site: site, - prefix: () => { - if (cameraType === '004') return h(NIcon, () => h(PtzCamera)); - if (cameraType === '005') return h(NIcon, () => h(HemiPtzCamera)); - if (cameraType === '006') return h(NIcon, () => h(BulletCamera)); - }, - }; - - // 添加摄像机节点到子区域节点 - if (!subAreaNode.children?.find((cameraNode) => cameraNode.key === cameraGbCode)) { - subAreaNode.children?.push(cameraNode); - } - - // 统计站点、区域、子区域的在线/离线/总摄像机数量 - siteNode.stats.total++; - targetMainAreaNode.stats.total++; - subAreaNode.stats.total++; - if (camera.status === 1) { - siteNode.stats.online++; - targetMainAreaNode.stats.online++; - subAreaNode.stats.online++; - } - if (camera.status === 0) { - siteNode.stats.offline++; - targetMainAreaNode.stats.offline++; - subAreaNode.stats.offline++; - } + if (siteType === 'parking') { + return codeParkingAreas.find((area) => area.code === mainAreaCode); } + if (siteType === 'occ') { + return codeOccAreas.find((area) => area.code === mainAreaCode); + } + if (siteType === 'train') { + return codeTrainAreas.find((area) => area.code === mainAreaCode); + } + return undefined; + }; + const findSubArea = (siteType: CodeSites[string]['type'] | undefined, mainAreaCode: string, areaCode: string) => { + if (siteType === 'station') { + return codeStationAreas.find((area) => area.code === mainAreaCode)?.subs.find((subArea) => subArea.code === areaCode); + } + if (siteType === 'parking') { + return codeParkingAreas.find((area) => area.code === mainAreaCode)?.subs.find((subArea) => subArea.code === areaCode); + } + if (siteType === 'occ') { + return codeOccAreas.find((area) => area.code === mainAreaCode)?.subs.find((subArea) => subArea.code === areaCode); + } + if (siteType === 'train') { + return codeTrainAreas.find((area) => area.code === mainAreaCode)?.subs.find((subArea) => subArea.code === areaCode); + } + return undefined; + }; + const renderCascadedSuffix = (siteNode: CameraSiteNodeOption) => { siteNode.suffix = () => { const { online, offline, total } = siteNode.stats; return `(${online}/${offline}/${total})`; @@ -174,8 +80,138 @@ export const useCameraStore = defineStore('vimp-camera-store', () => { }; }); }); + }; + + const result: CameraLineTabPane[] = []; + + // 1. 线路索引 lineCode -> CameraLineTabPane + const linePaneMap = new Map(); + + // 遍历所有站点 + for (const site of sites) { + // 2. 站点节点 siteNode 不需要建立索引 + + const lineCode = site.code.substring(0, 3); + const lineName = codeLines[lineCode]?.name ?? ''; + + let linePane = linePaneMap.get(lineCode); + if (!linePane) { + linePane = { lineCode, lineName, cameraTree: [] }; + linePaneMap.set(lineCode, linePane); + result.push(linePane); + } + + const siteCode = site.code; + const siteName = codeSites[siteCode]?.name; + if (!siteName) continue; + + // 构造站点节点 + const siteNode: CameraSiteNodeOption = { + key: siteCode, + label: siteName, + children: [], + stats: { online: 0, offline: 0, total: 0 }, + online: site.online, + }; + linePane.cameraTree.push(siteNode); + + // 获取所有摄像机 + const cameras = siteCodeToCamerasMap.get(siteCode); + if (!cameras) continue; + + // 3. 1级区域索引 mainAreaNodeKey -> CameraMainAreaNodeOption + // mainAreaNodeKey = ${siteCode}${cameraMainAreaCode} + const mainAreaNodeMap = new Map(); + // 4. 2级区域索引 subAreaNodeKey -> CameraSubAreaNodeOption + // subAreaNodeKey = ${siteCode}${cameraAreaCode} + const subAreaNodeMap = new Map(); + // 5. 摄像机索引 subAreaNodeKey -> Set + const subAreaNodeKeyToCameraGbCodeSetMap = new Map>(); + + // 遍历摄像机 + for (const camera of cameras) { + // 计算相关编码 + const { code: cameraGbCode, name: cameraName } = camera; + const cameraSiteCode = cameraGbCode.substring(0, 6); + const cameraSiteType = codeSites[cameraSiteCode]?.type; + const cameraAreaCode = cameraGbCode.substring(6, 11); + const cameraMainAreaCode = cameraAreaCode.slice(0, cameraSiteType === 'train' ? 3 : 2); + + // 查找1级区域,如果未找到则跳过该摄像机 + const mainArea = findMainArea(cameraSiteType, cameraMainAreaCode); + if (!mainArea) continue; + // 尝试从索引中获取1级区域节点,若不存在则创建 + const mainAreaNodeKey = buildMainAreaNodeKey(cameraSiteCode, cameraMainAreaCode); + let mainAreaNode = mainAreaNodeMap.get(mainAreaNodeKey); + if (!mainAreaNode) { + mainAreaNode = { + key: mainAreaNodeKey, + label: mainArea.name, + children: [], + stats: { online: 0, offline: 0, total: 0 }, + site: site, + }; + mainAreaNodeMap.set(mainAreaNodeKey, mainAreaNode); + siteNode.children?.push(mainAreaNode); + } + + // 查找2级区域,如果未找到则跳过该摄像机 + const subArea = findSubArea(cameraSiteType, cameraMainAreaCode, cameraAreaCode); + if (!subArea) continue; + // 尝试从索引中获取2级区域节点,若不存在则创建 + const subAreaNodeKey = buildSubAreaNodeKey(cameraSiteCode, cameraAreaCode); + let subAreaNode = subAreaNodeMap.get(subAreaNodeKey); + if (!subAreaNode) { + subAreaNode = { + key: subAreaNodeKey, + label: subArea.name, + children: [], + stats: { online: 0, offline: 0, total: 0 }, + site: site, + }; + subAreaNodeMap.set(subAreaNodeKey, subAreaNode); + mainAreaNode.children?.push(subAreaNode); + } + + // 构造摄像机节点 + let cameraGbCodeSet = subAreaNodeKeyToCameraGbCodeSetMap.get(subAreaNodeKey); + if (!cameraGbCodeSet) { + cameraGbCodeSet = new Set(); + subAreaNodeKeyToCameraGbCodeSetMap.set(subAreaNodeKey, cameraGbCodeSet); + } + if (cameraGbCodeSet.has(cameraGbCode)) continue; + cameraGbCodeSet.add(cameraGbCode); + const cameraType = camera.code.substring(11, 14); + const cameraNode: CameraNodeOption = { + key: cameraGbCode, + label: cameraName, + type: cameraType, + camera: camera, + site: site, + prefix: () => renderCameraNodePrefix(cameraType), + }; + subAreaNode.children?.push(cameraNode); + + // 统计站点、区域、子区域的在线/离线/总摄像机数量 + siteNode.stats.total++; + mainAreaNode.stats.total++; + subAreaNode.stats.total++; + if (camera.status === 1) { + siteNode.stats.online++; + mainAreaNode.stats.online++; + subAreaNode.stats.online++; + } + if (camera.status === 0) { + siteNode.stats.offline++; + mainAreaNode.stats.offline++; + subAreaNode.stats.offline++; + } + } + + renderCascadedSuffix(siteNode); } - lineTabPanes.value = _lineTabPanes; + + lineTabPanes.value = result; }; return {