import { useEffect, useMemo, useState } from 'react'; import { ArrowLeft, Eye, FileSliders, Search } from 'lucide-react'; import { useNavigate, useParams } from 'react-router-dom'; import { adminApi, type AdminChannel, type ChannelReportField, type ClientSmsSignature, type DictionaryItem, type ReportRecord, type ReportTask } from '@/api/adminApi'; import { Breadcrumb, Button, Input, Modal, Select, Tag, Textarea } from '@/components/ui'; import { formatDateTime } from '@/utils/dateTime'; type ReportType = 'signature' | 'drainage' | 'both'; type DrainageItem = Record & { id?: string; url?: string; siteName?: string; submittedAt?: string; remark?: string }; const statusMeta: Record = { approved: { label: '报备成功', tone: 'success' }, success: { label: '报备成功', tone: 'success' }, failed: { label: '报备失败', tone: 'danger' }, rejected: { label: '报备失败', tone: 'danger' }, pending: { label: '未报备', tone: 'neutral' }, waiting_material: { label: '资料待补充', tone: 'warning' }, exporting: { label: '报备中', tone: 'warning' }, reporting: { label: '报备中', tone: 'warning' }, abandoned: { label: '已放弃', tone: 'neutral' }, partial_success: { label: '部分成功', tone: 'warning' }, filing: { label: '报备中', tone: 'warning' }, }; const fieldTypeLabel: Record = { string: '字符串', image: '图片', file: '文件' }; function asRecord(value: unknown): Record { return value && typeof value === 'object' && !Array.isArray(value) ? value as Record : {}; } function formatSignatureName(value?: string | null) { const name = String(value ?? '-').trim().replace(/^[【\[]+|[】\]]+$/g, ''); return `【${name || '-'}】`; } function drainageItems(signature?: ClientSmsSignature) { const payload = asRecord(signature?.drainageInfo); return Array.isArray(payload.links) ? payload.links.filter((item): item is DrainageItem => Boolean(item) && typeof item === 'object') : []; } function DateTime({ value }: { value?: unknown }) { return value ? {formatDateTime(String(value))} : -; } function ReportStatus({ value }: { value?: string }) { const meta = statusMeta[value ?? ''] ?? { label: value || '未报备', tone: 'neutral' as const }; return {meta.label}; } function DetailModal({ drainage, signature, task, onClose }: { drainage?: DrainageItem; signature?: ClientSmsSignature; task: ReportTask; onClose: () => void }) { const payload = asRecord(signature?.drainageInfo); const profile = asRecord(payload.signatureProfile); const reportValues = asRecord(drainage ? drainage.reportValues : payload.signatureReportValues); return ( 关闭} onClose={onClose} open size="xl" title={drainage ? '查看引流信息详情' : '查看签名详情'}>
{drainage ? String(drainage.siteName || drainage.url || '引流信息') : formatSignatureName(signature?.name ?? task.signature?.name)}

企业{signature?.tenant?.name ?? task.tenantId}

企业应用{signature?.application?.name ?? '-'}

{!drainage ? <>

签名依据{String(profile.basis ?? '-')}

公司名称{String(profile.companyName ?? '-')}

统一社会信用代码{String(profile.creditCode ?? '-')}

: null} {drainage ? <>

引流地址{String(drainage.url ?? '-')}

备注{String(drainage.remark ?? '-')}

: null} {Object.entries(reportValues).map(([key, value]) =>

{key}{typeof value === 'object' ? String(asRecord(value).fileName ?? asRecord(value).fileObjectId ?? '-') : String(value ?? '-')}

)}
); } export function AdminChannelReportPage() { const navigate = useNavigate(); const { channelId = '' } = useParams(); const [channel, setChannel] = useState(); const [tasks, setTasks] = useState([]); const [records, setRecords] = useState([]); const [signatures, setSignatures] = useState([]); const [fields, setFields] = useState([]); const [libraryFields, setLibraryFields] = useState([]); const [keyword, setKeyword] = useState(''); const [status, setStatus] = useState('all'); const [detail, setDetail] = useState<{ task: ReportTask; signature?: ClientSmsSignature; drainage?: DrainageItem }>(); const [statusTask, setStatusTask] = useState(); const [nextStatus, setNextStatus] = useState('approved'); const [statusReason, setStatusReason] = useState(''); const [configType, setConfigType] = useState(); const [drainageFieldId, setDrainageFieldId] = useState(''); const [required, setRequired] = useState(false); const [description, setDescription] = useState(''); const [error, setError] = useState(''); function loadData() { Promise.all([ adminApi.listChannels(), adminApi.listReportTasks({ channelId }), adminApi.listReportRecords({ channelId }), adminApi.listEnterpriseSignatures(), adminApi.listChannelReportFields(channelId), adminApi.listDrainageFields(), ]).then(([channelItems, taskItems, recordItems, signatureItems, fieldItems, libraryItems]) => { setChannel(channelItems.find((item) => item.id === channelId)); setTasks(taskItems); setRecords(recordItems); setSignatures(signatureItems); setFields(fieldItems); setLibraryFields(libraryItems.filter((item) => item.status === 'active')); setError(''); }).catch((failure: Error) => setError(failure.message || '通道报备详情加载失败')); } useEffect(loadData, [channelId]); const signatureMap = useMemo(() => new Map(signatures.map((item) => [item.id, item])), [signatures]); const visibleTasks = useMemo(() => tasks.filter((task) => { const signature = signatureMap.get(task.signatureId); const drainage = drainageItems(signature).find((item) => String(item.id) === task.drainageItemId); const matchesKeyword = !keyword.trim() || [signature?.name, signature?.tenant?.name, signature?.application?.name, drainage?.siteName, drainage?.url].some((value) => String(value ?? '').includes(keyword.trim())); return matchesKeyword && (status === 'all' || task.status === status); }), [keyword, signatureMap, status, tasks]); function lastRecord(taskId: string, action: string) { return records.find((record) => record.taskId === taskId && record.action === action); } function createField() { if (!configType || !drainageFieldId) return; adminApi.createChannelReportField({ channelId, drainageFieldId, reportType: configType, required, description, status: 'active' }) .then(() => { setConfigType(undefined); setDrainageFieldId(''); setRequired(false); setDescription(''); loadData(); }) .catch((failure: Error) => setError(failure.message || '报备字段保存失败')); } function saveTaskStatus() { if (!statusTask) return; adminApi.changeReportTaskStatuses({ items: [{ signatureId: statusTask.signatureId, channelId: statusTask.channelId, reportType: statusTask.reportType, drainageItemId: statusTask.drainageItemId ?? undefined, status: nextStatus }], reason: statusReason }) .then(() => { setStatusTask(undefined); setStatusReason(''); loadData(); }) .catch((failure: Error) => setError(failure.message || '报备状态保存失败')); } return (

{channel?.name ?? '通道报备详情'}

通道编号:{channel?.code ?? channelId} · 已配置字段 {fields.length} 个
{error ?

{error}

: null}
setKeyword(event.target.value)} placeholder="请输入关键词" prefix={} value={keyword} /> setNextStatus(event.target.value)} options={[{label:'未报备',value:'pending'},{label:'资料待补充',value:'waiting_material'},{label:'报备中',value:'reporting'},{label:'报备通过',value:'approved'},{label:'报备失败',value:'failed'},{label:'放弃报备',value:'abandoned'}]} value={nextStatus}/>