diff --git a/vite-frontend/src/pages/node.tsx b/vite-frontend/src/pages/node.tsx index fc78251..23ed558 100644 --- a/vite-frontend/src/pages/node.tsx +++ b/vite-frontend/src/pages/node.tsx @@ -1,5 +1,6 @@ import { useState, useEffect, useMemo, useCallback } from "react"; import toast from "react-hot-toast"; +import { useNavigate } from "react-router-dom"; import { DndContext, KeyboardSensor, @@ -64,6 +65,7 @@ import { useNodeOfflineTimers } from "@/pages/node/use-node-offline-timers"; import { useNodeRealtime } from "@/pages/node/use-node-realtime"; import { useLocalStorageState } from "@/hooks/use-local-storage-state"; import { loadStoredOrder, saveOrder } from "@/utils/order-storage"; +import { PlusIcon, DeleteIcon, CheckAllIcon, XCircleIcon, ListCheckIcon, XIcon, ArrowUpIcon, ShareIcon } from "@/components/icons"; interface Node { id: number; @@ -116,6 +118,8 @@ interface NodeForm { socks: number; // 0 关 1 开 } +type NodeTab = "local" | "remote"; + const SortableItem = ({ id, children, @@ -158,6 +162,7 @@ const SortableItem = ({ }; export default function NodePage() { + const navigate = useNavigate(); const [nodeList, setNodeList] = useState([]); const [nodeOrder, setNodeOrder] = useState([]); const [loading, setLoading] = useState(false); @@ -165,6 +170,10 @@ export default function NodePage() { "node-search-keyword", "", ); + const [activeTab, setActiveTab] = useLocalStorageState( + "node-active-tab", + "local", + ); const [isSearchVisible, setIsSearchVisible] = useState(false); const [dialogVisible, setDialogVisible] = useState(false); const [dialogTitle, setDialogTitle] = useState(""); @@ -963,7 +972,7 @@ export default function NodePage() { }; const selectAll = () => { - setSelectedIds(new Set(sortedNodes.map((n) => n.id))); + setSelectedIds(new Set(displayNodes.map((n) => n.id))); }; const deselectAll = () => { @@ -1062,94 +1071,169 @@ export default function NodePage() { return sortedByDb; }, [nodeList, nodeOrder, searchKeyword]); - const sortableNodeIds = useMemo( - () => sortedNodes.map((n) => n.id), + const localNodes = useMemo( + () => sortedNodes.filter((node) => node.isRemote !== 1), [sortedNodes], ); + const remoteNodes = useMemo( + () => sortedNodes.filter((node) => node.isRemote === 1), + [sortedNodes], + ); + + const displayNodes = useMemo( + () => (activeTab === "remote" ? remoteNodes : localNodes), + [activeTab, localNodes, remoteNodes], + ); + + const sortableNodeIds = useMemo( + () => displayNodes.map((n) => n.id), + [displayNodes], + ); + return ( -
-
- setIsSearchVisible(false)} - onOpen={() => setIsSearchVisible(true)} - /> +
+
+ +
-
-
+
+
+ setIsSearchVisible(false)} + onOpen={() => setIsSearchVisible(true)} + /> +
+ +
{selectMode ? ( <> - 已选 {selectedIds.size} 项 + 已选择 {selectedIds.size} 项 ) : ( <> + )} @@ -1176,6 +1260,15 @@ export default function NodePage() { className="h-64" message="暂无节点配置,点击上方按钮开始创建" /> + ) : displayNodes.length === 0 ? ( + ) : (
- {sortedNodes.map((node) => { + {displayNodes.map((node) => { const isRemoteNode = node.isRemote === 1; return ( @@ -1195,18 +1288,20 @@ export default function NodePage() { >
-
+
{selectMode && ( toggleSelect(node.id)} /> )} -

- {node.name} -

+
+

+ {node.name} +

+
-
+