208 lines
9.2 KiB
TypeScript
208 lines
9.2 KiB
TypeScript
import { useEffect, useMemo, useState } from 'react';
|
|
import { Check, FileSearch, Search, X } from 'lucide-react';
|
|
import { adminApi, type EnterpriseCertification } from '@/api/adminApi';
|
|
import { Breadcrumb, Button, Input, Modal, Select, Table, Tag, type TableColumn } from '@/components/ui';
|
|
|
|
type EnterpriseAuditStatus = 'pending' | 'approved' | 'rejected';
|
|
|
|
type EnterpriseAuditRecord = {
|
|
id: string;
|
|
companyName: string;
|
|
creditCode: string;
|
|
legalPerson: string;
|
|
registeredAddress: string;
|
|
businessLicense: string;
|
|
bankAccountName: string;
|
|
bankName: string;
|
|
bankAccountNo: string;
|
|
verificationAmount: string;
|
|
contactName: string;
|
|
contactPhone: string;
|
|
contactEmail: string;
|
|
submittedAt: string;
|
|
reviewRemark: string;
|
|
status: EnterpriseAuditStatus;
|
|
};
|
|
|
|
const statusOptions = [
|
|
{ label: '全部状态', value: 'all' },
|
|
{ label: '待审核', value: 'pending' },
|
|
{ label: '已通过', value: 'approved' },
|
|
{ label: '已拒绝', value: 'rejected' },
|
|
];
|
|
|
|
const statusTextMap: Record<EnterpriseAuditStatus, string> = {
|
|
pending: '待审核',
|
|
approved: '已通过',
|
|
rejected: '已拒绝',
|
|
};
|
|
|
|
const statusToneMap: Record<EnterpriseAuditStatus, 'warning' | 'success' | 'danger'> = {
|
|
pending: 'warning',
|
|
approved: 'success',
|
|
rejected: 'danger',
|
|
};
|
|
|
|
function mapCertification(record: EnterpriseCertification): EnterpriseAuditRecord {
|
|
const materials = record.materials ?? {};
|
|
return {
|
|
id: record.id,
|
|
companyName: record.companyName,
|
|
creditCode: record.licenseNo ?? '',
|
|
legalPerson: String(materials.legalPerson ?? ''),
|
|
registeredAddress: String(materials.registeredAddress ?? ''),
|
|
businessLicense: String(materials.businessLicense ?? ''),
|
|
bankAccountName: String(materials.bankAccountName ?? record.companyName),
|
|
bankName: String(materials.bankName ?? ''),
|
|
bankAccountNo: String(materials.bankAccountNo ?? ''),
|
|
verificationAmount: String(materials.verificationAmount ?? ''),
|
|
contactName: record.contactName ?? '',
|
|
contactPhone: record.contactPhone ?? '',
|
|
contactEmail: String(materials.contactEmail ?? ''),
|
|
submittedAt: new Date(record.submittedAt).toLocaleString('zh-CN', { hour12: false }),
|
|
reviewRemark: record.rejectReason ?? String(materials.reviewRemark ?? ''),
|
|
status: record.status as EnterpriseAuditStatus,
|
|
};
|
|
}
|
|
|
|
export function AdminEnterpriseAuditPage() {
|
|
const [keyword, setKeyword] = useState('');
|
|
const [status, setStatus] = useState('all');
|
|
const [records, setRecords] = useState<EnterpriseAuditRecord[]>([]);
|
|
const [error, setError] = useState('');
|
|
const [detailRecord, setDetailRecord] = useState<EnterpriseAuditRecord | null>(null);
|
|
|
|
useEffect(() => {
|
|
adminApi.listEnterpriseCertifications({ keyword, status })
|
|
.then((items) => {
|
|
setRecords(items.map(mapCertification));
|
|
setError('');
|
|
})
|
|
.catch((failure: Error) => {
|
|
setRecords([]);
|
|
setError(failure.message || '企业认证审核数据加载失败');
|
|
});
|
|
}, [keyword, status]);
|
|
|
|
const filteredRecords = useMemo(
|
|
() => records.filter((record) => {
|
|
const matchesKeyword = !keyword || `${record.companyName}${record.creditCode}`.includes(keyword);
|
|
const matchesStatus = status === 'all' || record.status === status;
|
|
return matchesKeyword && matchesStatus;
|
|
}),
|
|
[keyword, records, status],
|
|
);
|
|
|
|
async function updateStatus(id: string, nextStatus: EnterpriseAuditStatus) {
|
|
const updated = nextStatus === 'approved'
|
|
? await adminApi.approveEnterpriseCertification(id)
|
|
: await adminApi.rejectEnterpriseCertification(id);
|
|
const mapped = mapCertification(updated);
|
|
setRecords((items) => items.map((item) => (item.id === id ? mapped : item)));
|
|
setDetailRecord((current) => (current?.id === id ? mapped : current));
|
|
}
|
|
|
|
const columns: Array<TableColumn<EnterpriseAuditRecord>> = [
|
|
{ key: 'id', title: '申请单号', render: (record) => <span className="muted">{record.id}</span> },
|
|
{ key: 'companyName', title: '企业名称', render: (record) => <strong>{record.companyName}</strong> },
|
|
{ key: 'creditCode', title: '统一社会信用代码', render: (record) => record.creditCode },
|
|
{ key: 'contactName', title: '联系人', render: (record) => record.contactName },
|
|
{ key: 'contactPhone', title: '联系电话', render: (record) => record.contactPhone },
|
|
{ key: 'submittedAt', title: '提交时间', render: (record) => record.submittedAt },
|
|
{
|
|
key: 'status',
|
|
title: '状态',
|
|
render: (record) => <Tag tone={statusToneMap[record.status]}>{statusTextMap[record.status]}</Tag>,
|
|
},
|
|
{
|
|
key: 'actions',
|
|
title: '操作',
|
|
align: 'right',
|
|
render: (record) => (
|
|
<div className="audit-actions">
|
|
{record.status === 'pending' ? (
|
|
<>
|
|
<button className="audit-link audit-link--success" onClick={() => void updateStatus(record.id, 'approved')} type="button">通过</button>
|
|
<button className="audit-link audit-link--danger" onClick={() => void updateStatus(record.id, 'rejected')} type="button">拒绝</button>
|
|
</>
|
|
) : null}
|
|
<button className="audit-link" onClick={() => setDetailRecord(record)} type="button">详情</button>
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<section className="page-stack admin-audit-page">
|
|
<Breadcrumb items={['审核中心', '企业认证审核']} />
|
|
{error ? <p className="form-error">{error}</p> : null}
|
|
<div className="surface audit-filter-card">
|
|
<div className="audit-filter-grid audit-filter-grid--enterprise">
|
|
<Input label="企业名称/信用代码" onChange={(event) => setKeyword(event.target.value)} placeholder="请输入企业名称或统一社会信用代码" value={keyword} />
|
|
<Select label="审核状态" onChange={(event) => setStatus(event.target.value)} options={statusOptions} value={status} />
|
|
<div className="audit-filter-actions">
|
|
<Button icon={<Search size={17} />}>查询</Button>
|
|
<Button onClick={() => { setKeyword(''); setStatus('all'); }} variant="ghost">重置</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="surface audit-table-card">
|
|
<Table columns={columns} data={filteredRecords} emptyText="暂无企业认证审核记录" rowKey="id" />
|
|
<div className="audit-pagination">
|
|
<span>共 {filteredRecords.length} 条</span>
|
|
<Button disabled icon={<FileSearch size={16} />} size="sm" variant="ghost">更多</Button>
|
|
<Button disabled icon={<Check size={15} />} size="sm" variant="secondary">1</Button>
|
|
<Button disabled icon={<X size={15} />} size="sm" variant="ghost">下一页</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{detailRecord ? (
|
|
<Modal
|
|
footer={(
|
|
<>
|
|
<Button onClick={() => setDetailRecord(null)} variant="ghost">关闭</Button>
|
|
{detailRecord.status === 'pending' ? (
|
|
<>
|
|
<Button onClick={() => void updateStatus(detailRecord.id, 'rejected')} variant="danger">驳回认证</Button>
|
|
<Button onClick={() => void updateStatus(detailRecord.id, 'approved')}>审核通过</Button>
|
|
</>
|
|
) : null}
|
|
</>
|
|
)}
|
|
onClose={() => setDetailRecord(null)}
|
|
open
|
|
size="xl"
|
|
title={<div className="template-modal-title"><h2>企业认证详情</h2><p>{detailRecord.id}</p></div>}
|
|
>
|
|
<div className="enterprise-audit-detail">
|
|
<section>
|
|
<h3>主体资料</h3>
|
|
<div><span>企业名称</span><strong>{detailRecord.companyName}</strong></div>
|
|
<div><span>统一社会信用代码</span><strong>{detailRecord.creditCode}</strong></div>
|
|
<div><span>法定代表人</span><strong>{detailRecord.legalPerson}</strong></div>
|
|
<div><span>注册地址</span><strong>{detailRecord.registeredAddress}</strong></div>
|
|
<div><span>营业执照附件</span><strong>{detailRecord.businessLicense}</strong></div>
|
|
</section>
|
|
<section>
|
|
<h3>对公验证</h3>
|
|
<div><span>账户户名</span><strong>{detailRecord.bankAccountName}</strong></div>
|
|
<div><span>开户银行</span><strong>{detailRecord.bankName}</strong></div>
|
|
<div><span>银行账号</span><strong>{detailRecord.bankAccountNo}</strong></div>
|
|
<div><span>验证金额</span><strong>{detailRecord.verificationAmount}</strong></div>
|
|
</section>
|
|
<section>
|
|
<h3>联系人与审核</h3>
|
|
<div><span>联系人</span><strong>{detailRecord.contactName}</strong></div>
|
|
<div><span>联系电话</span><strong>{detailRecord.contactPhone}</strong></div>
|
|
<div><span>联系邮箱</span><strong>{detailRecord.contactEmail}</strong></div>
|
|
<div><span>提交时间</span><strong>{detailRecord.submittedAt}</strong></div>
|
|
<div><span>当前状态</span><strong>{statusTextMap[detailRecord.status]}</strong></div>
|
|
<div className="enterprise-audit-detail__remark"><span>审核备注</span><strong>{detailRecord.reviewRemark}</strong></div>
|
|
</section>
|
|
</div>
|
|
</Modal>
|
|
) : null}
|
|
</section>
|
|
);
|
|
}
|