2026-03-10 14:34:39 +08:00
|
|
|
|
import React, { useState, useCallback, useMemo } from 'react';
|
|
|
|
|
|
import { Card, Row, Col, Typography, message } from 'antd';
|
|
|
|
|
|
import { DatabaseOutlined, CloudServerOutlined, WarningOutlined, HomeOutlined, TeamOutlined, BarChartOutlined, DashboardOutlined } from '@ant-design/icons';
|
2026-01-20 11:28:53 +08:00
|
|
|
|
import api from '../api';
|
2026-03-10 14:34:39 +08:00
|
|
|
|
import { designTokens } from '../config/theme';
|
|
|
|
|
|
import { useFetch } from '../hooks/useSWR';
|
|
|
|
|
|
import {
|
|
|
|
|
|
AnimatedCounter,
|
|
|
|
|
|
CircularProgress,
|
|
|
|
|
|
PowerGauge,
|
|
|
|
|
|
DeviceTrendChart,
|
|
|
|
|
|
StatusLegend,
|
|
|
|
|
|
StatCard,
|
|
|
|
|
|
NavigationGrid,
|
|
|
|
|
|
QuickStats,
|
|
|
|
|
|
SystemInfo,
|
|
|
|
|
|
} from '../components/dashboard';
|
2025-12-18 10:45:13 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const { Title } = Typography;
|
2025-12-26 11:03:47 +08:00
|
|
|
|
|
|
|
|
|
|
const containerStyle = {
|
|
|
|
|
|
minHeight: '100vh',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
background: 'linear-gradient(180deg, #f5f7fa 0%, #e8ecf1 100%)',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
padding: 'clamp(12px, 3vw, 20px)',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const headerStyle = {
|
|
|
|
|
|
textAlign: 'center',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
marginBottom: '32px',
|
2026-01-29 13:09:43 +08:00
|
|
|
|
padding: 'clamp(20px, 5vw, 32px) clamp(16px, 4vw, 48px)',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
2026-01-29 13:09:43 +08:00
|
|
|
|
borderRadius: 'clamp(12px, 3vw, 20px)',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
color: '#fff',
|
2026-01-20 11:28:53 +08:00
|
|
|
|
boxShadow: '0 8px 32px rgba(102, 126, 234, 0.3)',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
animation: 'fadeInDown 0.6s ease-out',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const titleStyle = {
|
2026-01-29 13:09:43 +08:00
|
|
|
|
fontSize: 'clamp(1.4rem, 4vw, 2.2rem)',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
fontWeight: '700',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
color: '#fff',
|
|
|
|
|
|
margin: '0 0 8px 0',
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
justifyContent: 'center',
|
2026-01-29 13:09:43 +08:00
|
|
|
|
gap: 'clamp(8px, 2vw, 16px)',
|
|
|
|
|
|
flexWrap: 'wrap',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
textAlign: 'center',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const subtitleStyle = {
|
2026-01-29 13:09:43 +08:00
|
|
|
|
fontSize: 'clamp(0.85rem, 2.5vw, 1.1rem)',
|
2025-12-29 11:15:55 +08:00
|
|
|
|
color: 'rgba(255, 255, 255, 0.85)',
|
2026-01-29 13:09:43 +08:00
|
|
|
|
margin: '0',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
textAlign: 'center',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-01-20 14:31:20 +08:00
|
|
|
|
const progressCardStyle = {
|
|
|
|
|
|
borderRadius: designTokens.borderRadius.large,
|
|
|
|
|
|
border: 'none',
|
|
|
|
|
|
boxShadow: designTokens.shadows.medium,
|
|
|
|
|
|
background: '#fff',
|
|
|
|
|
|
height: '100%',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
animation: 'fadeInUp 0.6s ease-out 0.3s backwards',
|
2026-01-20 14:31:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const chartContainerStyle = {
|
|
|
|
|
|
padding: '20px',
|
|
|
|
|
|
borderRadius: designTokens.borderRadius.medium,
|
|
|
|
|
|
background: 'linear-gradient(135deg, #fafafa 0%, #f5f5f5 100%)',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
border: '1px solid #f0f0f0',
|
2026-01-20 14:31:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const PIE_CHART_COLORS = {
|
|
|
|
|
|
success: designTokens.colors.success.main,
|
|
|
|
|
|
warning: designTokens.colors.warning.main,
|
|
|
|
|
|
error: designTokens.colors.error.main,
|
2026-02-10 11:04:01 +08:00
|
|
|
|
primary: designTokens.colors.primary.main,
|
2026-01-20 14:31:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const pieChartStyle = {
|
|
|
|
|
|
width: '180px',
|
|
|
|
|
|
height: '180px',
|
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
|
background: `conic-gradient(
|
|
|
|
|
|
${PIE_CHART_COLORS.success} 0deg 216deg,
|
|
|
|
|
|
${PIE_CHART_COLORS.warning} 216deg 288deg,
|
|
|
|
|
|
${PIE_CHART_COLORS.error} 288deg 324deg,
|
|
|
|
|
|
${PIE_CHART_COLORS.primary} 324deg 360deg
|
|
|
|
|
|
)`,
|
|
|
|
|
|
position: 'relative',
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
alignItems: 'center',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
justifyContent: 'center',
|
2026-01-20 14:31:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const pieChartInner = {
|
|
|
|
|
|
width: '120px',
|
|
|
|
|
|
height: '120px',
|
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
|
background: '#fff',
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
justifyContent: 'center',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
flexDirection: 'column',
|
2026-01-20 14:31:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-26 11:03:47 +08:00
|
|
|
|
const overviewCardStyle = {
|
2026-01-20 11:28:53 +08:00
|
|
|
|
borderRadius: designTokens.borderRadius.large,
|
2025-12-26 11:03:47 +08:00
|
|
|
|
border: 'none',
|
2026-01-20 11:28:53 +08:00
|
|
|
|
boxShadow: designTokens.shadows.medium,
|
|
|
|
|
|
background: '#fff',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
animation: 'fadeInUp 0.6s ease-out 0.2s backwards',
|
2025-12-26 11:03:47 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-12-18 10:45:13 +08:00
|
|
|
|
function Dashboard() {
|
2025-12-29 11:15:55 +08:00
|
|
|
|
const [hoveredCard, setHoveredCard] = useState(null);
|
2026-01-20 11:28:53 +08:00
|
|
|
|
const [animatedKey, setAnimatedKey] = useState(0);
|
2025-12-18 10:45:13 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const { data: devicesData, isLoading: devicesLoading, mutate: mutateDevices } = useFetch(
|
|
|
|
|
|
'/devices?pageSize=1'
|
|
|
|
|
|
);
|
|
|
|
|
|
const { data: racksData, isLoading: racksLoading } = useFetch('/racks?pageSize=1');
|
|
|
|
|
|
const { data: roomsData, isLoading: roomsLoading } = useFetch('/rooms');
|
|
|
|
|
|
const { data: usersData, isLoading: usersLoading } = useFetch('/users?pageSize=1');
|
|
|
|
|
|
const { data: ticketsData, isLoading: ticketsLoading } = useFetch('/tickets?pageSize=1&status=open');
|
|
|
|
|
|
const { data: faultData, isLoading: faultLoading } = useFetch('/devices?status=fault&pageSize=1');
|
2025-12-29 11:15:55 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const loading = devicesLoading || racksLoading || roomsLoading || usersLoading || ticketsLoading;
|
|
|
|
|
|
const isRefreshing = devicesLoading && devicesData;
|
2025-12-29 11:15:55 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const stats = useMemo(() => {
|
|
|
|
|
|
const totalDevices = devicesData?.total || 0;
|
|
|
|
|
|
const totalRacks = racksData?.total || 0;
|
|
|
|
|
|
const rooms = roomsData || [];
|
|
|
|
|
|
const totalRooms = Array.isArray(rooms) ? rooms.length : 0;
|
|
|
|
|
|
const totalUsers = usersData?.total || 0;
|
|
|
|
|
|
const activeTickets = ticketsData?.total || 0;
|
|
|
|
|
|
const faultDevices = faultData?.total || 0;
|
2025-12-29 11:15:55 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
return {
|
|
|
|
|
|
totalDevices,
|
|
|
|
|
|
totalRacks,
|
|
|
|
|
|
totalRooms,
|
|
|
|
|
|
totalUsers,
|
|
|
|
|
|
activeTickets,
|
|
|
|
|
|
faultDevices,
|
|
|
|
|
|
deviceGrowth: 2.5,
|
|
|
|
|
|
faultTrend: -12.3,
|
|
|
|
|
|
onlineRate:
|
|
|
|
|
|
totalDevices > 0
|
|
|
|
|
|
? (((totalDevices - faultDevices) / totalDevices) * 100).toFixed(1)
|
|
|
|
|
|
: 100,
|
|
|
|
|
|
powerUsage: Math.floor(Math.random() * 5000) + 2000,
|
|
|
|
|
|
};
|
|
|
|
|
|
}, [devicesData, racksData, roomsData, usersData, ticketsData, faultData]);
|
2025-12-29 11:15:55 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const handleRefresh = useCallback(async () => {
|
|
|
|
|
|
setAnimatedKey((prev) => prev + 1);
|
|
|
|
|
|
await Promise.all([
|
|
|
|
|
|
mutateDevices(),
|
|
|
|
|
|
]);
|
|
|
|
|
|
}, [mutateDevices]);
|
2026-01-20 11:28:53 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
const handleHover = useCallback((key) => {
|
|
|
|
|
|
setHoveredCard(key);
|
2025-12-18 10:45:13 +08:00
|
|
|
|
}, []);
|
|
|
|
|
|
|
2026-02-10 11:04:01 +08:00
|
|
|
|
const statCards = useMemo(
|
|
|
|
|
|
() => [
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'devices',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: CloudServerOutlined,
|
|
|
|
|
|
color: designTokens.colors.primary.main,
|
|
|
|
|
|
statKey: 'totalDevices',
|
|
|
|
|
|
title: '总设备数',
|
|
|
|
|
|
trend: stats.deviceGrowth,
|
|
|
|
|
|
tagColor: 'blue',
|
|
|
|
|
|
delay: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'racks',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: DatabaseOutlined,
|
2026-03-10 14:34:39 +08:00
|
|
|
|
color: '#722ed1',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
statKey: 'totalRacks',
|
|
|
|
|
|
title: '总机柜数',
|
|
|
|
|
|
trend: 0,
|
|
|
|
|
|
tagColor: 'green',
|
|
|
|
|
|
customStatus: true,
|
|
|
|
|
|
delay: 1,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'rooms',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: HomeOutlined,
|
|
|
|
|
|
color: designTokens.colors.success.main,
|
|
|
|
|
|
statKey: 'totalRooms',
|
|
|
|
|
|
title: '总机房数',
|
|
|
|
|
|
trend: 0,
|
|
|
|
|
|
tagColor: 'green',
|
|
|
|
|
|
customStatus: true,
|
|
|
|
|
|
delay: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'faults',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: WarningOutlined,
|
|
|
|
|
|
color: designTokens.colors.error.main,
|
|
|
|
|
|
statKey: 'faultDevices',
|
|
|
|
|
|
title: '故障设备',
|
|
|
|
|
|
trend: stats.faultTrend,
|
|
|
|
|
|
tagColor: 'red',
|
|
|
|
|
|
delay: 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'users',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: TeamOutlined,
|
2026-03-10 14:34:39 +08:00
|
|
|
|
color: '#13c2c2',
|
2026-02-10 11:04:01 +08:00
|
|
|
|
statKey: 'totalUsers',
|
|
|
|
|
|
title: '用户总数',
|
|
|
|
|
|
trend: 5.2,
|
|
|
|
|
|
tagColor: 'cyan',
|
|
|
|
|
|
delay: 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: 'tickets',
|
|
|
|
|
|
xs: 12,
|
|
|
|
|
|
sm: 12,
|
|
|
|
|
|
md: 8,
|
|
|
|
|
|
lg: 6,
|
|
|
|
|
|
xl: 4,
|
|
|
|
|
|
icon: BarChartOutlined,
|
|
|
|
|
|
color: '#fa8c16',
|
|
|
|
|
|
statKey: 'activeTickets',
|
|
|
|
|
|
title: '待处理工单',
|
|
|
|
|
|
trend: -8.5,
|
|
|
|
|
|
tagColor: 'orange',
|
|
|
|
|
|
delay: 5,
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
[stats.deviceGrowth, stats.faultTrend]
|
|
|
|
|
|
);
|
2025-12-19 15:05:31 +08:00
|
|
|
|
|
2026-02-10 11:04:01 +08:00
|
|
|
|
const deviceTrendData = useMemo(
|
|
|
|
|
|
() => [
|
|
|
|
|
|
{ label: '周一', value: 45, color: designTokens.colors.primary.main },
|
|
|
|
|
|
{ label: '周二', value: 52, color: designTokens.colors.primary.main },
|
|
|
|
|
|
{ label: '周三', value: 48, color: designTokens.colors.primary.main },
|
|
|
|
|
|
{ label: '周四', value: 60, color: designTokens.colors.success.main },
|
|
|
|
|
|
{ label: '周五', value: 55, color: designTokens.colors.success.main },
|
|
|
|
|
|
{ label: '周六', value: 42, color: designTokens.colors.warning.main },
|
|
|
|
|
|
{ label: '周日', value: 58, color: designTokens.colors.primary.main },
|
|
|
|
|
|
],
|
|
|
|
|
|
[]
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-01-20 11:28:53 +08:00
|
|
|
|
const styles = `
|
|
|
|
|
|
@keyframes fadeInDown {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(-20px);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
@keyframes fadeInUp {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(20px);
|
|
|
|
|
|
}
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`;
|
2025-12-19 15:05:31 +08:00
|
|
|
|
|
2025-12-18 10:45:13 +08:00
|
|
|
|
return (
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<>
|
|
|
|
|
|
<style>{styles}</style>
|
2026-01-29 13:09:43 +08:00
|
|
|
|
<div style={containerStyle} className="dashboard-container">
|
|
|
|
|
|
<div style={headerStyle} className="dashboard-header">
|
|
|
|
|
|
<h1 style={titleStyle} className="dashboard-title">
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<DashboardOutlined />
|
|
|
|
|
|
IDC设备管理系统
|
|
|
|
|
|
</h1>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<p style={subtitleStyle} className="dashboard-subtitle">
|
|
|
|
|
|
实时监控 · 智能管理 · 高效运维
|
|
|
|
|
|
</p>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</div>
|
2025-12-19 15:05:31 +08:00
|
|
|
|
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<Row gutter={[24, 24]} style={{ marginBottom: '32px' }}>
|
2026-03-10 14:34:39 +08:00
|
|
|
|
{statCards.map((config) => (
|
|
|
|
|
|
<StatCard
|
|
|
|
|
|
key={config.statKey}
|
|
|
|
|
|
config={config}
|
|
|
|
|
|
stats={stats}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
animatedKey={animatedKey}
|
|
|
|
|
|
hoveredCard={hoveredCard}
|
|
|
|
|
|
onHover={handleHover}
|
|
|
|
|
|
/>
|
|
|
|
|
|
))}
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</Row>
|
2025-12-19 15:05:31 +08:00
|
|
|
|
|
2026-01-21 16:31:07 +08:00
|
|
|
|
<Row gutter={[24, 24]} style={{ marginBottom: '24px' }}>
|
|
|
|
|
|
<Col xs={24} md={8}>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<Card style={progressCardStyle}>
|
|
|
|
|
|
<div style={{ padding: '20px' }}>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<Title
|
|
|
|
|
|
level={5}
|
|
|
|
|
|
style={{ margin: '0 0 20px 0', color: designTokens.colors.text.primary }}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
设备状态分布
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
|
|
|
|
|
<div style={pieChartStyle}>
|
|
|
|
|
|
<div style={pieChartInner}>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<span
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '1.5rem',
|
|
|
|
|
|
fontWeight: '700',
|
|
|
|
|
|
color: designTokens.colors.text.primary,
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
{stats.totalDevices}
|
|
|
|
|
|
</span>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<span
|
|
|
|
|
|
style={{ fontSize: '0.75rem', color: designTokens.colors.text.secondary }}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
设备总数
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2025-12-29 11:15:55 +08:00
|
|
|
|
</div>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<StatusLegend />
|
2025-12-29 11:15:55 +08:00
|
|
|
|
</div>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
2025-12-29 11:15:55 +08:00
|
|
|
|
|
2026-01-21 16:31:07 +08:00
|
|
|
|
<Col xs={24} md={8}>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<Card style={progressCardStyle}>
|
|
|
|
|
|
<div style={{ padding: '20px' }}>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<Title
|
|
|
|
|
|
level={5}
|
|
|
|
|
|
style={{ margin: '0 0 20px 0', color: designTokens.colors.text.primary }}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
系统健康指标
|
|
|
|
|
|
</Title>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
flexDirection: 'column',
|
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
|
gap: '24px',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<CircularProgress
|
|
|
|
|
|
percentage={parseFloat(stats.onlineRate)}
|
|
|
|
|
|
size={140}
|
|
|
|
|
|
strokeWidth={12}
|
|
|
|
|
|
color={designTokens.colors.success.main}
|
|
|
|
|
|
label="在线率"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<PowerGauge value={stats.powerUsage} maxValue={10000} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
2025-12-19 15:05:31 +08:00
|
|
|
|
|
2026-01-21 16:31:07 +08:00
|
|
|
|
<Col xs={24} md={8}>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
<Card style={progressCardStyle}>
|
|
|
|
|
|
<div style={{ padding: '20px' }}>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<Title
|
|
|
|
|
|
level={5}
|
|
|
|
|
|
style={{ margin: '0 0 20px 0', color: designTokens.colors.text.primary }}
|
|
|
|
|
|
>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
周设备趋势
|
|
|
|
|
|
</Title>
|
|
|
|
|
|
<div style={chartContainerStyle}>
|
|
|
|
|
|
<DeviceTrendChart data={deviceTrendData} />
|
|
|
|
|
|
</div>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<div
|
|
|
|
|
|
style={{
|
|
|
|
|
|
marginTop: '16px',
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
|
gap: '16px',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-03-10 14:34:39 +08:00
|
|
|
|
<span style={{ fontSize: '0.75rem', color: designTokens.colors.text.tertiary }}>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
周一 至 周日 设备变化趋势
|
2026-03-10 14:34:39 +08:00
|
|
|
|
</span>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Card style={overviewCardStyle}>
|
|
|
|
|
|
<div style={{ padding: '24px' }}>
|
2026-01-21 16:31:07 +08:00
|
|
|
|
<Row gutter={[24, 24]}>
|
|
|
|
|
|
<Col xs={24} md={16}>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<div
|
|
|
|
|
|
className="welcome-banner"
|
|
|
|
|
|
style={{
|
|
|
|
|
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
|
|
|
|
borderRadius: designTokens.borderRadius.medium,
|
|
|
|
|
|
padding: '28px',
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
marginBottom: '24px',
|
|
|
|
|
|
animation: 'fadeInUp 0.6s ease-out 0.4s backwards',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<h2
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '1.4rem',
|
|
|
|
|
|
fontWeight: '600',
|
|
|
|
|
|
margin: '0 0 8px 0',
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-01-21 16:31:07 +08:00
|
|
|
|
欢迎使用IDC设备管理系统
|
|
|
|
|
|
</h2>
|
2026-02-10 11:04:01 +08:00
|
|
|
|
<p
|
|
|
|
|
|
style={{
|
|
|
|
|
|
fontSize: '1rem',
|
|
|
|
|
|
margin: '0',
|
|
|
|
|
|
opacity: '0.9',
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
2026-01-21 16:31:07 +08:00
|
|
|
|
专业的机房设备管理解决方案,提供全方位的设备监控和管理能力
|
|
|
|
|
|
</p>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</div>
|
2026-01-21 16:31:07 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
<QuickStats onlineRate={stats.onlineRate} powerUsage={stats.powerUsage} />
|
2026-01-21 16:31:07 +08:00
|
|
|
|
</Col>
|
|
|
|
|
|
|
|
|
|
|
|
<Col xs={24} md={8}>
|
2026-03-10 14:34:39 +08:00
|
|
|
|
<SystemInfo onRefresh={handleRefresh} isRefreshing={isRefreshing} />
|
2026-01-21 16:31:07 +08:00
|
|
|
|
</Col>
|
|
|
|
|
|
</Row>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
|
2026-03-10 14:34:39 +08:00
|
|
|
|
<NavigationGrid hoveredCard={hoveredCard} onHover={handleHover} />
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</Card>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<style>{`
|
|
|
|
|
|
@media (max-width: 576px) {
|
2026-01-29 13:09:43 +08:00
|
|
|
|
.dashboard-container {
|
|
|
|
|
|
padding: 12px !important;
|
2026-01-20 11:28:53 +08:00
|
|
|
|
}
|
2026-01-29 13:09:43 +08:00
|
|
|
|
.dashboard-header {
|
|
|
|
|
|
padding: 20px 16px !important;
|
|
|
|
|
|
border-radius: 12px !important;
|
2026-01-20 11:28:53 +08:00
|
|
|
|
}
|
2026-01-29 13:09:43 +08:00
|
|
|
|
.dashboard-title {
|
|
|
|
|
|
font-size: 1.4rem !important;
|
|
|
|
|
|
gap: 8px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.dashboard-subtitle {
|
|
|
|
|
|
font-size: 0.85rem !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
|
font-size: 1.6rem !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-grid {
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr) !important;
|
|
|
|
|
|
gap: 8px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-button {
|
|
|
|
|
|
padding: 12px 8px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-icon {
|
|
|
|
|
|
width: 40px !important;
|
|
|
|
|
|
height: 40px !important;
|
|
|
|
|
|
font-size: 20px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.nav-text {
|
|
|
|
|
|
font-size: 0.7rem !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
@media (max-width: 375px) {
|
|
|
|
|
|
.nav-grid {
|
|
|
|
|
|
grid-template-columns: repeat(2, 1fr) !important;
|
2026-01-20 11:28:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
`}</style>
|
2025-12-19 15:05:31 +08:00
|
|
|
|
</div>
|
2026-01-20 11:28:53 +08:00
|
|
|
|
</>
|
2025-12-18 10:45:13 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 11:03:47 +08:00
|
|
|
|
export default React.memo(Dashboard);
|