import { useEffect, useMemo, useRef } from 'react' import { useRuntimeConfig } from '@/hooks/use-clash' import { useVerge } from '@/hooks/use-verge' import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context' import delayManager from '@/services/delay' import { debugLog } from '@/utils/debug' import { filterSort } from './use-filter-sort' import { DEFAULT_STATE, useHeadStateNew, type HeadState, } from './use-head-state' import { useWindowWidth } from './use-window-width' // 定义代理项接口 interface IProxyItem { name: string type: string udp: boolean xudp: boolean tfo: boolean mptcp: boolean smux: boolean history: { time: string delay: number }[] provider?: string testUrl?: string [key: string]: any // 添加索引签名以适应其他可能的属性 } // 代理组类型 type ProxyGroup = { name: string type: string udp: boolean xudp: boolean tfo: boolean mptcp: boolean smux: boolean history: { time: string delay: number }[] now: string all: IProxyItem[] hidden?: boolean icon?: string testUrl?: string provider?: string } export interface IRenderItem { // 组 | head | item | empty | item col type: 0 | 1 | 2 | 3 | 4 key: string group: ProxyGroup proxy?: IProxyItem col?: number proxyCol?: IProxyItem[] headState?: HeadState // 新增支持图标和其他元数据 icon?: string provider?: string testUrl?: string } type GroupCache = { now: string all: IProxyItem[] headState: HeadState col: number latencyTimeout: number | undefined items: IRenderItem[] } // 优化列布局计算 const calculateColumns = (width: number, configCol: number): number => { if (configCol > 0 && configCol < 6) return configCol if (width > 1920) return 5 if (width > 1450) return 4 if (width > 1024) return 3 if (width > 900) return 2 if (width >= 600) return 2 return 1 } // 优化分组逻辑 const groupProxies = (list: T[], size: number): T[][] => { return list.reduce((acc, item) => { const lastGroup = acc[acc.length - 1] if (!lastGroup || lastGroup.length >= size) { acc.push([item]) } else { lastGroup.push(item) } return acc }, [] as T[][]) } export const useRenderList = ( mode: string, isChainMode?: boolean, selectedGroup?: string | null, ) => { // 使用全局数据提供者 const { proxies: proxiesData } = useProxiesData() const { refreshProxy } = useAppRefreshers() const { verge } = useVerge() const { width } = useWindowWidth() const [headStates, setHeadState] = useHeadStateNew() const latencyTimeout = verge?.default_latency_timeout // 获取运行时配置用于链式代理模式 const { data: runtimeConfig } = useRuntimeConfig(!!isChainMode) // 计算列数 const col = useMemo( () => calculateColumns(width, verge?.proxy_layout_column || 6), [width, verge?.proxy_layout_column], ) // 确保代理数据加载 useEffect(() => { if (!proxiesData) return const { groups, proxies } = proxiesData if ( (mode === 'rule' && !groups.length) || (mode === 'global' && proxies.length < 2) ) { const handle = setTimeout(() => refreshProxy(), 500) return () => clearTimeout(handle) } }, [proxiesData, mode, refreshProxy]) // 链式代理模式节点自动计算延迟 useEffect(() => { if (!isChainMode || !runtimeConfig) return const allProxies: IProxyItem[] = Object.values( (runtimeConfig as any).proxies || {}, ) if (allProxies.length === 0) return // 设置组监听器,当有延迟更新时自动刷新 const groupListener = () => { debugLog('[ChainMode] 延迟更新,刷新UI') refreshProxy() } delayManager.setGroupListener('chain-mode', groupListener) const calculateDelays = async () => { try { const timeout = verge?.default_latency_timeout || 10000 debugLog(`[ChainMode] 开始计算 ${allProxies.length} 个节点的延迟`) // 使用 delayManager 计算延迟,每个节点计算完成后会自动触发监听器刷新界面 delayManager.checkListDelay(allProxies, 'chain-mode', timeout) } catch (error) { console.error('Failed to calculate delays for chain mode:', error) } } // 延迟执行避免阻塞 const handle = setTimeout(calculateDelays, 100) return () => { clearTimeout(handle) // 清理组监听器 delayManager.removeGroupListener('chain-mode') } }, [isChainMode, runtimeConfig, verge?.default_latency_timeout, refreshProxy]) const groupCacheRef = useRef>(new Map()) const prevListRef = useRef([]) // 处理渲染列表 const renderList: IRenderItem[] = useMemo(() => { if (!proxiesData) return [] // 链式代理模式下,显示代理组和其节点 if (isChainMode && runtimeConfig && mode === 'rule') { // 使用正常的规则模式代理组 const allGroups = proxiesData.groups.length ? proxiesData.groups : [proxiesData.global!] // 如果选择了特定代理组,只显示该组的节点 if (selectedGroup) { const targetGroup = allGroups.find((g: any) => g.name === selectedGroup) if (targetGroup) { const proxies = filterSort( targetGroup.all, targetGroup.name, '', 0, latencyTimeout, ) if (col > 1) { return groupProxies(proxies, col).map((proxyCol, colIndex) => ({ type: 4, key: `chain-col-${selectedGroup}-${colIndex}`, group: targetGroup, headState: DEFAULT_STATE, col, proxyCol, provider: proxyCol[0]?.provider, })) } else { return proxies.map((proxy) => ({ type: 2, key: `chain-${selectedGroup}-${proxy!.name}`, group: targetGroup, proxy, headState: DEFAULT_STATE, provider: proxy.provider, })) } } return [] } // 如果没有选择特定组,显示第一个组的节点(如果有组的话) if (allGroups.length > 0) { const firstGroup = allGroups[0] const proxies = filterSort( firstGroup.all, firstGroup.name, '', 0, latencyTimeout, ) if (col > 1) { return groupProxies(proxies, col).map((proxyCol, colIndex) => ({ type: 4, key: `chain-col-first-${colIndex}`, group: firstGroup, headState: DEFAULT_STATE, col, proxyCol, provider: proxyCol[0]?.provider, })) } else { return proxies.map((proxy) => ({ type: 2, key: `chain-first-${proxy!.name}`, group: firstGroup, proxy, headState: DEFAULT_STATE, provider: proxy.provider, })) } } // 如果没有组,显示所有节点 const allProxies: IProxyItem[] = allGroups.flatMap( (group: any) => group.all, ) // 为每个节点获取延迟信息 const proxiesWithDelay = allProxies.map((proxy) => { const delay = delayManager.getDelay(proxy.name, 'chain-mode') return { ...proxy, // 如果delayManager有延迟数据,更新history history: delay >= 0 ? [{ time: new Date().toISOString(), delay }] : proxy.history || [], } }) // 创建一个虚拟的组来容纳所有节点 const virtualGroup: ProxyGroup = { name: 'All Proxies', type: 'Selector', udp: false, xudp: false, tfo: false, mptcp: false, smux: false, history: [], now: '', all: proxiesWithDelay, } if (col > 1) { return groupProxies(proxiesWithDelay, col).map( (proxyCol, colIndex) => ({ type: 4, key: `chain-col-all-${colIndex}`, group: virtualGroup, headState: DEFAULT_STATE, col, proxyCol, provider: proxyCol[0]?.provider, }), ) } else { return proxiesWithDelay.map((proxy) => ({ type: 2, key: `chain-all-${proxy.name}`, group: virtualGroup, proxy, headState: DEFAULT_STATE, provider: proxy.provider, })) } } // 链式代理模式下的其他模式(如global)仍显示所有节点 if (isChainMode && runtimeConfig) { // 从运行时配置直接获取 proxies 列表 (需要类型断言) const allProxies: IProxyItem[] = Object.values( (runtimeConfig as any).proxies || {}, ) // 为每个节点获取延迟信息 const proxiesWithDelay = allProxies.map((proxy) => { const delay = delayManager.getDelay(proxy.name, 'chain-mode') return { ...proxy, // 如果delayManager有延迟数据,更新history history: delay >= 0 ? [{ time: new Date().toISOString(), delay }] : proxy.history || [], } }) // 创建一个虚拟的组来容纳所有节点 const virtualGroup: ProxyGroup = { name: 'All Proxies', type: 'Selector', udp: false, xudp: false, tfo: false, mptcp: false, smux: false, history: [], now: '', all: proxiesWithDelay, } // 返回节点列表(不显示组头) if (col > 1) { return groupProxies(proxiesWithDelay, col).map( (proxyCol, colIndex) => ({ type: 4, key: `chain-col-${colIndex}`, group: virtualGroup, headState: DEFAULT_STATE, col, proxyCol, provider: proxyCol[0]?.provider, }), ) } else { return proxiesWithDelay.map((proxy) => ({ type: 2, key: `chain-${proxy.name}`, group: virtualGroup, proxy, headState: DEFAULT_STATE, provider: proxy.provider, })) } } // 正常模式的渲染逻辑 const useRule = mode === 'rule' || mode === 'script' const renderGroups = useRule && proxiesData.groups.length ? proxiesData.groups : [proxiesData.global!] const cache = groupCacheRef.current let anyChanged = false const retList = renderGroups.flatMap((group: ProxyGroup) => { const headState = headStates[group.name] || DEFAULT_STATE const cached = cache.get(group.name) if ( cached && cached.now === group.now && cached.all === group.all && cached.headState === headState && cached.col === col && cached.latencyTimeout === latencyTimeout ) { return cached.items } anyChanged = true const ret: IRenderItem[] = [ { type: 0, key: group.name, group, headState, icon: group.icon, testUrl: group.testUrl, }, ] if (headState?.open || !useRule) { const proxies = filterSort( group.all, group.name, headState.filterText, headState.sortType, latencyTimeout, { matchCase: headState.filterMatchCase, matchWholeWord: headState.filterMatchWholeWord, useRegularExpression: headState.filterUseRegularExpression, }, ) // 全局模式下,添加组头 if (!useRule) { ret.push({ type: 1, key: `head-${group.name}`, group, headState, }) } if (!proxies.length) { ret.push({ type: 3, key: `empty-${group.name}`, group, headState, }) } else if (col > 1) { ret.push( ...groupProxies(proxies, col).map((proxyCol, colIndex) => ({ type: 4 as const, key: `col-${group.name}-${proxyCol[0].name}-${colIndex}`, group, headState, col, proxyCol, provider: proxyCol[0].provider, })), ) } else { ret.push( ...proxies.map((proxy) => ({ type: 2 as const, key: `${group.name}-${proxy!.name}`, group, proxy, headState, provider: proxy.provider, })), ) } } cache.set(group.name, { now: group.now, all: group.all, headState, col, latencyTimeout, items: ret, }) return ret }) const filtered = !useRule ? retList.slice(1) : retList.filter((item: IRenderItem) => !item.group.hidden) if (!anyChanged && prevListRef.current.length === filtered.length) { return prevListRef.current } prevListRef.current = filtered return filtered }, [ headStates, proxiesData, mode, col, isChainMode, runtimeConfig, selectedGroup, latencyTimeout, ]) return { renderList, onProxies: refreshProxy, onHeadState: setHeadState, currentColumns: col, } } // 优化建议:如有大数据量,建议用虚拟滚动(已在 ProxyGroups 组件中实现),此处无需额外处理。