import { useEffect, useState } from 'react'; import { Plus, Search } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { adminApi, type CmppConnectionState } from '@/api/adminApi'; import { Breadcrumb, Button, Input, Modal, Select } from '@/components/ui'; import { ChannelFormModal } from './channels/ChannelFormModal'; import { ChannelLogModal } from './channels/ChannelLogModal'; import { ChannelTable } from './channels/ChannelTable'; import { SmsTestModal } from './channels/SmsTestModal'; import { buildChannelPayload, carrierOptions, mapApiChannel, mapUiStatusToApi, statusOptions } from './channels/channelModel'; import type { ChannelConfirmAction, ChannelLogState, ChannelModalState, SmsChannel } from './channels/channelTypes'; import './channels/AdminChannelsPage.css'; export function AdminChannelsPage() { const navigate = useNavigate(); const [channels, setChannels] = useState([]); const [error, setError] = useState(''); const [keyword, setKeyword] = useState(''); const [carrier, setCarrier] = useState('all'); const [status, setStatus] = useState('all'); const [modal, setModal] = useState(null); const [testChannel, setTestChannel] = useState(null); const [confirmAction, setConfirmAction] = useState(null); const [logState, setLogState] = useState(null); const [logKeyword, setLogKeyword] = useState(''); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const pageSize = 10; function loadChannels(targetPage = page, filters = { keyword, carrier, status }) { Promise.all([ adminApi.listChannelsPage({ keyword: filters.keyword.trim() || undefined, carrier: filters.carrier, status: filters.status, page: targetPage, pageSize }), adminApi.getSendQuality(), ]) .then(async ([result, quality]) => { const visibleChannels = result.items; const connections = await Promise.all(visibleChannels.map((channel) => adminApi.listChannelConnections(channel.id).catch(() => [] as CmppConnectionState[]), )); const qualityByChannel = new Map(quality.channels.map((item) => [item.channelId, item])); setChannels(visibleChannels.map((item, index) => mapApiChannel(item, connections[index], qualityByChannel.get(item.id)))); setTotal(result.total); setError(''); }) .catch((failure: Error) => setError(failure.message || '通道列表加载失败')); } useEffect(() => { loadChannels(page); }, [page]); const totalPages = Math.max(1, Math.ceil(total / pageSize)); const currentPage = Math.min(page, totalPages); async function upsertChannel(nextChannel: SmsChannel) { try { if (modal?.mode === 'edit' && modal.channel) { await adminApi.updateChannel(modal.channel.id, buildChannelPayload(nextChannel, nextChannel.passwordCipher)); } else { await adminApi.createChannel({ code: `CH-${Date.now()}`, ...buildChannelPayload(nextChannel, nextChannel.passwordCipher || 'secret'), status: 'active', }); } loadChannels(); setModal(null); setError(''); } catch (failure) { setError(failure instanceof Error ? failure.message : '通道保存失败'); } } async function toggleChannel(channel: SmsChannel) { await adminApi.changeChannelStatus(channel.id, mapUiStatusToApi(channel)); loadChannels(); } async function copyChannel(channel: SmsChannel) { await adminApi.copyChannel(channel.id); loadChannels(); } async function openLinkLogs(channel: SmsChannel) { setLogState({ channel }); setLogKeyword(''); try { const data = await adminApi.listChannelConnectionLogs(channel.id); setLogState({ channel, data }); } catch (failure) { setLogState(null); setError(failure instanceof Error ? failure.message : '连接日志加载失败'); } } function submitConfirmAction() { if (!confirmAction) { return; } if (confirmAction.type === 'toggle') { void toggleChannel(confirmAction.channel); } if (confirmAction.type === 'copy') { void copyChannel(confirmAction.channel); } setConfirmAction(null); } const confirmTitle = confirmAction?.type === 'copy' ? '确认复制通道' : confirmAction?.channel.status === 'stopped' ? '确认启用通道' : '确认停用通道'; const confirmDescription = confirmAction?.type === 'copy' ? '系统将复制当前通道配置和报备详情,并新建一条名称带“副本”的通道。' : confirmAction?.channel.status === 'stopped' ? '启用后通道会进入连接中状态,后续可继续观察网关连接。' : '停用后该通道将不再承接新的发送任务。'; return (
{error ?

{error}

: null}
setKeyword(event.target.value)} placeholder="请输入通道名称" value={keyword} /> setStatus(event.target.value)} options={statusOptions} value={status} />
void loadChannels()} onEdit={setModal} onOpenLogs={(channel) => void openLinkLogs(channel)} onOpenReports={(channel) => navigate(`/admin/channels/${channel.id}/reports`)} onPageChange={setPage} onTest={setTestChannel} total={total} totalPages={totalPages} /> {modal ? setModal(null)} onSubmit={upsertChannel} /> : null} {testChannel ? ( setTestChannel(null)} onOpenRecords={() => { setTestChannel(null); navigate('/admin/sms-records'); }} /> ) : null} {confirmAction ? ( )} onClose={() => setConfirmAction(null)} open title={confirmTitle} >
{confirmAction.channel.name} 通道 ID:{confirmAction.channel.id}

{confirmDescription}

) : null} {logState ? ( setLogState(null)} onKeywordChange={setLogKeyword} /> ) : null}
); }