diff --git a/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx b/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx index c46879c..0ca1078 100644 --- a/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx +++ b/frontend/src/components/monitor/dashboard/HostMonitorDashboard.tsx @@ -1,10 +1,11 @@ -// 主机监控大屏 — 列表视图 + 可排序 + 操作栏 // Logan.Li +// 主机监控大屏 — Grafana 风格:统计卡片 + 资源条状图 + 可排序列表 // Logan.Li +// 参照: Grafana node-dashboard.json (表格+时序) / k8s-dashboard.json (bargauge+阈值) import { useState, useEffect, useCallback } from 'react'; import { useNavigate } from 'react-router-dom'; import { Server, Cpu, HardDrive, Activity, Monitor, Bell, ArrowUpDown, ArrowUp, ArrowDown, RefreshCw, Search, - ChevronLeft, ChevronRight + ChevronLeft, ChevronRight, Zap, AlertTriangle, Wifi, WifiOff } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -133,37 +134,151 @@ export default function HostMonitorDashboard() { return (
- {/* 统计卡片 */} -
+ {/* 统计卡片 — Grafana Quick Facts 风格 (参照 JVM Dashboard) */} +
- -
+ +
-
{stats.online}
-
Agent 在线
+
{stats.total}
+
主机总数
- -
+ +
-
{stats.offline}
-
Agent 离线
+
{stats.online}
+
Agent 在线
- -
+ +
-
{stats.total}
-
主机总数
+
{stats.offline}
+
离线
+ {(() => { + const onlines = hosts.filter((h: any) => h.agent_status === 'online'); + const avgCpu = onlines.length > 0 ? onlines.reduce((s: number, h: any) => s + (h.cpu_usage || 0), 0) / onlines.length : 0; + const avgMem = onlines.length > 0 ? onlines.reduce((s: number, h: any) => s + (h.memory_usage || 0), 0) / onlines.length : 0; + const cpuColor = avgCpu > 85 ? 'text-red-400' : avgCpu > 60 ? 'text-amber-400' : 'text-emerald-400'; + const memColor = avgMem > 85 ? 'text-red-400' : avgMem > 60 ? 'text-amber-400' : 'text-emerald-400'; + return (<> + + +
+
+
{avgCpu.toFixed(1)}%
+
平均 CPU
+
+
+
+ + +
+
+
{avgMem.toFixed(1)}%
+
平均内存
+
+
+
+ ); + })()}
+ {/* 资源条状图 — Grafana Bargauge 风格 (参照 K8s Node CPU/Memory Ratio) */} + {hosts.filter((h: any) => h.agent_status === 'online').length > 0 && ( +
+ {/* CPU 排行 */} + + + + + CPU 使用率 Top 5 + 阈值: 60% / 85% + + + + {[...hosts] + .filter((h: any) => h.agent_status === 'online') + .sort((a: any, b: any) => (b.cpu_usage || 0) - (a.cpu_usage || 0)) + .slice(0, 5) + .map((h: any) => { + const val = h.cpu_usage || 0; + return ( +
+ {h.hostname} +
+
= 85 + ? 'linear-gradient(90deg, #ef4444, #f87171)' + : val >= 60 + ? 'linear-gradient(90deg, #f59e0b, #fbbf24)' + : 'linear-gradient(90deg, #10b981, #34d399)' + }} + /> +
+ = 85 ? 'text-red-400' : val >= 60 ? 'text-amber-400' : 'text-emerald-400' + }`}>{val.toFixed(1)}% +
+ ); + })} + + + + {/* 内存排行 */} + + + + + 内存使用率 Top 5 + 阈值: 60% / 85% + + + + {[...hosts] + .filter((h: any) => h.agent_status === 'online') + .sort((a: any, b: any) => (b.memory_usage || 0) - (a.memory_usage || 0)) + .slice(0, 5) + .map((h: any) => { + const val = h.memory_usage || 0; + return ( +
+ {h.hostname} +
+
= 85 + ? 'linear-gradient(90deg, #ef4444, #f87171)' + : val >= 60 + ? 'linear-gradient(90deg, #f59e0b, #fbbf24)' + : 'linear-gradient(90deg, #10b981, #34d399)' + }} + /> +
+ = 85 ? 'text-red-400' : val >= 60 ? 'text-amber-400' : 'text-emerald-400' + }`}>{val.toFixed(1)}% +
+ ); + })} + + +
+ )} + {/* 表格卡片 */}