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() {
>
批量
+