import { useMemo } from 'react' import { Button, Card, Input, Switch, Table } from '../../../../shared/components' import type { SortOrder, TableColumn } from '../../../../shared/components/Table' import type { ProxyIPHealthResult } from '../../types' import { BUILTIN_PROXY_IDS, sourceHostLabel, type ProxyDisplayInfo } from './helpers' interface ProxyPoolTableCardProps { allFilteredSelected: boolean checkingIPHealthIds: Set data: ProxyDisplayInfo[] filterGroup: string filterKeyword: string filterProtocol: string globalAutoRefreshEnabled: boolean globalRefreshInterval: number globalRefreshIntervalM: string groups: string[] ipHealthMap: Record loading: boolean onCheckOneIPHealth: (record: ProxyDisplayInfo) => void onClearFilters: () => void onDelete: (proxyId: string) => void onEdit: (record: ProxyDisplayInfo) => void onFilterGroupChange: (nextValue: string) => void onFilterKeywordChange: (nextValue: string) => void onFilterProtocolChange: (nextValue: string) => void onGlobalAutoRefreshEnabledChange: (checked: boolean) => void onGlobalRefreshIntervalMChange: (nextValue: string) => void onOpenBatchDelete: () => void onOpenIPHealthDetail: (proxyId: string) => void onRefreshSingleSource: (sourceId: string) => void onSort: (next: { column: string; order: SortOrder }) => void onTestOne: (record: ProxyDisplayInfo) => void onToggleAll: () => void onToggleOne: (proxyId: string) => void protocolOptions: string[] refreshingSourceIds: Set selectedCount: number selectedIds: Set someFilteredSelected: boolean sortColumn: string sortOrder: SortOrder latencyMap: Record } export function ProxyPoolTableCard({ allFilteredSelected, checkingIPHealthIds, data, filterGroup, filterKeyword, filterProtocol, globalAutoRefreshEnabled, globalRefreshInterval, globalRefreshIntervalM, groups, ipHealthMap, loading, onCheckOneIPHealth, onClearFilters, onDelete, onEdit, onFilterGroupChange, onFilterKeywordChange, onFilterProtocolChange, onGlobalAutoRefreshEnabledChange, onGlobalRefreshIntervalMChange, onOpenBatchDelete, onOpenIPHealthDetail, onRefreshSingleSource, onSort, onTestOne, onToggleAll, onToggleOne, protocolOptions, refreshingSourceIds, selectedCount, selectedIds, someFilteredSelected, sortColumn, sortOrder, latencyMap, }: ProxyPoolTableCardProps) { const hasActiveFilters = filterProtocol !== 'all' || !!filterKeyword || filterGroup !== 'all' const renderLatency = (record: ProxyDisplayInfo) => { if (record.proxyConfig === 'direct://') { return 不适用 } const value = latencyMap[record.proxyId] if (value === undefined) return - if (value === -1) return 测试中... if (value === -2) return 超时 if (value === -3) return 不支持 if (value === -4) return 失败 const color = value < 200 ? 'text-green-500' : value < 500 ? 'text-yellow-500' : 'text-red-500' return {value} ms } const renderIPHealth = (record: ProxyDisplayInfo) => { if (record.proxyConfig === 'direct://') { return 不适用 } if (checkingIPHealthIds.has(record.proxyId)) { return 检测中... } const result = ipHealthMap[record.proxyId] if (!result) return - if (!result.ok) { return (
失败
) } const location = [result.country, result.region, result.city].filter(Boolean).join(' / ') return (
{result.ip || '-'}
{`fraud ${result.fraudScore} | ${result.isResidential ? '住宅' : '机房'}${location ? ` | ${location}` : ''}`}
) } const columns = useMemo[]>(() => [ { key: 'checkbox', title: '', width: '40px', render: (_, record) => ( onToggleOne(record.proxyId)} onClick={event => event.stopPropagation()} className="w-4 h-4 rounded border-[var(--color-border-default)] accent-[var(--color-accent)] cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed" /> ), }, { key: 'proxyName', title: '代理名称', width: '180px', sortable: true }, { key: 'groupName', title: '分组', width: '100px', sortable: true, render: (value) => value ? {value} : '-', }, { key: 'source', title: '来源', width: '180px', render: (_, record) => { if (!record.sourceUrl) return '-' const host = sourceHostLabel(record.sourceUrl) return (
{host}
{globalAutoRefreshEnabled ? `自动刷新 ${globalRefreshInterval} 分钟(全局)` : '手动刷新'}
) }, }, { key: 'type', title: '类型', width: '90px', sortable: true }, { key: 'server', title: '服务器', width: '180px', sortable: true }, { key: 'port', title: '端口', width: '80px', sortable: true, render: (value) => value || '-' }, { key: 'latency', title: '延迟', width: '90px', sortable: true, render: (_, record) => renderLatency(record), }, { key: 'ipHealth', title: (
IP健康
仅供参考
), width: '280px', render: (_, record) => renderIPHealth(record), }, { key: 'actions', title: '操作', width: '320px', render: (_, record) => { const isBuiltin = BUILTIN_PROXY_IDS.has(record.proxyId) const sourceId = record.sourceId || '' const hasSource = !!sourceId && !!record.sourceUrl return (
{hasSource && ( )}
) }, }, ], [ checkingIPHealthIds, globalAutoRefreshEnabled, globalRefreshInterval, ipHealthMap, latencyMap, onCheckOneIPHealth, onDelete, onEdit, onOpenIPHealthDetail, onRefreshSingleSource, onTestOne, onToggleOne, refreshingSourceIds, selectedIds, ]) return (
onFilterKeywordChange(event.target.value)} placeholder="搜索名称或服务器..." style={{ width: '220px' }} /> {hasActiveFilters && ( )}
全局自动刷新 onGlobalRefreshIntervalMChange(event.target.value)} className="w-24" disabled={!globalAutoRefreshEnabled} /> 分钟
{data.length > 0 && ( )} {selectedCount > 0 && ( )}
) }