import React, { useState, useCallback, ReactNode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { Pie, Column, Line } from '@ant-design/plots';
import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons';
import {
Card
} from 'antd';
import { AlarmDeviceData } from '../share/monitorTypes.ts';
import { queryFns } from './api.ts';
import { ThreeJSRoom } from './components_three.tsx';
import { GlobalConfig } from "../share/types.ts";
const queryClient = new QueryClient();
// 声明全局配置对象类型
declare global {
interface Window {
CONFIG?: GlobalConfig;
}
}
interface ServerMonitorChartsProps {
type?: 'pie' | 'column' | 'line' | 'pie2';
}
const pushStateAndTrigger = (url: string, target: string) => {
window.history.pushState({}, '', url);
window.dispatchEvent(new Event('popstate'));
}
// 统一的链接处理函数
const handleNavigate = (url: string) => {
// 判断是否在iframe中
const isInIframe = window.self !== window.top;
if (isInIframe) {
pushStateAndTrigger(url, 'top');
} else {
window.open(url, '_blank');
}
};
function AlarmDeviceTable() {
const { data = [], isLoading } = useQuery({
queryKey: ['topAlarmDevices'],
queryFn: queryFns.fetchTopAlarmDevices,
refetchInterval: 30000
});
if (isLoading) {
return (
加载中...
);
}
return (
{/* 表头 */}
{/* 表格内容 */}
{data.map((item: AlarmDeviceData) => (
))}
{data.length === 0 && (
暂无数据
)}
);
}
function CustomCard({ title, children, className = '', bodyStyle = {}, onClick }: {
title?: string;
children: ReactNode;
className?: string;
bodyStyle?: React.CSSProperties;
onClick?: () => void;
}) {
return (
{title && (
{title}
)}
{children}
);
}
function MetricCards() {
const { data: metrics = {
totalDevices: 0,
onlineDevices: 0,
offlineDevices: 0,
onlineRate: "0.00",
riskLevel: { level: '健康', color: '#52c41a' }
}, isLoading } = useQuery({
queryKey: ['deviceMetrics'],
queryFn: queryFns.fetchDeviceMetrics,
refetchInterval: 30000,
refetchIntervalInBackground: true
});
const metricConfigs = [
{
title: "设备数",
value: metrics.totalDevices,
link: "/admin/alarm/manage"
},
{
title: "正常数",
value: metrics.onlineDevices,
link: "/admin/alarm/manage"
},
{
title: "在线率",
value: `${metrics.onlineRate}%`,
link: "/admin/device/rate"
},
{
title: "异常数",
value: metrics.offlineDevices,
link: "/admin/alert/manage"
},
{
title: "风险等级",
value: metrics.riskLevel.level,
color: metrics.riskLevel.color,
link: undefined
}
];
return (
<>
{metricConfigs.map((metric, index) => (
metric.link && handleNavigate(metric.link)}
className={metric.link ? "cursor-pointer" : undefined}
>
{metric.title}
{isLoading ? "-" : metric.value}
))}
>
);
}
function ServerMonitorCharts({ type = 'pie' }: ServerMonitorChartsProps) {
// 资产分类数据
const { data: categoryData } = useQuery({
queryKey: ['zichanCategory'],
queryFn: queryFns.fetchCategoryData,
enabled: type === 'pie'
});
// 在线率变化数据
const { data: onlineRateData } = useQuery({
queryKey: ['zichanOnlineRate'],
queryFn: queryFns.fetchOnlineRateData,
enabled: type === 'column'
});
// 资产流转状态数据
const { data: stateData } = useQuery({
queryKey: ['zichanState'],
queryFn: queryFns.fetchStateData,
enabled: type === 'pie2'
});
// 告警数据变化
const { data: alarmData } = useQuery({
queryKey: ['pingAlarm'],
queryFn: queryFns.fetchAlarmData,
enabled: type === 'line'
});
const renderChart = () => {
switch (type) {
case 'pie':
return (
{
// 只有占比超过5%的项才显示标签
if (percent < 0.05) return null;
return `${设备分类}\n(${设备数})`;
},
style: {
fill: '#fff',
fontSize: 12,
fontWeight: 500,
},
transform: [{ type: 'overlapDodgeY' }],
}}
theme={{
colors10: ['#36cfc9', '#ff4d4f', '#ffa940', '#73d13d', '#4096ff'],
}}
legend={false}
autoFit={true}
interaction={{
tooltip: {
render: (_: any, { items, title }: { items: any[], title: string }) => {
if (!items || items.length === 0) return '';
// 获取当前选中项的数据
const item = items[0];
// 根据value找到对应的完整数据项
const fullData = categoryData?.find(d => d['设备数'] === item.value);
if (!fullData) return '';
return `
数量: ${item.value}
占比: ${fullData['百分比']}%
`;
}
}
}}
/>
);
case 'column':
return (
{
let content = items['time_interval'];
content += `\n(${(items['total_devices'])})`;
return content;
},
}}
xAxis={{
label: {
style: {
fill: '#fff',
},
},
}}
yAxis={{
label: {
style: {
fill: '#fff',
},
},
}}
autoFit={true}
interaction={{
tooltip:false
}}
/>
);
case 'line':
return (
{
const value = items['total_devices'];
// if (value === 0) return null;
// const maxValue = Math.max(...(alarmData || []).map(item => item.total_devices));
// if (value < maxValue * 0.3 && alarmData && alarmData.length > 8) return null;
return `${items['time_interval']}\n(${value})`;
},
transform: [{ type: 'overlapDodgeY' }],
}}
xAxis={{
label: {
style: {
fill: '#fff',
},
autoHide: true,
autoRotate: true,
},
}}
yAxis={{
label: {
style: {
fill: '#fff',
},
},
}}
autoFit={true}
interaction={{
tooltip: {
render: (_: any, { items, title }: { items: any[], title: string }) => {
if (!items || items.length === 0) return '';
// 获取当前选中项的数据
const item = items[0];
// 根据value找到对应的完整数据项
const fullData = alarmData?.find(d => d.total_devices === item.value);
if (!fullData) return '';
return `
${fullData.time_interval}
数量: ${item.value}
`;
}
}
}}
/>
);
case 'pie2':
return (
{
// 只有占比超过5%的项才显示标签
if (percent < 0.05) return null;
return `${资产流转}\n(${设备数})`;
},
style: {
fill: '#fff',
fontSize: 12,
fontWeight: 500,
},
transform: [{ type: 'overlapDodgeY' }],
}}
theme={{
colors10: ['#36cfc9', '#ff4d4f', '#ffa940', '#73d13d', '#4096ff'],
}}
legend={{
color: {
itemLabelFill: '#fff',
}
}}
autoFit={true}
interaction={{
tooltip: {
render: (_: any, { items, title }: { items: any[], title: string }) => {
if (!items || items.length === 0) return '';
// 获取当前选中项的数据
const item = items[0];
// 根据value找到对应的完整数据项
const fullData = stateData?.find(d => d['设备数'] === item.value);
if (!fullData) return '';
return `
数量: ${item.value}
占比: ${fullData['百分比']}%
`;
}
}
}}
/>
);
}
};
return (
{renderChart()}
);
}
function PageTitle() {
return (
);
}
function DataCenter() {
const [isFullscreen, setIsFullscreen] = useState(false);
const toggleFullscreen = useCallback(() => {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
setIsFullscreen(true);
} else {
document.exitFullscreen().then(() => {
setIsFullscreen(false);
window.location.reload();
});
}
}, []);
return (
{/* 顶部标题区域 */}
{/* 全屏切换按钮 */}
{/* 主要内容区域 */}
{/* 顶部指标卡片 */}
{/* 左侧图表区域 */}
{/* 饼图 */}
handleNavigate('/admin/device/type')}
>
{/* 柱状图 */}
handleNavigate('/admin/device/rate')}
>
{/* 饼图2 */}
handleNavigate('/admin/asset/transfer/chart')}
>
{/* 右侧区域 */}
{/* 中间3D机房区域 */}
{/* 底部区域分为两列 */}
{/* 左侧告警曲线图 */}
handleNavigate('/admin/alarm/trend')}
>
{/* 右侧告警设备表格 */}
handleNavigate('/admin/alert/manage')}
>
);
}
// 渲染应用
const root = createRoot(document.getElementById('root') as HTMLElement);
root.render(
);