From 92a8fed7967c850dc1a5c7d8e850602473960b54 Mon Sep 17 00:00:00 2001 From: sagitchu Date: Wed, 18 Mar 2026 15:48:48 +0800 Subject: [PATCH] feat: redesign monitor page to nezha-style server grid --- plans/046-monitor-nezha-redesign.md | 15 +++ vite-frontend/src/pages/node/monitor-view.tsx | 125 ++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 plans/046-monitor-nezha-redesign.md diff --git a/plans/046-monitor-nezha-redesign.md b/plans/046-monitor-nezha-redesign.md new file mode 100644 index 0000000..9f7ad4a --- /dev/null +++ b/plans/046-monitor-nezha-redesign.md @@ -0,0 +1,15 @@ +# Monitor Nezha Redesign +## Summary +Redesign the monitor overview to resemble Nezha dashboard by introducing a Server Matrix (Grid) containing cards with metrics visualisations. + +## Objectives +- [x] Analyze `monitor-view.tsx` layout boundaries. +- [x] Create `ServerCard` utilizing `Progress` component. +- [x] Render metrics (CPU, RAM, Disk, System Load, Connections, Network speeeds). +- [x] Implement robust real-time updates and seamless state linkage via existing hooks. +- [x] Adjust layout positioning for impact at top-of-page. + +## Technical Details +Injected a `ServerCard` inline component mapping over unified node metric states. Used the tailwind and hero UI components matching visual designs like progress bars and small monospace typograhy. Extracted utility color scales. + +## Status: Complete diff --git a/vite-frontend/src/pages/node/monitor-view.tsx b/vite-frontend/src/pages/node/monitor-view.tsx index 78ae0ed..cd9d4c6 100644 --- a/vite-frontend/src/pages/node/monitor-view.tsx +++ b/vite-frontend/src/pages/node/monitor-view.tsx @@ -26,6 +26,12 @@ import { Edit, Activity, Play, + Server, + Zap, + HardDrive, + Cpu, + Clock, + Globe, } from "lucide-react"; import toast from "react-hot-toast"; @@ -69,6 +75,7 @@ import { DropdownItem, } from "@/shadcn-bridge/heroui/dropdown"; import { Card, CardBody, CardHeader } from "@/shadcn-bridge/heroui/card"; +import { Progress } from "@/shadcn-bridge/heroui/progress"; import { useNodeRealtime } from "@/pages/node/use-node-realtime"; interface MonitorViewProps { @@ -145,6 +152,117 @@ const formatBytesPerSecond = (bytesPerSecond: number): string => { return `${parseFloat((bytesPerSecond / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`; }; +const formatUptime = (seconds: number) => { + if (!seconds) return "-"; + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + if (days > 0) return `${days} 天 ${hours} 小时`; + return `${hours} 小时`; +}; + +const getColorByUsage = (usage?: number) => { + if (usage === undefined || usage === null) return "default"; + if (usage >= 90) return "danger"; + if (usage >= 75) return "warning"; + if (usage >= 50) return "primary"; + return "success"; +}; + +function ServerCard({ node, metric }: { node: any; metric: RealtimeNodeMetric | null }) { + const isOnline = node.connectionStatus === "online"; + + return ( + + +
+ +
{node.name}
+
+ + {isOnline ? "在线" : "离线"} + +
+ + +
+
+
+ 运行 +
+ {metric ? formatUptime(metric.uptime) : "-"} +
+
+
+ 负载 +
+ {metric ? metric.load1.toFixed(2) : "-"} +
+
+
+ 连接 +
+ {metric ? metric.tcpConns : "-"} +
+
+
+ UDP +
+ {metric ? metric.udpConns : "-"} +
+
+ +
+
+
+
CPU
+ {metric ? metric.cpuUsage.toFixed(1) : "0.0"}% +
+ +
+
+
+
RAM
+ {metric ? metric.memoryUsage.toFixed(1) : "0.0"}% +
+ +
+
+
+
Disk
+ {metric ? metric.diskUsage.toFixed(1) : "0.0"}% +
+ +
+
+ +
+
+
+ 网络 ↓ + {metric ? formatBytesPerSecond(metric.netInSpeed) : "0 B/s"} +
+
+ 总计 + {metric ? formatBytes(metric.netInBytes) : "0 B"} +
+
+ +
+
+ 网络 ↑ + {metric ? formatBytesPerSecond(metric.netOutSpeed) : "0 B/s"} +
+
+ 总计 + {metric ? formatBytes(metric.netOutBytes) : "0 B"} +
+
+
+
+
+ ); +} + type MetricType = | "cpu" | "memory" @@ -1059,6 +1177,13 @@ export function MonitorView({ nodeMap }: MonitorViewProps) { {!accessDenied && ( <> +
+ {nodes.map((node) => { + const metric = realtimeNodeMetrics[node.id] || null; + return ; + })} +
+

监控概览