From 8c8a79140928bb887fd79f4a95ad0b3f3c23b5ab Mon Sep 17 00:00:00 2001 From: yangsy Date: Sun, 17 Aug 2025 01:22:08 +0800 Subject: [PATCH] feat: ui --- src/components/device-alarm-stat-modal.vue | 7 + src/components/offline-device-tree-modal.vue | 148 +++++++++++++++++ src/components/station-card.vue | 131 ++++++++++++--- .../query/use-line-alarms-query.ts | 18 +++ .../query/use-line-devices-query.ts | 150 ++++++++++++++++++ .../query/use-station-list-query.ts | 20 ++- src/enums/device-type.ts | 43 ++--- src/layouts/app-layout.vue | 2 +- src/pages/alarm-page.vue | 7 +- src/pages/dashboard-page.vue | 39 +++-- src/pages/device-page.vue | 144 ++++++++++++++++- src/pages/log-page.vue | 9 +- src/pages/statistics-page.vue | 7 +- 13 files changed, 663 insertions(+), 62 deletions(-) create mode 100644 src/components/device-alarm-stat-modal.vue create mode 100644 src/components/offline-device-tree-modal.vue create mode 100644 src/composables/query/use-line-alarms-query.ts create mode 100644 src/composables/query/use-line-devices-query.ts diff --git a/src/components/device-alarm-stat-modal.vue b/src/components/device-alarm-stat-modal.vue new file mode 100644 index 0000000..1017376 --- /dev/null +++ b/src/components/device-alarm-stat-modal.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/src/components/offline-device-tree-modal.vue b/src/components/offline-device-tree-modal.vue new file mode 100644 index 0000000..6610c88 --- /dev/null +++ b/src/components/offline-device-tree-modal.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/components/station-card.vue b/src/components/station-card.vue index a81a377..a98f9c4 100644 --- a/src/components/station-card.vue +++ b/src/components/station-card.vue @@ -1,54 +1,147 @@ diff --git a/src/composables/query/use-line-alarms-query.ts b/src/composables/query/use-line-alarms-query.ts new file mode 100644 index 0000000..b789fef --- /dev/null +++ b/src/composables/query/use-line-alarms-query.ts @@ -0,0 +1,18 @@ +import { useStationStore } from '@/stores/station'; +import { useQuery } from '@tanstack/vue-query'; +import { storeToRefs } from 'pinia'; +import { computed } from 'vue'; + +export function useLineDevicesQuery() { + const stationStore = useStationStore(); + const { updatedTime, stationList, onlineStationList } = storeToRefs(stationStore); + + return useQuery({ + queryKey: ['line-devices', updatedTime], + enabled: computed(() => onlineStationList.value.length > 0), + queryFn: async () => { + if (!stationList?.value) { + } + }, + }); +} diff --git a/src/composables/query/use-line-devices-query.ts b/src/composables/query/use-line-devices-query.ts new file mode 100644 index 0000000..a2e6df3 --- /dev/null +++ b/src/composables/query/use-line-devices-query.ts @@ -0,0 +1,150 @@ +import { useQuery } from '@tanstack/vue-query'; +import type { PageParams } from '@/apis/models/base/page'; +import { + postNdmCameraPage, + postNdmDecoderPage, + postNdmKeyboardPage, + postNdmMediaServerPage, + postNdmNvrPage, + postNdmSecurityBoxPage, + postNdmSwitchPage, + postNdmVideoServerPage, +} from '@/apis/requests/device'; +import { useStationStore } from '@/stores/station'; +import { storeToRefs } from 'pinia'; +import { computed } from 'vue'; +import { DeviceType } from '@/enums/device-type'; +import type { + NdmCameraResultVO, + NdmDecoderResultVO, + NdmKeyboardResultVO, + NdmMediaServerResultVO, + NdmNvrResultVO, + NdmSecurityBoxResultVO, + NdmSwitchResultVO, + NdmVideoServerResultVO, +} from '@/apis/models/device'; + +// 定义设备数据类型 +interface StationDevices { + // ndmCameraList: any[]; + // ndmDecoderList: any[]; + // ndmKeyboardList: any[]; + // ndmMedisServerList: any[]; + // ndmNvrList: any[]; + // ndmSecurityBoxList: any[]; + // ndmSwitchList: any[]; + // ndmVideoServerList: any[]; + [DeviceType.Camera]: NdmCameraResultVO[]; + [DeviceType.Decoder]: NdmDecoderResultVO[]; + [DeviceType.Keyboard]: NdmKeyboardResultVO[]; + [DeviceType.MediaServer]: NdmMediaServerResultVO[]; + [DeviceType.Nvr]: NdmNvrResultVO[]; + [DeviceType.SecurityBox]: NdmSecurityBoxResultVO[]; + [DeviceType.Switch]: NdmSwitchResultVO[]; + [DeviceType.VideoServer]: NdmVideoServerResultVO[]; +} + +export function useLineDevicesQuery() { + const stationStore = useStationStore(); + const { updatedTime, stationList, onlineStationList } = storeToRefs(stationStore); + + return useQuery({ + queryKey: ['line-devices', updatedTime], + enabled: computed(() => onlineStationList.value.length > 0), + placeholderData: (prev) => prev, + queryFn: async (): Promise> => { + const pageQuery: PageParams<{}> = { model: {}, extra: {}, size: 5000, current: 1, sort: 'id', order: 'ascending' }; + + const lineDevices: Record = {}; + + // 如果没有车站列表,返回空对象 + if (!stationList?.value) { + return lineDevices; + } + + // 遍历所有车站 + for (const station of stationList.value) { + // 如果车站离线,设置空数组 + if (!station.online) { + lineDevices[station.code] = { + // ndmCameraList: [], + // ndmDecoderList: [], + // ndmKeyboardList: [], + // ndmMedisServerList: [], + // ndmNvrList: [], + // ndmSecurityBoxList: [], + // ndmSwitchList: [], + // ndmVideoServerList: [], + [DeviceType.Camera]: [], + [DeviceType.Decoder]: [], + [DeviceType.Keyboard]: [], + [DeviceType.MediaServer]: [], + [DeviceType.Nvr]: [], + [DeviceType.SecurityBox]: [], + [DeviceType.Switch]: [], + [DeviceType.VideoServer]: [], + }; + continue; + } + + try { + // 并行获取该车站的所有设备类型数据 + const [cameraData, decoderData, keyboardData, mediaServerData, nvrData, securityBoxData, switchData, videoServerData] = await Promise.all([ + postNdmCameraPage(station.code, pageQuery), + postNdmDecoderPage(station.code, pageQuery), + postNdmKeyboardPage(station.code, pageQuery), + postNdmMediaServerPage(station.code, pageQuery), + postNdmNvrPage(station.code, pageQuery), + postNdmSecurityBoxPage(station.code, pageQuery), + postNdmSwitchPage(station.code, pageQuery), + postNdmVideoServerPage(station.code, pageQuery), + ]); + + // 存储该车站的设备数据 + lineDevices[station.code] = { + // ndmCameraList: cameraData.records ?? [], + // ndmDecoderList: decoderData.records ?? [], + // ndmKeyboardList: keyboardData.records ?? [], + // ndmMedisServerList: mediaServerData.records ?? [], + // ndmNvrList: nvrData.records ?? [], + // ndmSecurityBoxList: securityBoxData.records ?? [], + // ndmSwitchList: switchData.records ?? [], + // ndmVideoServerList: videoServerData.records ?? [], + [DeviceType.Camera]: cameraData.records ?? [], + [DeviceType.Decoder]: decoderData.records ?? [], + [DeviceType.Keyboard]: keyboardData.records ?? [], + [DeviceType.MediaServer]: mediaServerData.records ?? [], + [DeviceType.Nvr]: nvrData.records ?? [], + [DeviceType.SecurityBox]: securityBoxData.records ?? [], + [DeviceType.Switch]: switchData.records ?? [], + [DeviceType.VideoServer]: videoServerData.records ?? [], + }; + } catch (error) { + console.error(`获取车站 ${station.name} 设备数据失败:`, error); + // 如果获取失败,设置空数组 + lineDevices[station.code] = { + // ndmCameraList: [], + // ndmDecoderList: [], + // ndmKeyboardList: [], + // ndmMedisServerList: [], + // ndmNvrList: [], + // ndmSecurityBoxList: [], + // ndmSwitchList: [], + // ndmVideoServerList: [], + [DeviceType.Camera]: [], + [DeviceType.Decoder]: [], + [DeviceType.Keyboard]: [], + [DeviceType.MediaServer]: [], + [DeviceType.Nvr]: [], + [DeviceType.SecurityBox]: [], + [DeviceType.Switch]: [], + [DeviceType.VideoServer]: [], + }; + } + } + + return lineDevices; + }, + }); +} diff --git a/src/composables/query/use-station-list-query.ts b/src/composables/query/use-station-list-query.ts index a5306ea..7bcbdff 100644 --- a/src/composables/query/use-station-list-query.ts +++ b/src/composables/query/use-station-list-query.ts @@ -5,29 +5,39 @@ import { useStationStore } from '@/stores/station'; import axios from 'axios'; import dayjs from 'dayjs'; import { storeToRefs } from 'pinia'; +import { getAppEnvConfig } from '@/utils/env'; +import { useQueryControlStore } from '@/stores/query-control'; +import { computed } from 'vue'; export function useStationListQuery() { const stationStore = useStationStore(); - const { stationList } = storeToRefs(stationStore); - useQuery({ + const { updatedTime, stationList } = storeToRefs(stationStore); + const queryControlStore = useQueryControlStore(); + const { pollingEnabled } = storeToRefs(queryControlStore); + + return useQuery({ queryKey: ['station-list'], + enabled: computed(() => pollingEnabled.value), queryFn: async () => { const { data: ndmStationList } = await axios.get<{ code: string; name: string }[]>(`/minio/ndm/ndm-stations.json?_t=${dayjs().unix()}`); - stationList.value = ndmStationList.map((record) => ({ + const stations = ndmStationList.map((record) => ({ code: record.code ?? '', name: record.name ?? '', online: false, })); - const pingResultList = await Promise.allSettled(stationList.value.map((station) => ndmVerify(station.code))); + const pingResultList = await Promise.allSettled(stations.map((station) => ndmVerify(station.code))); - stationList.value = stationList.value.map((station, index) => ({ + stationList.value = stations.map((station, index) => ({ ...station, online: pingResultList[index].status === 'fulfilled', })); + updatedTime.value = dayjs().toJSON(); + return stationList.value; }, + refetchInterval: getAppEnvConfig().requestInterval * 1000, }); } diff --git a/src/enums/device-type.ts b/src/enums/device-type.ts index ff055c2..fbe7e77 100644 --- a/src/enums/device-type.ts +++ b/src/enums/device-type.ts @@ -1,22 +1,23 @@ -export enum DeviceType { - Switch = '220', - Server = '401+403', - MediaServer = '403', - VideoServer = '401', - Decoder = '114', - Camera = '132', - Nvr = '118', - SecurityBox = '222', - Keyboard = '141', -} +export const DeviceType = { + Camera: '132', + Decoder: '114', + Keyboard: '141', + MediaServer: '403', + Nvr: '118', + SecurityBox: '222', + Switch: '220', + VideoServer: '401', +} as const; -export const deviceTypesMap = new Map(); -deviceTypesMap.set(DeviceType.Switch, '交换机'); -deviceTypesMap.set(DeviceType.Server, '服务器'); -deviceTypesMap.set(DeviceType.MediaServer, '媒体服务器'); -deviceTypesMap.set(DeviceType.VideoServer, '视频服务器'); -deviceTypesMap.set(DeviceType.Decoder, '解码器'); -deviceTypesMap.set(DeviceType.Camera, '摄像机'); -deviceTypesMap.set(DeviceType.Nvr, '网络录像机'); -deviceTypesMap.set(DeviceType.SecurityBox, '智能安防箱'); -deviceTypesMap.set(DeviceType.Keyboard, '网络键盘'); +export type DeviceTypeCode = keyof typeof DeviceType; + +export const DeviceTypeName = { + [DeviceType.Camera]: '摄像机', + [DeviceType.Decoder]: '解码器', + [DeviceType.Keyboard]: '网络键盘', + [DeviceType.MediaServer]: '媒体服务器', + [DeviceType.Nvr]: '网络录像机', + [DeviceType.SecurityBox]: '智能安防箱', + [DeviceType.Switch]: '交换机', + [DeviceType.VideoServer]: '视频服务器', +}; diff --git a/src/layouts/app-layout.vue b/src/layouts/app-layout.vue index 9d44c8f..27b386a 100644 --- a/src/layouts/app-layout.vue +++ b/src/layouts/app-layout.vue @@ -107,7 +107,7 @@ const selectDropdownOption = (key: string, option: DropdownOption) => { - + diff --git a/src/pages/alarm-page.vue b/src/pages/alarm-page.vue index 25a8705..c6b9f2c 100644 --- a/src/pages/alarm-page.vue +++ b/src/pages/alarm-page.vue @@ -1,7 +1,12 @@ - + diff --git a/src/pages/dashboard-page.vue b/src/pages/dashboard-page.vue index 5620871..53fa81c 100644 --- a/src/pages/dashboard-page.vue +++ b/src/pages/dashboard-page.vue @@ -2,28 +2,49 @@ import { ref } from 'vue'; import StationCard from '@/components/station-card.vue'; import { NGrid, NGi } from 'naive-ui'; -import { useQuery } from '@tanstack/vue-query'; +import { useStationStore } from '@/stores/station'; +import { storeToRefs } from 'pinia'; +import { DeviceType } from '@/enums/device-type'; +import { useLineDevicesQuery } from '@/composables/query/use-line-devices-query'; + +const stationStore = useStationStore(); +const { stationList } = storeToRefs(stationStore); // 模拟数据 const stations = ref( Array.from({ length: 32 }).map((_, i) => ({ - name: `车站 ${i + 1}`, + code: `${i}`, + name: `浦东1号2号航站楼${i + 1}`, online: Math.random() > 0.5, offlineDeviceCount: Math.floor(Math.random() * 20), alarmCount: Math.floor(Math.random() * 10), })), ); -// const query = useQuery({ -// queryKey: ['line-devices'], -// queryFn: async () => {}, -// }); +// 获取所有在线车站的设备数据 +const { data: lineDevices } = useLineDevicesQuery(); diff --git a/src/pages/device-page.vue b/src/pages/device-page.vue index fc884c4..edcc198 100644 --- a/src/pages/device-page.vue +++ b/src/pages/device-page.vue @@ -1,7 +1,145 @@ - + - + diff --git a/src/pages/log-page.vue b/src/pages/log-page.vue index 7edc285..c6b9f2c 100644 --- a/src/pages/log-page.vue +++ b/src/pages/log-page.vue @@ -1,7 +1,12 @@ - + diff --git a/src/pages/statistics-page.vue b/src/pages/statistics-page.vue index 1683e02..c8a288b 100644 --- a/src/pages/statistics-page.vue +++ b/src/pages/statistics-page.vue @@ -1,7 +1,12 @@ - +