diff --git a/plans/047-beautify-monitor-tab.md b/plans/047-beautify-monitor-tab.md new file mode 100644 index 0000000..8748675 --- /dev/null +++ b/plans/047-beautify-monitor-tab.md @@ -0,0 +1,9 @@ +# 047 - Beautify Monitor Tab + +## Goal +Redesign the node monitoring cards in the Monitor tab (监控标签) to make them more visually appealing. Incorporate a dynamic, clean layout mimicking the Nezha monitoring dashboard. + +## Tasks +- [x] Redesign `ServerCard` in `vite-frontend/src/pages/node/monitor-view.tsx` with gradients, neon statuses, clean layout, up/down arrows for speeds. +- [x] Update the realtime KPI cards in the Monitor detailed view to have subtle gradients and dynamically colored text. +- [x] Fix TypeScript linting issues with `Progress` component colors (`classNames` vs `color`). diff --git a/vite-frontend/src/pages/node/monitor-view.tsx b/vite-frontend/src/pages/node/monitor-view.tsx index 6b2eaf7..963dd1c 100644 --- a/vite-frontend/src/pages/node/monitor-view.tsx +++ b/vite-frontend/src/pages/node/monitor-view.tsx @@ -170,141 +170,85 @@ function ServerCard({ node, metric, onPress }: { node: any; metric: RealtimeNode return ( - -
-
-
-
-

{node.name}

- -
+ {/* Dynamic top gradient bar based on status */} +
+ + {/* Decorative background glow */} +
+ + +
+
+
+
+
-
- - {metric ? formatUptime(metric.uptime) : "-"} +
+

{node.name}

+
+ {isOnline ? "在线" : "离线"} +
- - {/* IP & Version row */} -
-
- 版本 - {metric ? "在线" : "-"} + +
+
+
+ CPU + {isOnline && metric ? `${metric.cpuUsage.toFixed(1)}%` : "-"} +
+ +
+
+
+ 内存 + {isOnline && metric ? `${metric.memoryUsage.toFixed(1)}%` : "-"} +
+
- {/* System metrics */} -
-
-
-
- CPU - {isOnline && metric ? `${metric.cpuUsage.toFixed(1)}%` : "-"} -
- -
-
-
- 内存 - {isOnline && metric ? `${metric.memoryUsage.toFixed(1)}%` : "-"} -
- +
+
+
+ ↑ + 上传
+ {isOnline && metric ? formatBytesPerSecond(metric.netOutSpeed) : "-"}
- -
-
-
上传
-
{isOnline && metric ? formatBytesPerSecond(metric.netOutSpeed) : "-"}
-
-
-
下载
-
{isOnline && metric ? formatBytesPerSecond(metric.netInSpeed) : "-"}
+
+
+
+ 下载 + ↓
+ {isOnline && metric ? formatBytesPerSecond(metric.netInSpeed) : "-"}
+
- {/* Traffic stats */} -
-
-
↑ 上行流量
-
- {isOnline && metric ? formatBytes(metric.netOutBytes) : "-"} -
-
-
-
↓ 下行流量
-
- {isOnline && metric ? formatBytes(metric.netInBytes) : "-"} -
-
+
+
+ + {metric ? formatUptime(metric.uptime) : "-"} +
+
+ + Load: {metric ? metric.load1.toFixed(2) : "-"}
- - {/* Extended metrics */} - {isOnline && metric && ( -
- {metric.diskUsage > 0 && ( -
-
- 磁盘 - {metric.diskUsage.toFixed(1)}% -
- -
- )} - {(metric.load1 > 0 || metric.load5 > 0 || metric.load15 > 0) && ( -
-
-
负载 1m
-
{metric.load1.toFixed(2)}
-
-
-
负载 5m
-
{metric.load5.toFixed(2)}
-
-
-
负载 15m
-
{metric.load15.toFixed(2)}
-
-
- )} - {(metric.tcpConns > 0 || metric.udpConns > 0) && ( -
-
-
TCP 连接
-
{metric.tcpConns}
-
-
-
UDP 连接
-
{metric.udpConns}
-
-
- )} -
- )}
@@ -1282,19 +1226,21 @@ export function MonitorView({ nodeMap }: MonitorViewProps) { {/* Realtime KPI cards */} {detailRealtimeMetric && ( -
+
{[ { label: "CPU", value: `${detailRealtimeMetric.cpuUsage.toFixed(1)}%`, color: getColorByUsage(detailRealtimeMetric.cpuUsage) }, { label: "内存", value: `${detailRealtimeMetric.memoryUsage.toFixed(1)}%`, color: getColorByUsage(detailRealtimeMetric.memoryUsage) }, { label: "磁盘", value: `${detailRealtimeMetric.diskUsage.toFixed(1)}%`, color: getColorByUsage(detailRealtimeMetric.diskUsage) }, - { label: "↓ 下行", value: formatBytesPerSecond(detailRealtimeMetric.netInSpeed), color: "success" as const }, - { label: "↑ 上行", value: formatBytesPerSecond(detailRealtimeMetric.netOutSpeed), color: "primary" as const }, + { label: "↓ 下行速度", value: formatBytesPerSecond(detailRealtimeMetric.netInSpeed), color: "success" as const }, + { label: "↑ 上行速度", value: formatBytesPerSecond(detailRealtimeMetric.netOutSpeed), color: "primary" as const }, { label: "运行时间", value: formatUptime(detailRealtimeMetric.uptime), color: "default" as const }, ].map((item) => ( - - - {item.label} - {item.value} + + + {item.label} + + {item.value} + ))} diff --git a/vite-frontend/src/pages/user.tsx b/vite-frontend/src/pages/user.tsx index c955a2f..0ad21a6 100644 --- a/vite-frontend/src/pages/user.tsx +++ b/vite-frontend/src/pages/user.tsx @@ -245,7 +245,6 @@ export default function UserPage() { // 分配新隧道权限相关状态 const [assignLoading, setAssignLoading] = useState(false); - const [isTunnelListExpanded, setIsTunnelListExpanded] = useState(false); const [batchTunnelSelections, setBatchTunnelSelections] = useState< Map >(new Map()); @@ -1519,7 +1518,7 @@ export default function UserPage() {

分配新权限

-
-

- 隧道列表 -

+
+ 流量限制、规则数量、到期时间、流量重置时间将自动继承用户设置 +
- {/* 顶部触发框 */} -
- setIsTunnelListExpanded(!isTunnelListExpanded) - } - > - 0 ? "text-primary-500 font-bold" : "text-default-400"}`} - > - {batchTunnelSelections.size > 0 - ? `已选 ${batchTunnelSelections.size} 项:` + - Array.from(batchTunnelSelections.keys()) - .map( - (id) => tunnels.find((t) => t.id === id)?.name, - ) - .join("、") - : "请选择隧道(勾选后配置限速)"} - - - - -
+
+ {tunnels.map((tunnel) => { + const isAssigned = isTunnelAssigned(tunnel.id); + const isSelected = batchTunnelSelections.has(tunnel.id); + const tunnelSpeedLimits = getSpeedLimitsForTunnel( + tunnel.id, + ); - {/* 列表悬浮层 */} - {isTunnelListExpanded && ( -
e.stopPropagation()} - > -
- {tunnels.map((tunnel) => { - const isAssigned = isTunnelAssigned(tunnel.id); - const isSelected = batchTunnelSelections.has( - tunnel.id, - ); - const tunnelSpeedLimits = getSpeedLimitsForTunnel( - tunnel.id, - ); - const currentSpeedId = batchTunnelSelections.get( - tunnel.id, - ); + return ( +
+ !isAssigned && toggleTunnelSelection(tunnel.id) + } + onKeyDown={(event) => { + if (isAssigned) { + return; + } - return ( -
{ - if (isAssigned) return; - e.stopPropagation(); - toggleTunnelSelection(tunnel.id); - }} + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + toggleTunnelSelection(tunnel.id); + } + }} + > +
+
+ event.stopPropagation()} + onKeyDown={(event) => event.stopPropagation()} + onValueChange={() => + toggleTunnelSelection(tunnel.id) + } + /> + -
- - - {tunnel.name} - -
+ {tunnel.name} +
+ {isAssigned && ( + + 已分配 + + )} +
- {/* 右侧限速选择:复刻 image_24879b */} - {isSelected && !isAssigned && ( -
e.stopPropagation()} - > - e.stopPropagation()} + onSelectionChange={(keys) => { + const selectedKey = Array.from(keys)[0] as + | string + | undefined; - updateTunnelSpeedLimit( - tunnel.id, - selectedKey - ? Number(selectedKey) - : null, - ); - }} + updateTunnelSpeedLimit( + tunnel.id, + selectedKey ? Number(selectedKey) : null, + ); + }} + > + {tunnelSpeedLimits.map((sl) => ( + - {tunnelSpeedLimits.map((sl) => ( - - {sl.name} - - ))} - -
- )} - {isAssigned && ( - - 已分配 - - )} + {sl.name} + + ))} +
- ); - })} -
-
- + )} +
+ ); + })} + {tunnels.length === 0 && ( +
+ 暂无可用隧道
)}
+