import { useEffect, useRef, useState } from 'react'; import { FileSpreadsheet, Plus, Search } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { adminApi, type ClientSmsApplication, type ClientSmsSignature, type TenantOption } from '@/api/adminApi'; import { Breadcrumb, Button, Input, Modal, Tabs } from '@/components/ui'; import { ReportMaterialImportModal } from './ReportMaterialImportModal'; import { DrainageFormModal } from './enterprise-signatures/DrainageFormModal'; import { EnterpriseSignaturesTable } from './enterprise-signatures/EnterpriseSignaturesTable'; import { SignatureFormModal } from './enterprise-signatures/SignatureFormModal'; import { ChannelReportStatusModal, ConfirmModal, DrainageReportStatusModal, } from './enterprise-signatures/SignatureReportModals'; import { buildDrainagePayload, readDrainagePayload } from './enterprise-signatures/signature.helpers'; import type { DrainageInfo, SignatureFormState } from './enterprise-signatures/signature.types'; /** R4 page container: owns query state and coordinates focused presentation components. */ export function AdminEnterpriseSignaturesPage() { const navigate = useNavigate(); const [activeTab, setActiveTab] = useState<'sms' | 'mms'>('sms'); const [applications, setApplications] = useState([]); const [deleteTarget, setDeleteTarget] = useState<{ kind: 'drainage'; signatureId: string; id: string; name: string; } | null>(null); const [drainageModal, setDrainageModal] = useState<{ signatureId: string; item?: DrainageInfo } | null>(null); const [drainageStatusTarget, setDrainageStatusTarget] = useState<{ signature: ClientSmsSignature; item: DrainageInfo; } | null>(null); const [enterpriseKeyword, setEnterpriseKeyword] = useState(''); const [appliedEnterpriseKeyword, setAppliedEnterpriseKeyword] = useState(''); const [applicationKeyword, setApplicationKeyword] = useState(''); const [appliedApplicationKeyword, setAppliedApplicationKeyword] = useState(''); const [drainageKeyword, setDrainageKeyword] = useState(''); const [appliedDrainageKeyword, setAppliedDrainageKeyword] = useState(''); const [error, setError] = useState(''); const [expandedSignatureId, setExpandedSignatureId] = useState(''); const [signatureKeyword, setSignatureKeyword] = useState(''); const [appliedSignatureKeyword, setAppliedSignatureKeyword] = useState(''); const [signatureModal, setSignatureModal] = useState(null); const [reportStatusTarget, setReportStatusTarget] = useState(null); const [signatures, setSignatures] = useState([]); const [total, setTotal] = useState(0); const [pendingReportMaterialTotal, setPendingReportMaterialTotal] = useState(0); const [tenants, setTenants] = useState([]); const [page, setPage] = useState(1); const [importOpen, setImportOpen] = useState(false); const [materialChangedSignature, setMaterialChangedSignature] = useState(null); const listRequestSequence = useRef(0); const pageSize = 10; async function loadData( filters = { enterpriseKeyword: appliedEnterpriseKeyword, applicationKeyword: appliedApplicationKeyword, signatureKeyword: appliedSignatureKeyword, drainageKeyword: appliedDrainageKeyword, }, targetPage = page, ) { const sequence = ++listRequestSequence.current; try { const signatureResult = await adminApi.listEnterpriseSignaturesPage({ ...filters, page: targetPage, pageSize }); if (sequence !== listRequestSequence.current) return; setSignatures(signatureResult.items); setTotal(signatureResult.total); setPendingReportMaterialTotal(signatureResult.pendingReportMaterialTotal); setError(''); } catch (failure) { if (sequence !== listRequestSequence.current) return; setError(failure instanceof Error ? failure.message : '企业签名加载失败'); } } useEffect(() => { let cancelled = false; void Promise.all([adminApi.listTenantOptions(), adminApi.listEnterpriseApplicationOptions()]) .then(([tenantItems, applicationItems]) => { if (cancelled) return; setTenants(tenantItems.filter((tenant) => tenant.status !== 'deleted')); setApplications(applicationItems); }) .catch((failure: Error) => { if (!cancelled) setError(failure.message || '企业及应用选项加载失败'); }); return () => { cancelled = true; }; }, []); useEffect(() => { queueMicrotask(() => void loadData(undefined, page)); }, [page]); const filteredSignatures = signatures; const totalPages = Math.max(1, Math.ceil(total / pageSize)); const currentPage = Math.min(page, totalPages); const visibleSignatures = signatures; async function saveSignature(state: SignatureFormState) { const existing = signatureModal && signatureModal !== 'new' ? signatureModal : null; const existingPayload = existing ? readDrainagePayload(existing) : { links: [], signatureProfile: undefined }; const drainageInfo = buildDrainagePayload( { mobile: state.mobile, unicom: state.unicom, telecom: state.telecom, }, existingPayload.links, existingPayload.signatureProfile, state.reportValues, ); try { let saved: ClientSmsSignature; if (existing) { saved = await adminApi.updateEnterpriseSignature(existing.id, { applicationId: state.applicationId || null, drainageInfo, name: state.name, purpose: state.purpose, }); } else { saved = await adminApi.createEnterpriseSignature({ applicationId: state.applicationId || undefined, drainageInfo, name: state.name, purpose: state.purpose, tenantId: state.tenantId, }); } setSignatureModal(null); if (saved.reportMaterialChanged) setMaterialChangedSignature(saved); await loadData(); } catch (failure) { setError(failure instanceof Error ? failure.message : '企业签名保存失败'); } } async function saveDrainage(signatureId: string, item: DrainageInfo) { const signature = signatures.find((current) => current.id === signatureId); if (!signature) { return; } const exists = readDrainagePayload(signature).links.some((current) => current.id === item.id); const body = { url: item.url.trim(), remark: item.remark, reportValues: item.reportValues }; if (exists) await adminApi.updateDrainageInfo(item.id, body); else await adminApi.createDrainageInfo(signatureId, body); setDrainageModal(null); setExpandedSignatureId(signatureId); await loadData(); } async function editSignature(signature: ClientSmsSignature) { try { setSignatureModal(await adminApi.getEnterpriseSignature(signature.id)); } catch (failure) { setError(failure instanceof Error ? failure.message : '签名详情加载失败'); } } async function openSignatureReport(signature: ClientSmsSignature) { try { const reportTargets = await adminApi.getEnterpriseSignatureReportTargets(signature.id); setReportStatusTarget({ ...signature, reportTargets }); } catch (failure) { setError(failure instanceof Error ? failure.message : '签名报备状态加载失败'); } } async function editDrainage(signature: ClientSmsSignature, item: DrainageInfo) { try { const detail = await adminApi.getEnterpriseSignature(signature.id); const detailItem = readDrainagePayload(detail).links.find((candidate) => candidate.id === item.id); if (!detailItem) throw new Error('引流信息不存在或已删除'); setDrainageModal({ signatureId: signature.id, item: detailItem }); } catch (failure) { setError(failure instanceof Error ? failure.message : '引流信息详情加载失败'); } } async function openDrainageReport(signature: ClientSmsSignature, item: DrainageInfo) { try { const targets = await adminApi.getDrainageInfoReportTargets(item.id); setDrainageStatusTarget({ signature: { ...signature, drainageReportTargets: { [item.id]: targets } }, item, }); } catch (failure) { setError(failure instanceof Error ? failure.message : '引流报备状态加载失败'); } } async function confirmDelete() { if (!deleteTarget) { return; } await adminApi.changeDrainageInfoStatus(deleteTarget.id, 'deleted', '运营端删除引流信息'); setDeleteTarget(null); await loadData(); } const smsSignatureContent = ( setDrainageModal({ signatureId: signature.id })} setDeleteTarget={setDeleteTarget} onEditDrainage={(signature, item) => void editDrainage(signature, item)} onOpenDrainageReport={(signature, item) => void openDrainageReport(signature, item)} setExpandedSignatureId={setExpandedSignatureId} setPage={setPage} onEditSignature={(signature) => void editSignature(signature)} onOpenSignatureReport={(signature) => void openSignatureReport(signature)} total={total} totalPages={totalPages} visibleSignatures={visibleSignatures} /> ); return (

企业签名管理

setEnterpriseKeyword(event.target.value)} placeholder="请输入企业名称" prefix={} value={enterpriseKeyword} /> setApplicationKeyword(event.target.value)} placeholder="请输入企业应用名称" prefix={} value={applicationKeyword} /> setSignatureKeyword(event.target.value)} placeholder="请输入签名" prefix={} value={signatureKeyword} /> setDrainageKeyword(event.target.value)} placeholder="请输入引流信息、URL 或备注" prefix={} value={drainageKeyword} />
待生成报备资料 {pendingReportMaterialTotal}
{error ?

{error}

: null}
setActiveTab(value as 'sms' | 'mms')} value={activeTab} items={[ { label: '短信签名', value: 'sms', content: smsSignatureContent }, { label: '彩信签名', pending: true, value: 'mms', content:
彩信签名待后端能力确认,本页不展示演示数据。
, }, ]} />
{signatureModal ? ( setSignatureModal(null)} onSubmit={(state) => { void saveSignature(state); }} tenants={tenants} /> ) : null} {materialChangedSignature ? ( } onClose={() => setMaterialChangedSignature(null)} open title="签名资料已更新" >
资料发生变化,如需提交至通道报备,请到“报备工作台-报备资料池”生成报备批次。
) : null} {importOpen ? ( setImportOpen(false)} onCompleted={(result) => { navigate(`/admin/signatures?tab=import&batchId=${encodeURIComponent(result.id)}&imported=1`); }} /> ) : null} {reportStatusTarget ? ( setReportStatusTarget(null)} onSaved={() => { setReportStatusTarget(null); void loadData(); }} /> ) : null} {drainageModal ? ( item.id === drainageModal.signatureId)?.applicationId} item={drainageModal.item} onClose={() => setDrainageModal(null)} onSubmit={(item) => { void saveDrainage(drainageModal.signatureId, item); }} /> ) : null} {drainageStatusTarget ? ( setDrainageStatusTarget(null)} onSaved={() => { setDrainageStatusTarget(null); void loadData(); }} signature={drainageStatusTarget.signature} /> ) : null} {deleteTarget ? ( setDeleteTarget(null)} onConfirm={() => { void confirmDelete(); }} /> ) : null}
); }