feat: complete reporting and filing workflows

This commit is contained in:
hectorzhao
2026-07-28 20:28:47 +08:00
parent 352a6293b4
commit 99c8c7c68b
52 changed files with 3490 additions and 376 deletions
@@ -1,10 +1,11 @@
import { useEffect, useMemo, useState } from 'react';
import { ChevronDown, ChevronRight, Edit3, FileText, Info, Plus, Search, Trash2, Upload } from 'lucide-react';
import { ChevronDown, ChevronRight, Edit3, FileSpreadsheet, FileText, Info, Plus, Search, Trash2, Upload } from 'lucide-react';
import { adminApi, type ApplicationReportField, type ClientSmsApplication, type ClientSmsSignature, type FileRef, type TenantOption } from '@/api/adminApi';
import { Breadcrumb, Button, DeleteRiskAction, FileActions, Input, Modal, Pagination, Select, Tabs, Tag, Textarea } from '@/components/ui';
import { displayFileName } from '@/utils/fileName';
import { formatDateTime } from '@/utils/dateTime';
import { getSmsSignatureValidationError, hasForbiddenSmsSignatureCharacter, isCompleteSmsSignature, SMS_SIGNATURE_CHARACTER_ERROR } from '@/utils/smsSignature';
import { ReportMaterialImportModal } from './ReportMaterialImportModal';
type CarrierStatus = 'approved' | 'pending' | 'rejected' | 'filing';
@@ -460,9 +461,9 @@ function DrainageFormModal({ applicationId, item, onClose, onSubmit }: { applica
<section>
<h3></h3>
<Input
label="* 引流网址"
label="* 引流url或号码"
onChange={(event) => update('url', event.target.value)}
placeholder="请输入引流网址"
placeholder="请输入引流url或号码"
required
value={form.url}
/>
@@ -475,7 +476,6 @@ function DrainageFormModal({ applicationId, item, onClose, onSubmit }: { applica
</ol>
</div>
<div className="signature-form-grid">
<Input label="* 引流信息" onChange={(event) => update('siteName', event.target.value)} placeholder="请输入引流信息" value={form.siteName} />
<Textarea className="signature-form-grid__wide" label="备注" onChange={(event) => update('remark', event.target.value)} rows={4} value={form.remark} />
</div>
<DynamicReportFields fields={reportFields} onChange={updateReportValue} title="引流信息报备资料(通用 + 通道)" values={form.reportValues} />
@@ -541,7 +541,7 @@ function DrainageReportModal({ item, onClose, signature }: { item: DrainageInfo;
return (
<Modal footer={<Button onClick={onClose}></Button>} onClose={onClose} open size="xl" title="引流信息报备详情">
<div className="detail-grid">
<div><span></span><strong>{item.siteName}</strong></div>
<div><span>url或号码</span><strong>{item.url}</strong></div>
<div><span>url或号码</span><strong>{item.url}</strong></div>
<div><span></span><CarrierReportTag summary={summary?.mobile} /></div>
<div><span></span><CarrierReportTag summary={summary?.unicom} /></div>
@@ -616,6 +616,8 @@ export function AdminEnterpriseSignaturesPage() {
const [signatures, setSignatures] = useState<ClientSmsSignature[]>([]);
const [tenants, setTenants] = useState<TenantOption[]>([]);
const [page, setPage] = useState(1);
const [importOpen, setImportOpen] = useState(false);
const [message, setMessage] = useState('');
async function loadData(filters = { enterpriseKeyword: appliedEnterpriseKeyword, applicationKeyword: appliedApplicationKeyword, signatureKeyword: appliedSignatureKeyword, drainageKeyword: appliedDrainageKeyword }) {
try {
@@ -693,7 +695,7 @@ export function AdminEnterpriseSignaturesPage() {
return;
}
const exists = readDrainagePayload(signature).links.some((current) => current.id === item.id);
const body = { siteName: item.siteName, url: item.url, remark: item.remark, reportValues: item.reportValues };
const body = { siteName: item.url.trim(), 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);
@@ -766,7 +768,7 @@ export function AdminEnterpriseSignaturesPage() {
<Button onClick={() => setDrainageReport({ signature, item })} size="sm" variant="ghost"></Button>
<Button disabled={item.auditStatus !== 'approved'} onClick={() => setDrainageStatusTarget({ signature, item })} size="sm" variant="ghost"></Button>
<Button onClick={() => setDrainageModal({ signatureId: signature.id, item })} size="sm" variant="ghost"></Button>
<Button onClick={() => setDeleteTarget({ kind: 'drainage', signatureId: signature.id, id: item.id, name: item.siteName })} size="sm" variant="danger"></Button>
<Button onClick={() => setDeleteTarget({ kind: 'drainage', signatureId: signature.id, id: item.id, name: item.url })} size="sm" variant="danger"></Button>
</span>
</div>
);})}
@@ -803,7 +805,10 @@ export function AdminEnterpriseSignaturesPage() {
<Breadcrumb items={['客户管理', '企业签名管理']} />
<h1></h1>
</div>
<Button icon={<Plus size={16} />} onClick={() => setSignatureModal(activeTab === 'sms' ? 'new' : null)}></Button>
<div className="page-heading-actions">
<Button icon={<FileSpreadsheet size={16} />} onClick={() => setImportOpen(true)} variant="ghost"></Button>
<Button icon={<Plus size={16} />} onClick={() => setSignatureModal(activeTab === 'sms' ? 'new' : null)}></Button>
</div>
</div>
<div className="surface admin-split-filter">
@@ -836,6 +841,7 @@ export function AdminEnterpriseSignaturesPage() {
</div>
{error ? <p className="form-error">{error}</p> : null}
{message ? <p className="form-success">{message}</p> : null}
<div className="surface section-stack">
<Tabs
@@ -857,6 +863,10 @@ export function AdminEnterpriseSignaturesPage() {
tenants={tenants}
/>
) : null}
{importOpen ? <ReportMaterialImportModal onClose={() => setImportOpen(false)} onCompleted={() => {
setMessage('导入解析完成,合格资料已进入审核中心的导入批次');
void loadData();
}} /> : null}
{signatureReport ? <SignatureReportModal item={signatureReport} onClose={() => setSignatureReport(null)} /> : null}
{reportStatusTarget ? <ChannelReportStatusModal item={reportStatusTarget} onClose={() => setReportStatusTarget(null)} onSaved={() => { setReportStatusTarget(null); void loadData(); }} /> : null}
{drainageModal ? (