some updates

This commit is contained in:
2025-08-12 19:49:22 +08:00
parent 42561b06de
commit a218995d09
6 changed files with 220 additions and 50 deletions

View File

@@ -0,0 +1,8 @@
export * from './ndm-camera';
export * from './ndm-decoder';
export * from './ndm-keyboard';
export * from './ndm-media-server';
export * from './ndm-nvr';
export * from './ndm-security-box';
export * from './ndm-switch';
export * from './ndm-video-server';

View File

@@ -2,15 +2,47 @@ import type { FC } from 'react';
import { AlertOutlined, ApiOutlined } from '@ant-design/icons';
import { Card, Col, Row, Statistic, Tag } from 'antd';
import { useState } from 'react';
import type { NdmCameraResultVO, NdmDecoderResultVO, NdmDeviceAlarmLogResultVO, NdmKeyboardResultVO, NdmMediaServerResultVO, NdmNvrResultVO, NdmSecurityBoxResultVO, NdmSwitchResultVO, NdmVideoServerResultVO } from '@/apis/models/device';
interface StationProps {
name: string;
online: boolean;
offlineDeviceCount: number;
alarmCount: number;
// offlineDeviceCount: number;
// alarmCount: number;
ndmCameraList: NdmCameraResultVO[];
ndmDecoderList: NdmDecoderResultVO[];
ndmKeyboardList: NdmKeyboardResultVO[];
ndmMediaServerList: NdmMediaServerResultVO[];
ndmNvrList: NdmNvrResultVO[];
ndmSecurityBoxList: NdmSecurityBoxResultVO[];
ndmSwitchList: NdmSwitchResultVO[];
ndmVideoServerList: NdmVideoServerResultVO[];
ndmDeviceAlarmLogList: NdmDeviceAlarmLogResultVO[];
}
export const Station: FC<StationProps> = ({ name, online, offlineDeviceCount, alarmCount }) => {
export const Station: FC<StationProps> = ({ name, online, ndmCameraList, ndmDecoderList, ndmKeyboardList, ndmMediaServerList, ndmNvrList, ndmSecurityBoxList, ndmSwitchList, ndmVideoServerList, ndmDeviceAlarmLogList }) => {
const [offlineDeviceCount] = useState(12);
const [alarmCount] = useState(3);
const offlineCameraCount = ndmCameraList.filter(device => device.deviceStatus === '20').length;
const cameraCount = ndmCameraList.length;
const offlineDecoderCount = ndmDecoderList.filter(device => device.deviceStatus === '20').length;
const decoderCount = ndmDecoderList.length;
const offlineKeyboardCount = ndmKeyboardList.filter(device => device.deviceStatus === '20').length;
const keyboardCount = ndmKeyboardList.length;
const offlineMediaServerCount = ndmMediaServerList.filter(device => device.deviceStatus === '20').length;
const mediaServerCount = ndmMediaServerList.length;
const offlineNvrCount = ndmNvrList.filter(device => device.deviceStatus === '20').length;
const nvrCount = ndmNvrList.length;
const offlineSecurityBoxCount = ndmSecurityBoxList.filter(device => device.deviceStatus === '20').length;
const securityBoxCount = ndmSecurityBoxList.length;
const switchOfflineCount = ndmSwitchList.filter(device => device.deviceStatus === '20').length;
const switchCount = ndmSwitchList.length;
const offlineVideoServerCount = ndmVideoServerList.filter(device => device.deviceStatus === '20').length;
const videoServerCount = ndmVideoServerList.length;
return (
<Card
size='small'

View File

@@ -0,0 +1,50 @@
import { useQuery } from '@tanstack/react-query';
import type { Station } from '@/apis/domains';
import type { PageResult } from '@/apis/models/base/page';
import type { DefParameterResultVO } from '@/apis/models/system';
import { ndmClient, userClient } from '@/apis/client';
export function useStationListQuery() {
return useQuery({
queryKey: ['station-list'],
queryFn: async () => {
// 第一步:获取车站基础信息
const [err, data] = await userClient.post<PageResult<DefParameterResultVO>>(`/api/system/defParameter/page`, {
current: 1,
extra: {},
model: {
key: 'NDM_STATION_',
},
order: 'ascending',
size: 100,
sort: 'id',
});
if (err || !data) {
throw err;
}
// 第二步:获取每个车站的在线状态
const stations: Station[] = data.records.map(record => ({
id: record.key ?? '',
code: record.value ?? '',
name: record.name ?? '',
online: false, // 默认离线
}));
// 第三步:并发检查所有车站的在线状态
const pingResultList = await Promise.allSettled(stations.map((station) => {
return ndmClient.post(`/${station.code}/api/ndm/ndmKeepAlive/verify`, {}, { timeout: 5000 });
}));
// 第四步:合并状态信息
const stationsWithStatus: Station[] = stations.map((station, index) => ({
...station,
online: pingResultList[index].status === 'fulfilled',
}));
return stationsWithStatus;
},
});
}

View File

@@ -4,7 +4,10 @@ import type { FC } from 'react';
import { AlertFilled, AreaChartOutlined, DownOutlined, FileTextFilled, HomeFilled, UserOutlined, VideoCameraFilled } from '@ant-design/icons';
import { Link, Outlet } from '@tanstack/react-router';
import { Avatar, Dropdown, Layout, Menu, Space, theme } from 'antd';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { useStationListQuery } from '@/hooks/query/use-stations-query';
import { useStationStore } from '@/store/station';
const { Content, Footer, Header, Sider } = Layout;
@@ -12,6 +15,15 @@ export const AppLayout: FC = () => {
const { token: { colorBgContainer } } = theme.useToken();
const [menuTheme] = useState<MenuTheme>('light');
const { data: stationList } = useStationListQuery();
const { setStationList } = useStationStore();
useEffect(() => {
if (stationList) {
setStationList(stationList);
}
}, [stationList, setStationList]);
const items: MenuProps['items'] = [
{
key: 'logout',

View File

@@ -1,58 +1,105 @@
import { useQuery } from '@tanstack/react-query';
import { createFileRoute, useLocation } from '@tanstack/react-router';
import { createFileRoute } from '@tanstack/react-router';
import { Col, Row } from 'antd';
import { userClient } from '@/apis/client';
import {
postNdmCameraPage,
postNdmDecoderPage,
postNdmKeyboardPage,
postNdmMediaServerPage,
postNdmNvrPage,
postNdmSecurityBoxPage,
postNdmSwitchPage,
postNdmVideoServerPage,
} from '@/apis/requests/device';
import { Station } from '@/components/dashboard/station';
import { useStationStore } from '@/store/station';
export const Route = createFileRoute('/_app/dashboard')({
component: DashboardPage,
});
function DashboardPage() {
// const location = useLocation();
// const { data, isLoading, isFetching, isPending } = useQuery({
// queryKey: ['station'],
// queryFn: async () => {
// const [err, , resp] = await userClient.get<any[]>('/api/users');
// if (err || !resp) {
// throw err;
// }
// console.log(resp);
// return resp;
// },
// });
const stationList = useStationStore(state => state.stationList);
const stations = Array.from({ length: 40 }).map((_, i) => ({
id: i,
name: `测试站点${i + 1}`,
isOnline: true,
offlineDeviceCount: 12,
alarmCount: 3,
}));
const onlineStationList = stationList.filter(station => station.online);
const { data } = useQuery({
queryKey: ['device-list', 'all-type', onlineStationList.map(s => s.code)],
queryFn: async () => {
const pageQuery = { model: {}, extra: {}, size: 5000, current: 1, sort: 'id', order: 'ascending' as const };
const stationDevicePromises = onlineStationList.map(async (station) => {
const deviceRequests = [
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),
];
const results = await Promise.all(deviceRequests);
const [
[, cameraData],
[, decoderData],
[, keyboardData],
[, mediaServerData],
[, nvrData],
[, securityBoxData],
[, switchData],
[, videoServerData],
] = results;
return {
stationCode: station.code,
ndmCameraList: cameraData?.records ?? [],
ndmDecoderList: decoderData?.records ?? [],
ndmKeyboardList: keyboardData?.records ?? [],
ndmMediaServerList: mediaServerData?.records ?? [],
ndmNvrList: nvrData?.records ?? [],
ndmSecurityBoxList: securityBoxData?.records ?? [],
ndmSwitchList: switchData?.records ?? [],
ndmVideoServerList: videoServerData?.records ?? [],
};
});
const allStationsData = await Promise.all(stationDevicePromises);
return allStationsData.reduce((acc, result) => {
acc[result.stationCode] = result;
return acc;
}, {} as Record<string, typeof allStationsData[number]>);
},
enabled: onlineStationList.length > 0,
});
return (
// <div>
// <Station name='测试站点' isOnline={true} offlineDeviceCount={12} alarmCount={3} />
// <div>
// <h2>当前路由信息</h2>
// <pre>{JSON.stringify(location, null, 2)}</pre>
// </div>
// <pre>{JSON.stringify(data, null, 2)}</pre>
// </div>
<div style={{ padding: '6px' }}>
<Row gutter={[6, 6]}>
{stations.map(station => (
<Col key={station.id} span={3}>
<Station
name={station.name}
online={station.isOnline}
offlineDeviceCount={station.offlineDeviceCount}
alarmCount={station.alarmCount}
/>
</Col>
))}
{stationList.map((station) => {
const deviceData = data?.[station.code];
return (
<Col key={station.id} span={3}>
<Station
name={station.name}
online={station.online}
ndmCameraList={deviceData?.ndmCameraList ?? []}
ndmDecoderList={deviceData?.ndmDecoderList ?? []}
ndmKeyboardList={deviceData?.ndmKeyboardList ?? []}
ndmMediaServerList={deviceData?.ndmMediaServerList ?? []}
ndmNvrList={deviceData?.ndmNvrList ?? []}
ndmSecurityBoxList={deviceData?.ndmSecurityBoxList ?? []}
ndmSwitchList={deviceData?.ndmSwitchList ?? []}
ndmVideoServerList={deviceData?.ndmVideoServerList ?? []}
ndmDeviceAlarmLogList={[]}
/>
</Col>
);
})}
</Row>
</div>
);

View File

@@ -1,17 +1,38 @@
import { create } from 'zustand';
import type { Station, StationStatusRecord } from '@/apis/domains';
import type { Station } from '@/apis/domains';
export interface StationState {
stationList: Station[];
stationStatusRecord: StationStatusRecord;
setStationList: (stationList: Station[]) => void;
setStationStatusRecord: (stationStatusRecord: StationStatusRecord) => void;
// 获取在线车站
getOnlineStations: () => Station[];
// 获取离线车站
getOfflineStations: () => Station[];
// 获取指定车站的在线状态
getStationStatus: (stationCode: string) => boolean;
}
export const useStationStore = create<StationState>(set => ({
export const useStationStore = create<StationState>((set, get) => ({
stationList: [],
stationStatusRecord: {},
setStationList: (stationList: Station[]) => set({ stationList }),
setStationStatusRecord: (stationStatusRecord: StationStatusRecord) => set({ stationStatusRecord }),
// 获取在线车站
getOnlineStations: () => {
const { stationList } = get();
return stationList.filter(station => station.online);
},
// 获取离线车站
getOfflineStations: () => {
const { stationList } = get();
return stationList.filter(station => !station.online);
},
// 获取指定车站的在线状态
getStationStatus: (stationCode: string) => {
const { stationList } = get();
const station = stationList.find(s => s.code === stationCode);
return station?.online || false;
},
}));