mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
add: home card drag (#4215)
* add home page control buttons * Update UPDATELOG.md * update zh en * Revert "add home page control buttons" This reverts commite184843855. * Update UPDATELOG.md * Revert "update zh en" This reverts commitcbaddf59fb. * remove unnecessary code * fix: home.tsx * add react-beautiful-dnd script * add home page drag * fix: react-beautiful-dndA setup problem was encountered * Revert "fix: react-beautiful-dndA setup problem was encountered" This reverts commit81c34dd472. * fix: react-beautiful-dndA setup problem was encountered * Update types.d.ts * Revert "Update types.d.ts" This reverts commit854046cf2f. * update @types/react-beautiful-dnd * Update home.tsx * Update UPDATELOG.md * remove unnecessary components * Revert "add react-beautiful-dnd script" This reverts commite84d569225. * Reapply "add react-beautiful-dnd script" This reverts commit2379fd27c4. * fix: home page error
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
- 多版本画布流量图表,丰富可视化选项
|
||||
- 新增强制刷新 `Clash` 配置/节点缓存功能,提升更新响应速度
|
||||
- 增加代理请求缓存机制,减少重复 `API` 调用
|
||||
- 添加首页卡片移动 (暂测)
|
||||
|
||||
### 🚀 性能优化
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
"monaco-yaml": "^5.4.0",
|
||||
"nanoid": "^5.1.5",
|
||||
"react": "19.1.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-error-boundary": "6.0.0",
|
||||
@@ -84,6 +85,7 @@
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/react": "19.1.8",
|
||||
"@types/react-beautiful-dnd": "^13.1.8",
|
||||
"@types/react-dom": "19.1.6",
|
||||
"@vitejs/plugin-legacy": "^7.1.0",
|
||||
"@vitejs/plugin-react": "4.7.0",
|
||||
|
||||
121
pnpm-lock.yaml
generated
121
pnpm-lock.yaml
generated
@@ -116,6 +116,9 @@ importers:
|
||||
react:
|
||||
specifier: 19.1.0
|
||||
version: 19.1.0
|
||||
react-beautiful-dnd:
|
||||
specifier: ^13.1.1
|
||||
version: 13.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
react-chartjs-2:
|
||||
specifier: ^5.3.0
|
||||
version: 5.3.0(chart.js@4.5.0)(react@19.1.0)
|
||||
@@ -174,6 +177,9 @@ importers:
|
||||
'@types/react':
|
||||
specifier: 19.1.8
|
||||
version: 19.1.8
|
||||
'@types/react-beautiful-dnd':
|
||||
specifier: ^13.1.8
|
||||
version: 13.1.8
|
||||
'@types/react-dom':
|
||||
specifier: 19.1.6
|
||||
version: 19.1.6(@types/react@19.1.8)
|
||||
@@ -1586,6 +1592,11 @@ packages:
|
||||
'@types/hast@3.0.4':
|
||||
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
|
||||
|
||||
'@types/hoist-non-react-statics@3.3.7':
|
||||
resolution: {integrity: sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
||||
'@types/js-yaml@4.0.9':
|
||||
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
|
||||
|
||||
@@ -1610,11 +1621,17 @@ packages:
|
||||
'@types/prop-types@15.7.15':
|
||||
resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==}
|
||||
|
||||
'@types/react-beautiful-dnd@13.1.8':
|
||||
resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==}
|
||||
|
||||
'@types/react-dom@19.1.6':
|
||||
resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==}
|
||||
peerDependencies:
|
||||
'@types/react': ^19.0.0
|
||||
|
||||
'@types/react-redux@7.1.34':
|
||||
resolution: {integrity: sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==}
|
||||
|
||||
'@types/react-transition-group@4.4.12':
|
||||
resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==}
|
||||
peerDependencies:
|
||||
@@ -1846,6 +1863,9 @@ packages:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
css-box-model@1.2.1:
|
||||
resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
@@ -2262,6 +2282,9 @@ packages:
|
||||
mdast-util-to-string@4.0.0:
|
||||
resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==}
|
||||
|
||||
memoize-one@5.2.1:
|
||||
resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
|
||||
|
||||
memoizee@0.4.17:
|
||||
resolution: {integrity: sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==}
|
||||
engines: {node: '>=0.12'}
|
||||
@@ -2500,6 +2523,16 @@ packages:
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
|
||||
raf-schd@4.0.3:
|
||||
resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==}
|
||||
|
||||
react-beautiful-dnd@13.1.1:
|
||||
resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
|
||||
deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672'
|
||||
peerDependencies:
|
||||
react: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
|
||||
|
||||
react-chartjs-2@5.3.0:
|
||||
resolution: {integrity: sha512-UfZZFnDsERI3c3CZGxzvNJd02SHjaSJ8kgW1djn65H1KK8rehwTjyrRKOG3VTMG8wtHZ5rgAO5oTHtHi9GCCmw==}
|
||||
peerDependencies:
|
||||
@@ -2544,6 +2577,9 @@ packages:
|
||||
react-is@16.13.1:
|
||||
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
|
||||
|
||||
react-is@17.0.2:
|
||||
resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
|
||||
|
||||
react-is@19.1.0:
|
||||
resolution: {integrity: sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==}
|
||||
|
||||
@@ -2560,6 +2596,18 @@ packages:
|
||||
react: '>=16.8.0 <20.0.0'
|
||||
react-dom: '>=16.8.0 <20.0.0'
|
||||
|
||||
react-redux@7.2.9:
|
||||
resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.3 || ^17 || ^18
|
||||
react-dom: '*'
|
||||
react-native: '*'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
|
||||
react-refresh@0.17.0:
|
||||
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2601,6 +2649,9 @@ packages:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
|
||||
redux@4.2.1:
|
||||
resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==}
|
||||
|
||||
regenerate-unicode-properties@10.2.0:
|
||||
resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -2762,6 +2813,9 @@ packages:
|
||||
resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
tiny-invariant@1.3.3:
|
||||
resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
@@ -2841,6 +2895,11 @@ packages:
|
||||
peerDependencies:
|
||||
browserslist: '>= 4.21.0'
|
||||
|
||||
use-memo-one@1.1.3:
|
||||
resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
use-sync-external-store@1.5.0:
|
||||
resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==}
|
||||
peerDependencies:
|
||||
@@ -4404,6 +4463,11 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/unist': 3.0.3
|
||||
|
||||
'@types/hoist-non-react-statics@3.3.7(@types/react@19.1.8)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.8
|
||||
hoist-non-react-statics: 3.3.2
|
||||
|
||||
'@types/js-yaml@4.0.9': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
@@ -4424,10 +4488,21 @@ snapshots:
|
||||
|
||||
'@types/prop-types@15.7.15': {}
|
||||
|
||||
'@types/react-beautiful-dnd@13.1.8':
|
||||
dependencies:
|
||||
'@types/react': 19.1.8
|
||||
|
||||
'@types/react-dom@19.1.6(@types/react@19.1.8)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.8
|
||||
|
||||
'@types/react-redux@7.1.34':
|
||||
dependencies:
|
||||
'@types/hoist-non-react-statics': 3.3.7(@types/react@19.1.8)
|
||||
'@types/react': 19.1.8
|
||||
hoist-non-react-statics: 3.3.2
|
||||
redux: 4.2.1
|
||||
|
||||
'@types/react-transition-group@4.4.12(@types/react@19.1.8)':
|
||||
dependencies:
|
||||
'@types/react': 19.1.8
|
||||
@@ -4666,6 +4741,10 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
css-box-model@1.2.1:
|
||||
dependencies:
|
||||
tiny-invariant: 1.3.3
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
d@1.0.2:
|
||||
@@ -5153,6 +5232,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
|
||||
memoize-one@5.2.1: {}
|
||||
|
||||
memoizee@0.4.17:
|
||||
dependencies:
|
||||
d: 1.0.2
|
||||
@@ -5456,6 +5537,22 @@ snapshots:
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
raf-schd@4.0.3: {}
|
||||
|
||||
react-beautiful-dnd@13.1.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
css-box-model: 1.2.1
|
||||
memoize-one: 5.2.1
|
||||
raf-schd: 4.0.3
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
react-redux: 7.2.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
redux: 4.2.1
|
||||
use-memo-one: 1.1.3(react@19.1.0)
|
||||
transitivePeerDependencies:
|
||||
- react-native
|
||||
|
||||
react-chartjs-2@5.3.0(chart.js@4.5.0)(react@19.1.0):
|
||||
dependencies:
|
||||
chart.js: 4.5.0
|
||||
@@ -5489,6 +5586,8 @@ snapshots:
|
||||
|
||||
react-is@16.13.1: {}
|
||||
|
||||
react-is@17.0.2: {}
|
||||
|
||||
react-is@19.1.0: {}
|
||||
|
||||
react-markdown@10.1.0(@types/react@19.1.8)(react@19.1.0):
|
||||
@@ -5515,6 +5614,18 @@ snapshots:
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
react-redux@7.2.9(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@types/react-redux': 7.1.34
|
||||
hoist-non-react-statics: 3.3.2
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
react: 19.1.0
|
||||
react-is: 17.0.2
|
||||
optionalDependencies:
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
|
||||
react-refresh@0.17.0: {}
|
||||
|
||||
react-router-dom@7.7.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
|
||||
@@ -5549,6 +5660,10 @@ snapshots:
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
redux@4.2.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
|
||||
regenerate-unicode-properties@10.2.0:
|
||||
dependencies:
|
||||
regenerate: 1.4.2
|
||||
@@ -5741,6 +5856,8 @@ snapshots:
|
||||
es5-ext: 0.10.64
|
||||
next-tick: 1.1.0
|
||||
|
||||
tiny-invariant@1.3.3: {}
|
||||
|
||||
tinyglobby@0.2.14:
|
||||
dependencies:
|
||||
fdir: 6.4.6(picomatch@4.0.3)
|
||||
@@ -5821,6 +5938,10 @@ snapshots:
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
use-memo-one@1.1.3(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
|
||||
use-sync-external-store@1.5.0(react@19.1.0):
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Button,
|
||||
IconButton,
|
||||
useTheme,
|
||||
keyframes,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
@@ -29,7 +28,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import { ProxyTunCard } from "@/components/home/proxy-tun-card";
|
||||
import { ClashModeCard } from "@/components/home/clash-mode-card";
|
||||
import { EnhancedTrafficStats } from "@/components/home/enhanced-traffic-stats";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { HomeProfileCard } from "@/components/home/home-profile-card";
|
||||
import { EnhancedCard } from "@/components/home/enhanced-card";
|
||||
import { CurrentProxyCard } from "@/components/home/current-proxy-card";
|
||||
@@ -40,19 +39,14 @@ import { useLockFn } from "ahooks";
|
||||
import { entry_lightweight_mode, openWebUrl } from "@/services/cmds";
|
||||
import { TestCard } from "@/components/home/test-card";
|
||||
import { IpInfoCard } from "@/components/home/ip-info-card";
|
||||
|
||||
// 定义旋转动画
|
||||
const round = keyframes`
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
`;
|
||||
|
||||
// 辅助函数解析URL和过期时间
|
||||
function parseUrl(url?: string) {
|
||||
if (!url) return "-";
|
||||
if (url.startsWith("http")) return new URL(url).host;
|
||||
return "local";
|
||||
}
|
||||
import {
|
||||
DragDropContext,
|
||||
Droppable,
|
||||
Draggable,
|
||||
DropResult,
|
||||
DroppableProvided,
|
||||
DraggableProvided,
|
||||
} from "react-beautiful-dnd";
|
||||
|
||||
// 定义首页卡片设置接口
|
||||
interface HomeCardsSettings {
|
||||
@@ -69,6 +63,13 @@ interface HomeCardsSettings {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
// 卡片配置接口,包含排序信息
|
||||
interface CardConfig {
|
||||
id: string;
|
||||
size: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
// 首页设置对话框组件接口
|
||||
interface HomeSettingsDialogProps {
|
||||
open: boolean;
|
||||
@@ -77,6 +78,35 @@ interface HomeSettingsDialogProps {
|
||||
onSave: (cards: HomeCardsSettings) => void;
|
||||
}
|
||||
|
||||
// 确保对象符合HomeCardsSettings类型的辅助函数
|
||||
const ensureHomeCardsSettings = (obj: any): HomeCardsSettings => {
|
||||
const defaultSettings: HomeCardsSettings = {
|
||||
profile: true,
|
||||
proxy: true,
|
||||
network: true,
|
||||
mode: true,
|
||||
traffic: true,
|
||||
info: false,
|
||||
clashinfo: true,
|
||||
systeminfo: true,
|
||||
test: true,
|
||||
ip: true,
|
||||
};
|
||||
|
||||
if (!obj || typeof obj !== "object") return defaultSettings;
|
||||
|
||||
// 合并默认值和传入对象,确保所有必要属性都存在
|
||||
return Object.keys(defaultSettings).reduce((acc, key) => {
|
||||
return {
|
||||
...acc,
|
||||
[key]:
|
||||
typeof obj[key] === "boolean"
|
||||
? obj[key]
|
||||
: defaultSettings[key as keyof HomeCardsSettings],
|
||||
};
|
||||
}, {} as HomeCardsSettings);
|
||||
};
|
||||
|
||||
// 首页设置对话框组件
|
||||
const HomeSettingsDialog = ({
|
||||
open,
|
||||
@@ -88,15 +118,16 @@ const HomeSettingsDialog = ({
|
||||
const [cards, setCards] = useState<HomeCardsSettings>(homeCards);
|
||||
const { patchVerge } = useVerge();
|
||||
|
||||
const handleToggle = (key: string) => {
|
||||
setCards((prev: HomeCardsSettings) => ({
|
||||
const handleToggle = (key: keyof HomeCardsSettings) => {
|
||||
setCards((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
await patchVerge({ home_cards: cards });
|
||||
// 明确类型为HomeCardsSettings
|
||||
await patchVerge({ home_cards: cards as HomeCardsSettings });
|
||||
onSave(cards);
|
||||
onClose();
|
||||
};
|
||||
@@ -109,7 +140,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.profile || false}
|
||||
checked={cards.profile}
|
||||
onChange={() => handleToggle("profile")}
|
||||
/>
|
||||
}
|
||||
@@ -118,7 +149,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.proxy || false}
|
||||
checked={cards.proxy}
|
||||
onChange={() => handleToggle("proxy")}
|
||||
/>
|
||||
}
|
||||
@@ -127,7 +158,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.network || false}
|
||||
checked={cards.network}
|
||||
onChange={() => handleToggle("network")}
|
||||
/>
|
||||
}
|
||||
@@ -136,7 +167,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.mode || false}
|
||||
checked={cards.mode}
|
||||
onChange={() => handleToggle("mode")}
|
||||
/>
|
||||
}
|
||||
@@ -145,7 +176,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.traffic || false}
|
||||
checked={cards.traffic}
|
||||
onChange={() => handleToggle("traffic")}
|
||||
/>
|
||||
}
|
||||
@@ -154,7 +185,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.test || false}
|
||||
checked={cards.test}
|
||||
onChange={() => handleToggle("test")}
|
||||
/>
|
||||
}
|
||||
@@ -163,7 +194,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.ip || false}
|
||||
checked={cards.ip}
|
||||
onChange={() => handleToggle("ip")}
|
||||
/>
|
||||
}
|
||||
@@ -172,7 +203,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.clashinfo || false}
|
||||
checked={cards.clashinfo}
|
||||
onChange={() => handleToggle("clashinfo")}
|
||||
/>
|
||||
}
|
||||
@@ -181,7 +212,7 @@ const HomeSettingsDialog = ({
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
checked={cards.systeminfo || false}
|
||||
checked={cards.systeminfo}
|
||||
onChange={() => handleToggle("systeminfo")}
|
||||
/>
|
||||
}
|
||||
@@ -201,41 +232,74 @@ const HomeSettingsDialog = ({
|
||||
|
||||
export const HomePage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { verge } = useVerge();
|
||||
const { verge, patchVerge } = useVerge();
|
||||
const { current, mutateProfiles } = useProfiles();
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
// 设置弹窗的状态
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
// 卡片显示状态
|
||||
// 卡片显示状态 - 确保类型正确
|
||||
const [homeCards, setHomeCards] = useState<HomeCardsSettings>(
|
||||
(verge?.home_cards as HomeCardsSettings) || {
|
||||
profile: true,
|
||||
proxy: true,
|
||||
network: true,
|
||||
mode: true,
|
||||
traffic: true,
|
||||
clashinfo: true,
|
||||
systeminfo: true,
|
||||
test: true,
|
||||
ip: true,
|
||||
},
|
||||
ensureHomeCardsSettings(verge?.home_cards),
|
||||
);
|
||||
|
||||
// 导航到订阅页面
|
||||
const goToProfiles = () => {
|
||||
navigate("/profile");
|
||||
};
|
||||
// 卡片排序配置 - 默认为初始顺序
|
||||
const [cardOrder, setCardOrder] = useState<string[]>(
|
||||
// 明确断言类型
|
||||
(verge?.card_order as string[]) || [
|
||||
"profile",
|
||||
"proxy",
|
||||
"network",
|
||||
"mode",
|
||||
"traffic",
|
||||
"test",
|
||||
"ip",
|
||||
"clashinfo",
|
||||
"systeminfo",
|
||||
],
|
||||
);
|
||||
|
||||
// 导航到代理页面
|
||||
const goToProxies = () => {
|
||||
navigate("/");
|
||||
};
|
||||
// 当homeCards变化时,确保cardOrder中只包含启用的卡片
|
||||
useEffect(() => {
|
||||
const enabledCards = Object.entries(homeCards)
|
||||
.filter(([_, enabled]) => enabled)
|
||||
.map(([id]) => id);
|
||||
|
||||
// 导航到设置页面
|
||||
const goToSettings = () => {
|
||||
navigate("/settings");
|
||||
// 过滤掉已禁用的卡片
|
||||
const filteredOrder = cardOrder.filter((id) => enabledCards.includes(id));
|
||||
|
||||
// 添加新启用但不在排序中的卡片
|
||||
const newCards = enabledCards.filter((id) => !filteredOrder.includes(id));
|
||||
|
||||
setCardOrder([...filteredOrder, ...newCards]);
|
||||
}, [homeCards]);
|
||||
|
||||
// 保存卡片排序
|
||||
const saveCardOrder = useLockFn(async (order: string[]) => {
|
||||
await patchVerge({ card_order: order } as any);
|
||||
setCardOrder(order);
|
||||
});
|
||||
|
||||
// 处理拖拽结束
|
||||
const handleDragEnd = (result: DropResult) => {
|
||||
const { destination, source, draggableId } = result;
|
||||
|
||||
// 拖拽到无效位置或原位置,不做处理
|
||||
if (
|
||||
!destination ||
|
||||
(destination.droppableId === source.droppableId &&
|
||||
destination.index === source.index)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 重新排序
|
||||
const newOrder = Array.from(cardOrder);
|
||||
newOrder.splice(source.index, 1);
|
||||
newOrder.splice(destination.index, 0, draggableId);
|
||||
|
||||
// 保存新顺序
|
||||
saveCardOrder(newOrder);
|
||||
};
|
||||
|
||||
// 文档链接函数
|
||||
@@ -243,12 +307,12 @@ export const HomePage = () => {
|
||||
return openWebUrl("https://clash-verge-rev.github.io/index.html");
|
||||
});
|
||||
|
||||
// 新增:打开设置弹窗
|
||||
// 卡片设置弹窗
|
||||
const openSettings = () => {
|
||||
setSettingsOpen(true);
|
||||
};
|
||||
|
||||
// 新增:保存设置时用requestIdleCallback/setTimeout
|
||||
// 保存勾选设置
|
||||
const handleSaveSettings = (newCards: HomeCardsSettings) => {
|
||||
if (window.requestIdleCallback) {
|
||||
window.requestIdleCallback(() => setHomeCards(newCards));
|
||||
@@ -257,6 +321,68 @@ export const HomePage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 获取卡片配置信息
|
||||
const getCardConfig = (id: string): CardConfig => {
|
||||
const configs: Record<string, CardConfig> = {
|
||||
profile: { id: "profile", size: 6, enabled: homeCards.profile },
|
||||
proxy: { id: "proxy", size: 6, enabled: homeCards.proxy },
|
||||
network: { id: "network", size: 6, enabled: homeCards.network },
|
||||
mode: { id: "mode", size: 6, enabled: homeCards.mode },
|
||||
traffic: { id: "traffic", size: 12, enabled: homeCards.traffic },
|
||||
test: { id: "test", size: 6, enabled: homeCards.test },
|
||||
ip: { id: "ip", size: 6, enabled: homeCards.ip },
|
||||
clashinfo: { id: "clashinfo", size: 6, enabled: homeCards.clashinfo },
|
||||
systeminfo: { id: "systeminfo", size: 6, enabled: homeCards.systeminfo },
|
||||
};
|
||||
|
||||
if (!configs[id]) {
|
||||
console.warn(`检测到未知卡片ID: ${id},使用默认配置`);
|
||||
return { id, size: 6, enabled: false };
|
||||
}
|
||||
|
||||
return configs[id];
|
||||
};
|
||||
|
||||
// 渲染卡片内容
|
||||
const renderCardContent = (id: string) => {
|
||||
switch (id) {
|
||||
case "profile":
|
||||
return (
|
||||
<HomeProfileCard
|
||||
current={current}
|
||||
onProfileUpdated={mutateProfiles}
|
||||
/>
|
||||
);
|
||||
case "proxy":
|
||||
return <CurrentProxyCard />;
|
||||
case "network":
|
||||
return <NetworkSettingsCard />;
|
||||
case "mode":
|
||||
return <ClashModeEnhancedCard />;
|
||||
case "traffic":
|
||||
return (
|
||||
<EnhancedCard
|
||||
title={t("Traffic Stats")}
|
||||
icon={<SpeedOutlined />}
|
||||
iconColor="secondary"
|
||||
>
|
||||
<EnhancedTrafficStats />
|
||||
</EnhancedCard>
|
||||
);
|
||||
case "test":
|
||||
return <TestCard />;
|
||||
case "ip":
|
||||
return <IpInfoCard />;
|
||||
case "clashinfo":
|
||||
return <ClashInfoCard />;
|
||||
case "systeminfo":
|
||||
return <SystemInfoCard />;
|
||||
default:
|
||||
console.warn(`无法渲染未知卡片: ${id}`);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BasePage
|
||||
title={t("Label-Home")}
|
||||
@@ -285,73 +411,62 @@ export const HomePage = () => {
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Grid container spacing={1.5} columns={{ xs: 6, sm: 6, md: 12 }}>
|
||||
{/* 订阅和当前节点部分 */}
|
||||
{homeCards.profile && (
|
||||
<Grid size={6}>
|
||||
<HomeProfileCard
|
||||
current={current}
|
||||
onProfileUpdated={mutateProfiles}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{homeCards.proxy && (
|
||||
<Grid size={6}>
|
||||
<CurrentProxyCard />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* 代理和网络设置区域 */}
|
||||
{homeCards.network && (
|
||||
<Grid size={6}>
|
||||
<NetworkSettingsCard />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{homeCards.mode && (
|
||||
<Grid size={6}>
|
||||
<ClashModeEnhancedCard />
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{/* 增强的流量统计区域 */}
|
||||
{homeCards.traffic && (
|
||||
<Grid size={12}>
|
||||
<EnhancedCard
|
||||
title={t("Traffic Stats")}
|
||||
icon={<SpeedOutlined />}
|
||||
iconColor="secondary"
|
||||
{/* 拖拽上下文 */}
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
<Droppable
|
||||
droppableId="home-cards"
|
||||
isDropDisabled={false}
|
||||
isCombineEnabled={false}
|
||||
ignoreContainerClipping={false}
|
||||
>
|
||||
<EnhancedTrafficStats />
|
||||
</EnhancedCard>
|
||||
{(provided: DroppableProvided) => (
|
||||
<Grid
|
||||
container
|
||||
spacing={1.5}
|
||||
columns={{ xs: 6, sm: 6, md: 12 }}
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{cardOrder
|
||||
.filter((id) => {
|
||||
const config = getCardConfig(id);
|
||||
return homeCards[id] && config.enabled;
|
||||
})
|
||||
.map((id, index) => {
|
||||
const config = getCardConfig(id);
|
||||
if (!config) return null;
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
key={id}
|
||||
draggableId={id}
|
||||
index={index}
|
||||
isDragDisabled={false}
|
||||
>
|
||||
{(provided: DraggableProvided) => (
|
||||
<Grid
|
||||
size={config.size}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
sx={{
|
||||
cursor: "grab",
|
||||
"&:active": {
|
||||
cursor: "grabbing",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{renderCardContent(id)}
|
||||
</Grid>
|
||||
)}
|
||||
{/* 测试网站部分 */}
|
||||
{homeCards.test && (
|
||||
<Grid size={6}>
|
||||
<TestCard />
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</Grid>
|
||||
)}
|
||||
{/* IP信息卡片 */}
|
||||
{homeCards.ip && (
|
||||
<Grid size={6}>
|
||||
<IpInfoCard />
|
||||
</Grid>
|
||||
)}
|
||||
{/* Clash信息 */}
|
||||
{homeCards.clashinfo && (
|
||||
<Grid size={6}>
|
||||
<ClashInfoCard />
|
||||
</Grid>
|
||||
)}
|
||||
{/* 系统信息 */}
|
||||
{homeCards.systeminfo && (
|
||||
<Grid size={6}>
|
||||
<SystemInfoCard />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
|
||||
{/* 首页设置弹窗 */}
|
||||
<HomeSettingsDialog
|
||||
|
||||
7
src/services/types.d.ts
vendored
7
src/services/types.d.ts
vendored
@@ -822,6 +822,7 @@ interface IVergeConfig {
|
||||
verge_tproxy_enabled?: boolean;
|
||||
verge_socks_enabled?: boolean;
|
||||
verge_http_enabled?: boolean;
|
||||
card_order?: string[];
|
||||
enable_proxy_guard?: boolean;
|
||||
enable_bypass_check?: boolean;
|
||||
use_default_bypass?: boolean;
|
||||
@@ -860,6 +861,12 @@ interface IVergeConfig {
|
||||
enable_external_controller?: boolean;
|
||||
}
|
||||
|
||||
interface CardConfig {
|
||||
id: string;
|
||||
size: number;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
interface IWebDavFile {
|
||||
filename: string;
|
||||
href: string;
|
||||
|
||||
Reference in New Issue
Block a user