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 @@
-
+
+
+ {{ name }}
+
-
-
-
{{ online ? '在线' : '离线' }}
-
+
+
+
+
+
+
-
+
+
+ 离线设备
+
+
+ {{ offlineDeviceCount }}
+
- 台
+ 台
-
+
+
+ 告警记录
+
+
+ {{ alarmCount }}
+
- 条
+ 条
+
+
+
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 @@
-
+
alarm
+ {{ route }}
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 @@
-
+
- device
+
+
+
+
+
+
+ 全部
+ 在线
+ 离线
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 返回
+
+
+
+ {{ lineDeviceTreeData }}
+
+
+
-
+
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 @@
-
+
- log
+ alarm
+ {{ route }}
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 @@
-
+
statistics
+ {{ route }}