diff --git a/src/components/home/current-proxy-card.tsx b/src/components/home/current-proxy-card.tsx
index b96dbb05..1e8d140f 100644
--- a/src/components/home/current-proxy-card.tsx
+++ b/src/components/home/current-proxy-card.tsx
@@ -1,6 +1,7 @@
import {
AccessTimeRounded,
ChevronRight,
+ NetworkCheckRounded,
WifiOff as SignalError,
SignalWifi3Bar as SignalGood,
SignalWifi2Bar as SignalMedium,
@@ -25,13 +26,17 @@ import {
alpha,
useTheme,
} from "@mui/material";
+import { useLockFn } from "ahooks";
+import React from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import { EnhancedCard } from "@/components/home/enhanced-card";
import { useProxySelection } from "@/hooks/use-proxy-selection";
+import { useVerge } from "@/hooks/use-verge";
import { useAppData } from "@/providers/app-data-context";
+import { getGroupProxyDelays, providerHealthCheck } from "@/services/cmds";
import delayManager from "@/services/delay";
// 本地存储的键名
@@ -47,7 +52,9 @@ interface ProxyOption {
// 排序类型: 默认 | 按延迟 | 按字母
type ProxySortType = 0 | 1 | 2;
-function convertDelayColor(delayValue: number) {
+function convertDelayColor(
+ delayValue: number,
+): "success" | "warning" | "error" | "primary" | "default" {
const colorStr = delayManager.formatDelayColor(delayValue);
if (!colorStr) return "default";
@@ -67,7 +74,11 @@ function convertDelayColor(delayValue: number) {
}
}
-function getSignalIcon(delay: number) {
+function getSignalIcon(delay: number): {
+ icon: React.ReactElement;
+ text: string;
+ color: string;
+} {
if (delay < 0)
return { icon: , text: "未测试", color: "text.secondary" };
if (delay >= 10000)
@@ -81,20 +92,12 @@ function getSignalIcon(delay: number) {
return { icon: , text: "延迟极佳", color: "success.main" };
}
-// 简单的防抖函数
-function debounce(fn: Function, ms = 100) {
- let timeoutId: ReturnType;
- return function (this: any, ...args: any[]) {
- clearTimeout(timeoutId);
- timeoutId = setTimeout(() => fn.apply(this, args), ms);
- };
-}
-
export const CurrentProxyCard = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const theme = useTheme();
const { proxies, clashConfig, refreshProxy } = useAppData();
+ const { verge } = useVerge();
// 统一代理选择器
const { handleSelectChange } = useProxySelection({
@@ -172,6 +175,7 @@ export const CurrentProxyCard = () => {
// 根据模式确定初始组
if (isGlobalMode) {
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
setState((prev) => ({
...prev,
selection: {
@@ -180,6 +184,7 @@ export const CurrentProxyCard = () => {
},
}));
} else if (isDirectMode) {
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
setState((prev) => ({
...prev,
selection: {
@@ -189,6 +194,7 @@ export const CurrentProxyCard = () => {
}));
} else {
const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP);
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
setState((prev) => ({
...prev,
selection: {
@@ -203,6 +209,7 @@ export const CurrentProxyCard = () => {
useEffect(() => {
if (!proxies) return;
+ // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
setState((prev) => {
// 只保留 Selector 类型的组用于选择
const filteredGroups = proxies.groups
@@ -270,16 +277,23 @@ export const CurrentProxyCard = () => {
}, [proxies, isGlobalMode, isDirectMode]);
// 使用防抖包装状态更新
+ const timeoutRef = React.useRef | null>(null);
+
const debouncedSetState = useCallback(
- debounce((updateFn: (prev: ProxyState) => ProxyState) => {
- setState(updateFn);
- }, 300),
- [],
+ (updateFn: (prev: ProxyState) => ProxyState) => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ timeoutRef.current = setTimeout(() => {
+ setState(updateFn);
+ }, 300);
+ },
+ [setState],
);
// 处理代理组变更
const handleGroupChange = useCallback(
- (event: SelectChangeEvent) => {
+ (event: SelectChangeEvent) => {
if (isGlobalMode || isDirectMode) return;
const newGroup = event.target.value;
@@ -314,7 +328,7 @@ export const CurrentProxyCard = () => {
// 处理代理节点变更
const handleProxyChange = useCallback(
- (event: SelectChangeEvent) => {
+ (event: SelectChangeEvent) => {
if (isDirectMode) return;
const newProxy = event.target.value;
@@ -399,6 +413,85 @@ export const CurrentProxyCard = () => {
localStorage.setItem(STORAGE_KEY_SORT_TYPE, newSortType.toString());
}, [sortType]);
+ // 延迟测试
+ const handleCheckDelay = useLockFn(async () => {
+ const groupName = state.selection.group;
+ if (!groupName || isDirectMode) return;
+
+ console.log(`[CurrentProxyCard] 开始测试所有延迟,组: ${groupName}`);
+
+ const timeout = verge?.default_latency_timeout || 10000;
+
+ // 获取当前组的所有代理
+ const proxyNames: string[] = [];
+ const providers: Set = new Set();
+
+ if (isGlobalMode && proxies?.global) {
+ // 全局模式
+ const allProxies = proxies.global.all
+ .filter((p: any) => {
+ const name = typeof p === "string" ? p : p.name;
+ return name !== "DIRECT" && name !== "REJECT";
+ })
+ .map((p: any) => (typeof p === "string" ? p : p.name));
+
+ allProxies.forEach((name: string) => {
+ const proxy = state.proxyData.records[name];
+ if (proxy?.provider) {
+ providers.add(proxy.provider);
+ } else {
+ proxyNames.push(name);
+ }
+ });
+ } else {
+ // 规则模式
+ const group = state.proxyData.groups.find((g) => g.name === groupName);
+ if (group) {
+ group.all.forEach((name: string) => {
+ const proxy = state.proxyData.records[name];
+ if (proxy?.provider) {
+ providers.add(proxy.provider);
+ } else {
+ proxyNames.push(name);
+ }
+ });
+ }
+ }
+
+ console.log(
+ `[CurrentProxyCard] 找到代理数量: ${proxyNames.length}, 提供者数量: ${providers.size}`,
+ );
+
+ // 测试提供者的节点
+ if (providers.size > 0) {
+ console.log(`[CurrentProxyCard] 开始测试提供者节点`);
+ await Promise.allSettled(
+ [...providers].map((p) => providerHealthCheck(p)),
+ );
+ }
+
+ // 测试非提供者的节点
+ if (proxyNames.length > 0) {
+ const url = delayManager.getUrl(groupName);
+ console.log(`[CurrentProxyCard] 测试URL: ${url}, 超时: ${timeout}ms`);
+
+ try {
+ await Promise.race([
+ delayManager.checkListDelay(proxyNames, groupName, timeout),
+ getGroupProxyDelays(groupName, url, timeout),
+ ]);
+ console.log(`[CurrentProxyCard] 延迟测试完成,组: ${groupName}`);
+ } catch (error) {
+ console.error(
+ `[CurrentProxyCard] 延迟测试出错,组: ${groupName}`,
+ error,
+ );
+ }
+ }
+
+ refreshProxy();
+ });
+
// 排序代理函数(增加非空校验)
const sortProxies = useCallback(
(proxies: ProxyOption[]) => {
@@ -474,7 +567,7 @@ export const CurrentProxyCard = () => {
]);
// 获取排序图标
- const getSortIcon = () => {
+ const getSortIcon = (): React.ReactElement => {
switch (sortType) {
case 1:
return ;
@@ -486,7 +579,7 @@ export const CurrentProxyCard = () => {
};
// 获取排序提示文本
- const getSortTooltip = () => {
+ const getSortTooltip = (): string => {
switch (sortType) {
case 0:
return t("Sort by default");
@@ -517,13 +610,24 @@ export const CurrentProxyCard = () => {
}
iconColor={currentProxy ? "primary" : undefined}
action={
-
+
+
+
+
+
+
+
+
{getSortIcon()}
@@ -657,7 +761,7 @@ export const CurrentProxyCard = () => {
>
{isDirectMode
? null
- : proxyOptions.map((proxy, index) => {
+ : proxyOptions.map((proxy) => {
const delayValue =
state.proxyData.records[proxy.name] &&
state.selection.group
@@ -668,7 +772,7 @@ export const CurrentProxyCard = () => {
: -1;
return (