some updates
This commit is contained in:
8
src/apis/requests/device/index.ts
Normal file
8
src/apis/requests/device/index.ts
Normal 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';
|
||||||
@@ -2,15 +2,47 @@ import type { FC } from 'react';
|
|||||||
|
|
||||||
import { AlertOutlined, ApiOutlined } from '@ant-design/icons';
|
import { AlertOutlined, ApiOutlined } from '@ant-design/icons';
|
||||||
import { Card, Col, Row, Statistic, Tag } from 'antd';
|
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 {
|
interface StationProps {
|
||||||
name: string;
|
name: string;
|
||||||
online: boolean;
|
online: boolean;
|
||||||
offlineDeviceCount: number;
|
// offlineDeviceCount: number;
|
||||||
alarmCount: 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 (
|
return (
|
||||||
<Card
|
<Card
|
||||||
size='small'
|
size='small'
|
||||||
|
|||||||
50
src/hooks/query/use-stations-query.ts
Normal file
50
src/hooks/query/use-stations-query.ts
Normal 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;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -4,7 +4,10 @@ import type { FC } from 'react';
|
|||||||
import { AlertFilled, AreaChartOutlined, DownOutlined, FileTextFilled, HomeFilled, UserOutlined, VideoCameraFilled } from '@ant-design/icons';
|
import { AlertFilled, AreaChartOutlined, DownOutlined, FileTextFilled, HomeFilled, UserOutlined, VideoCameraFilled } from '@ant-design/icons';
|
||||||
import { Link, Outlet } from '@tanstack/react-router';
|
import { Link, Outlet } from '@tanstack/react-router';
|
||||||
import { Avatar, Dropdown, Layout, Menu, Space, theme } from 'antd';
|
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;
|
const { Content, Footer, Header, Sider } = Layout;
|
||||||
|
|
||||||
@@ -12,6 +15,15 @@ export const AppLayout: FC = () => {
|
|||||||
const { token: { colorBgContainer } } = theme.useToken();
|
const { token: { colorBgContainer } } = theme.useToken();
|
||||||
const [menuTheme] = useState<MenuTheme>('light');
|
const [menuTheme] = useState<MenuTheme>('light');
|
||||||
|
|
||||||
|
const { data: stationList } = useStationListQuery();
|
||||||
|
const { setStationList } = useStationStore();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (stationList) {
|
||||||
|
setStationList(stationList);
|
||||||
|
}
|
||||||
|
}, [stationList, setStationList]);
|
||||||
|
|
||||||
const items: MenuProps['items'] = [
|
const items: MenuProps['items'] = [
|
||||||
{
|
{
|
||||||
key: 'logout',
|
key: 'logout',
|
||||||
|
|||||||
@@ -1,58 +1,105 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
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 { 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 { Station } from '@/components/dashboard/station';
|
||||||
|
import { useStationStore } from '@/store/station';
|
||||||
|
|
||||||
export const Route = createFileRoute('/_app/dashboard')({
|
export const Route = createFileRoute('/_app/dashboard')({
|
||||||
component: DashboardPage,
|
component: DashboardPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
function DashboardPage() {
|
function DashboardPage() {
|
||||||
// const location = useLocation();
|
const stationList = useStationStore(state => state.stationList);
|
||||||
// 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 stations = Array.from({ length: 40 }).map((_, i) => ({
|
const onlineStationList = stationList.filter(station => station.online);
|
||||||
id: i,
|
|
||||||
name: `测试站点${i + 1}`,
|
const { data } = useQuery({
|
||||||
isOnline: true,
|
queryKey: ['device-list', 'all-type', onlineStationList.map(s => s.code)],
|
||||||
offlineDeviceCount: 12,
|
queryFn: async () => {
|
||||||
alarmCount: 3,
|
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 (
|
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' }}>
|
<div style={{ padding: '6px' }}>
|
||||||
<Row gutter={[6, 6]}>
|
<Row gutter={[6, 6]}>
|
||||||
{stations.map(station => (
|
{stationList.map((station) => {
|
||||||
|
const deviceData = data?.[station.code];
|
||||||
|
return (
|
||||||
<Col key={station.id} span={3}>
|
<Col key={station.id} span={3}>
|
||||||
<Station
|
<Station
|
||||||
name={station.name}
|
name={station.name}
|
||||||
online={station.isOnline}
|
online={station.online}
|
||||||
offlineDeviceCount={station.offlineDeviceCount}
|
ndmCameraList={deviceData?.ndmCameraList ?? []}
|
||||||
alarmCount={station.alarmCount}
|
ndmDecoderList={deviceData?.ndmDecoderList ?? []}
|
||||||
|
ndmKeyboardList={deviceData?.ndmKeyboardList ?? []}
|
||||||
|
ndmMediaServerList={deviceData?.ndmMediaServerList ?? []}
|
||||||
|
ndmNvrList={deviceData?.ndmNvrList ?? []}
|
||||||
|
ndmSecurityBoxList={deviceData?.ndmSecurityBoxList ?? []}
|
||||||
|
ndmSwitchList={deviceData?.ndmSwitchList ?? []}
|
||||||
|
ndmVideoServerList={deviceData?.ndmVideoServerList ?? []}
|
||||||
|
ndmDeviceAlarmLogList={[]}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,17 +1,38 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
import type { Station, StationStatusRecord } from '@/apis/domains';
|
import type { Station } from '@/apis/domains';
|
||||||
|
|
||||||
export interface StationState {
|
export interface StationState {
|
||||||
stationList: Station[];
|
stationList: Station[];
|
||||||
stationStatusRecord: StationStatusRecord;
|
|
||||||
setStationList: (stationList: Station[]) => void;
|
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: [],
|
stationList: [],
|
||||||
stationStatusRecord: {},
|
|
||||||
setStationList: (stationList: Station[]) => set({ stationList }),
|
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;
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user