diff --git a/src/components/proxy/proxy-group-navigator.tsx b/src/components/proxy/proxy-group-navigator.tsx new file mode 100644 index 00000000..4999da49 --- /dev/null +++ b/src/components/proxy/proxy-group-navigator.tsx @@ -0,0 +1,94 @@ +import { Box, Button, Tooltip } from "@mui/material"; +import { useCallback, useMemo } from "react"; + +interface ProxyGroupNavigatorProps { + proxyGroupNames: string[]; + onGroupLocation: (groupName: string) => void; +} + +// 提取代理组名的第一个字符 +const getGroupDisplayChar = (groupName: string): string => { + if (!groupName) return "?"; + + // 直接返回第一个字符,支持表情符号 + const firstChar = Array.from(groupName)[0]; + return firstChar || "?"; +}; + +export const ProxyGroupNavigator = ({ + proxyGroupNames, + onGroupLocation, +}: ProxyGroupNavigatorProps) => { + const handleGroupClick = useCallback( + (groupName: string) => { + onGroupLocation(groupName); + }, + [onGroupLocation], + ); + + // 处理代理组数据,去重和排序 + const processedGroups = useMemo(() => { + return proxyGroupNames + .filter((name) => name && name.trim()) + .map((name) => ({ + name, + displayChar: getGroupDisplayChar(name), + })); + }, [proxyGroupNames]); + + if (processedGroups.length === 0) { + return null; + } + + return ( + + {processedGroups.map(({ name, displayChar }) => ( + + + + ))} + + ); +}; diff --git a/src/components/proxy/proxy-groups.tsx b/src/components/proxy/proxy-groups.tsx index 889e66b0..476f5f90 100644 --- a/src/components/proxy/proxy-groups.tsx +++ b/src/components/proxy/proxy-groups.tsx @@ -10,10 +10,11 @@ import { Menu, MenuItem, Divider, + Button, } from "@mui/material"; import { ArchiveOutlined, ExpandMoreRounded } from "@mui/icons-material"; import { useLockFn } from "ahooks"; -import { useRef, useState, useEffect, useCallback } from "react"; +import { useRef, useState, useEffect, useCallback, useMemo } from "react"; import useSWR from "swr"; import { useTranslation } from "react-i18next"; import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"; @@ -25,6 +26,7 @@ import { providerHealthCheck, getGroupProxyDelays, updateProxyChainConfigInRuntime, + getRuntimeConfig, } from "@/services/cmds"; import delayManager from "@/services/delay"; @@ -33,6 +35,7 @@ import { ScrollTopButton } from "../layout/scroll-top-button"; import { ProxyChain } from "./proxy-chain"; import { ProxyRender } from "./proxy-render"; +import { ProxyGroupNavigator } from "./proxy-group-navigator"; import { useRenderList } from "./use-render-list"; interface Props { @@ -326,6 +329,45 @@ export const ProxyGroups = (props: Props) => { } }; + // 获取运行时配置 + const { data: runtimeConfig } = useSWR("getRuntimeConfig", getRuntimeConfig, { + revalidateOnFocus: false, + revalidateIfStale: true, + }); + + // 获取所有代理组名称 + const getProxyGroupNames = useCallback(() => { + const config = runtimeConfig as any; + if (!config?.["proxy-groups"]) return []; + + return config["proxy-groups"] + .map((group: any) => group.name) + .filter((name: string) => name && name.trim() !== ""); + }, [runtimeConfig]); + + // 定位到指定的代理组 + const handleGroupLocationByName = useCallback( + (groupName: string) => { + const index = renderList.findIndex( + (item) => item.type === 0 && item.group?.name === groupName, + ); + + if (index >= 0) { + virtuosoRef.current?.scrollToIndex?.({ + index, + align: "start", + behavior: "smooth", + }); + } + }, + [renderList], + ); + + const proxyGroupNames = useMemo( + () => getProxyGroupNames(), + [getProxyGroupNames], + ); + if (mode === "direct") { return ; } @@ -522,6 +564,14 @@ export const ProxyGroups = (props: Props) => {
+ {/* 代理组导航栏 */} + {mode === "rule" && ( + + )} +