import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { Clock, Server, Cloud, Bell, DollarSign, TrendingUp, TrendingDown, ArrowRight } from 'lucide-react'; import { dashboardStatsApi, serverApi, kubernetesApi } from '@/services/api'; import { StatCard } from './DashboardStats'; import { CostOverviewCard, TopResourceUsageCard, ResourceUsageCard, computeMonthChange } from './DashboardCharts'; import { AlertMessagesCard, CertificateExpiryCard, ReleaseHistoryCard, ServerStatusCard, KubernetesClusterCard, } from './DashboardRecent'; import { SkeletonDashboard } from '@/components/layout/SkeletonCard'; /** * Dashboard - 优化后的仪表盘页面 * * 优化点: * 1. 使用 SkeletonDashboard 骨架屏替代简单 spinner * 2. 页面结构使用 dg-page* 工具类 * 3. 统计卡片添加 staggered fade-in 动画 * 4. 操作按钮使用 dg-btn* 工具类 * 5. 更好的空状态处理 * 6. 添加页面标题区域的操作入口 */ export function Dashboard() { const navigate = useNavigate(); const [initialLoading, setInitialLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [stats, setStats] = useState(null); const [servers, setServers] = useState([]); const [clusters, setClusters] = useState([]); const [alerts, setAlerts] = useState([]); const [releases, setReleases] = useState([]); const [expCerts, setExpCerts] = useState([]); const [metrics, setMetrics] = useState([]); const refreshData = useCallback(async () => { setRefreshing(true); try { const [statsRes, serversRes, clustersRes, alertsRes, releasesRes, metricsRes] = await Promise.all([ dashboardStatsApi.getStats().catch((e) => { console.error('Failed to fetch dashboard stats:', e); return null; }), serverApi.getServers({ page: 1, page_size: 100 }).catch((e) => { console.error('Failed to fetch servers:', e); return null; }), kubernetesApi.getClusters().catch((e) => { console.error('Failed to fetch k8s clusters:', e); return null; }), dashboardStatsApi.getAlerts(10).catch((e) => { console.error('Failed to fetch dashboard alerts:', e); return null; }), dashboardStatsApi.getReleases(5).catch((e) => { console.error('Failed to fetch dashboard releases:', e); return null; }), dashboardStatsApi.getMetrics(6).catch((e) => { console.error('Failed to fetch metrics:', e); return []; }), ]); setStats(statsRes); setServers(serversRes?.list ?? []); setClusters(clustersRes?.list ?? []); setAlerts(Array.isArray(alertsRes) ? alertsRes : []); setReleases(Array.isArray(releasesRes) ? releasesRes : []); setExpCerts(statsRes?.expiring_certs ?? []); setMetrics(Array.isArray(metricsRes) ? metricsRes : []); } catch (error) { console.error('Failed to load dashboard data:', error); } finally { setRefreshing(false); setInitialLoading(false); } }, []); useEffect(() => { refreshData(); // 每 5 分钟自动刷新 const interval = setInterval(refreshData, 5 * 60 * 1000); return () => clearInterval(interval); }, [refreshData]); if (initialLoading) { return ; } const serverStats = stats?.server_stats; const k8sStats = stats?.kubernetes_stats; const alertStats = stats?.alert_stats; const costStats = stats?.cost_stats; const costChange = computeMonthChange(costStats?.month_trend); return (
{/* Page Header */}

仪表盘

实时监控和运维概览 · 最后更新: {new Date().toLocaleTimeString('zh-CN')}

{/* Stats Grid with staggered animation */}
0 ? 'down' : 'neutral'} trendValue={Number(serverStats?.error ?? 0) > 0 ? `${serverStats?.error} 台异常` : '运行正常'} color="blue" onClick={() => navigate('/servers')} />
navigate('/kubernetes')} />
0 ? 'up' : 'down'} trendValue={`本周 ${alertStats?.week_count ?? 0} 条`} color="rose" onClick={() => navigate('/alert-center')} />
0 ? '+' : ''}${costChange.pct.toFixed(1)}%`} color="orange" onClick={() => navigate('/cloud-billing')} />
{/* Resource Usage Chart */}
{/* Cost & Top Resource Usage */}
{/* Alert Messages & Certificate Expiry */}
{/* Bottom Row */}
); }