import { useEffect, useMemo, useState } from 'react'; import { Clock3, Layers3, Pencil, Plus, RadioTower, Search, Trash2 } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { Breadcrumb, Button, Input, Modal, Pagination, Tag } from '@/components/ui'; import { adminApi, type ChannelGroup } from '@/api/adminApi'; type GroupCarrier = 'mobile' | 'unicom' | 'telecom'; const carrierLabels: Record = { mobile: '移动', unicom: '联通', telecom: '电信', }; function formatRetryLimit(group: ChannelGroup) { if (group.retryEnabled === false) return '已关闭'; const totalMinutes = group.retryTimeLimitMinutes ?? (group.retryTimeLimitHours ?? 12) * 60; return `${Math.floor(totalMinutes / 60)}小时${totalMinutes % 60}分钟`; } function getGroupSummary(group: ChannelGroup) { const items = group.items ?? []; const provinceCount = items.filter((item) => item.province).length; const nationalCount = items.length - provinceCount; const connectedCount = items.filter((item) => (item.channel?.connectionStates ?? []).some((connection) => connection.status === 'connected' && connection.desiredConnections > 0 && connection.currentConnections > 0 )).length; return { connectedCount, nationalCount, provinceCount, totalCount: items.length }; } export function AdminChannelGroupsPage() { const navigate = useNavigate(); const [groupName, setGroupName] = useState(''); const [groups, setGroups] = useState([]); const [deleteTarget, setDeleteTarget] = useState(null); const [page, setPage] = useState(1); const [error, setError] = useState(''); const pageSize = 10; function loadData() { adminApi.listChannelGroups() .then((items) => { setGroups(items); setError(''); }) .catch((failure: Error) => setError(failure.message || '通道组加载失败')); } useEffect(() => { loadData(); }, []); const filteredGroups = useMemo(() => groups.filter((group) => !groupName.trim() || group.name.includes(groupName.trim())), [groupName, groups]); const totalPages = Math.max(1, Math.ceil(filteredGroups.length / pageSize)); const currentPage = Math.min(page, totalPages); const visibleGroups = filteredGroups.slice((currentPage - 1) * pageSize, currentPage * pageSize); useEffect(() => { setPage(1); }, [groupName, groups.length]); function deleteGroup() { if (!deleteTarget) return; adminApi.deleteChannelGroup(deleteTarget.id) .then(() => { setDeleteTarget(null); loadData(); }) .catch((failure: Error) => setError(failure.message || '通道组删除失败')); } return (
{error ?

{error}

: null}
setGroupName(event.target.value)} placeholder="请输入通道组名称" value={groupName} />
{visibleGroups.map((group) => { const summary = getGroupSummary(group); const previewItems = (group.items ?? []).slice(0, 4); return (
{group.name} {carrierLabels[group.carrier] ?? group.carrier}通道组
省网 {summary.provinceCount}
全国 {summary.nationalCount}
链接正常 {summary.connectedCount}/{summary.totalCount}
{group.retryEnabled === false ? '补发关闭' : '补发开启'} {formatRetryLimit(group)} {summary.totalCount ? `${summary.totalCount} 个通道` : '暂无通道'}
{previewItems.map((item) => ( {item.province ? `${item.province} / ` : `P${item.priority} / `} {item.channel?.name ?? '未命名通道'} ))} {summary.totalCount > previewItems.length ? +{summary.totalCount - previewItems.length} : null} {summary.totalCount === 0 ? 暂无绑定通道 : null}
); })}
= totalPages} onNext={() => setPage((value) => Math.min(totalPages, value + 1))} onPrevious={() => setPage((value) => Math.max(1, value - 1))} page={currentPage} previousDisabled={currentPage <= 1} total={filteredGroups.length} />
)} onClose={() => setDeleteTarget(null)} open={Boolean(deleteTarget)} title="删除通道组" >
{deleteTarget?.name}

删除前会校验真实路由绑定;已被企业应用使用的通道组不会被删除。

); }