diff --git a/vite-frontend/src/pages/limit.tsx b/vite-frontend/src/pages/limit.tsx index a72314c..6e68198 100644 --- a/vite-frontend/src/pages/limit.tsx +++ b/vite-frontend/src/pages/limit.tsx @@ -18,6 +18,15 @@ import { ModalFooter, } from "@/shadcn-bridge/heroui/modal"; import { Chip } from "@/shadcn-bridge/heroui/chip"; +import { LayoutGrid, List } from "lucide-react"; +import { + Table, + TableHeader, + TableColumn, + TableBody, + TableRow, + TableCell, +} from "@/shadcn-bridge/heroui/table"; import { createSpeedLimit, getSpeedLimitList, @@ -51,6 +60,10 @@ export default function LimitPage() { "", ); const [isSearchVisible, setIsSearchVisible] = useState(false); + const [viewMode, setViewMode] = useLocalStorageState<"list" | "grid">( + "limit-view-mode", + "grid", + ); const filteredRules = useMemo(() => { if (!searchKeyword.trim()) return rules; @@ -231,13 +244,78 @@ export default function LimitPage() { /> - +
+ + +
{/* 统一卡片网格 */} {filteredRules.length > 0 ? ( + viewMode === "list" ? ( + + + 规则名称 + 速度限制 + 状态 + 操作 + + + {(rule) => ( + + {rule.name} + {rule.speed} Mbps + + + {rule.status === 1 ? "运行" : "异常"} + + + +
+ + +
+
+
+ )} +
+
+ ) : ( {filteredRules.map((rule) => ( @@ -324,6 +402,7 @@ export default function LimitPage() { ))} + ) ) : ( /* 空状态 */ diff --git a/vite-frontend/src/pages/node.tsx b/vite-frontend/src/pages/node.tsx index c455b4f..97ff29e 100644 --- a/vite-frontend/src/pages/node.tsx +++ b/vite-frontend/src/pages/node.tsx @@ -1768,7 +1768,15 @@ export default function NodePage() { /> ) : viewMode === "list" ? ( - +
) : ( -
+
状态 名称 @@ -1084,7 +1092,7 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) { const isOnline = node.connectionStatus === "online"; return ( - +
@@ -1536,7 +1544,15 @@ export function MonitorView({ nodeMap, viewMode = "grid" }: MonitorViewProps) {
) : modalResults.length > 0 ? ( -
+
时间 结果 diff --git a/vite-frontend/src/pages/node/tunnel-monitor-view.tsx b/vite-frontend/src/pages/node/tunnel-monitor-view.tsx index dfedfeb..9fc1a93 100644 --- a/vite-frontend/src/pages/node/tunnel-monitor-view.tsx +++ b/vite-frontend/src/pages/node/tunnel-monitor-view.tsx @@ -681,7 +681,15 @@ export function TunnelMonitorView({ viewMode = "grid" }: TunnelMonitorViewProps) ) : ( -
+
状态 名称 @@ -699,7 +707,7 @@ export function TunnelMonitorView({ viewMode = "grid" }: TunnelMonitorViewProps) : null; return ( - setDetailTunnelId(tunnel.id)}> + setDetailTunnelId(tunnel.id)}>
{isEnabled ? ( diff --git a/vite-frontend/src/pages/tunnel.tsx b/vite-frontend/src/pages/tunnel.tsx index 0ac955e..dfb913b 100644 --- a/vite-frontend/src/pages/tunnel.tsx +++ b/vite-frontend/src/pages/tunnel.tsx @@ -45,6 +45,15 @@ import { Alert } from "@/shadcn-bridge/heroui/alert"; import { Checkbox } from "@/shadcn-bridge/heroui/checkbox"; import { Progress } from "@/shadcn-bridge/heroui/progress"; import { Radio, RadioGroup } from "@/shadcn-bridge/heroui/radio"; +import { LayoutGrid, List } from "lucide-react"; +import { + Table, + TableHeader, + TableColumn, + TableBody, + TableRow, + TableCell, +} from "@/shadcn-bridge/heroui/table"; import { createTunnel, batchDeleteTunnelsWithForwards, @@ -181,6 +190,10 @@ export default function TunnelPage() { "", ); const [isSearchVisible, setIsSearchVisible] = useState(false); + const [viewMode, setViewMode] = useLocalStorageState<"list" | "grid">( + "tunnel-view-mode", + "grid", + ); // 模态框状态 const [modalOpen, setModalOpen] = useState(false); @@ -1523,6 +1536,14 @@ export default function TunnelPage() { > 批量 +
+ + {selectMode ? + 0} + onValueChange={(checked) => checked ? selectAll() : deselectAll()} + /> + : } + 隧道名称 + 类型 + 拓扑 + 流量统计 + 操作 + + + {(tunnel) => { + const typeDisplay = getTunnelTypeDisplay(tunnel.type); + const tunnelTypeChipClassName = + tunnel.type === 1 + ? "text-[10px] h-5 bg-primary-100 text-primary-800 border-primary-300 dark:bg-primary-900/45 dark:text-primary-200 dark:border-primary-700" + : "text-[10px] h-5 bg-success-100 text-success-800 border-success-300 dark:bg-success-900/35 dark:text-success-200 dark:border-success-700"; + + return ( + + {selectMode ? + toggleSelect(tunnel.id)} + /> + : } + {tunnel.name} + + + {typeDisplay.text} + + + +
+ + {tunnel.inNodeId?.length || 0}入口 + + → + + {tunnel.type === 2 ? tunnel.chainNodes?.length || 0 : 0}跳 + + → + + {tunnel.type === 2 ? tunnel.outNodeId?.length || 0 : tunnel.inNodeId?.length || 0}出口 + +
+
+ +
+ {getTunnelFlowDisplay(tunnel.flow)} + | + {tunnel.trafficRatio}x + {tunnel.type === 2 && tunnel.ipPreference && ( + <> + | + {tunnel.ipPreference === "v4" ? "IPv4" : "IPv6"} + + )} +
+
+ +
+ + + +
+
+
+ ); + }} +
+
+ ) : ( + ) ) : ( /* 空状态 */ diff --git a/vite-frontend/src/pages/user.tsx b/vite-frontend/src/pages/user.tsx index f6a140e..08844da 100644 --- a/vite-frontend/src/pages/user.tsx +++ b/vite-frontend/src/pages/user.tsx @@ -1080,7 +1080,15 @@ export default function UserPage() { ) : viewMode === "list" ? ( - +
用户名 流量统计 @@ -1888,8 +1896,9 @@ export default function UserPage() {