Initial CMPP frontend prototype

This commit is contained in:
hectorzhao
2026-06-30 16:09:46 +08:00
commit 2f3c274a30
98 changed files with 25255 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
import { useMemo, useState } from 'react';
import { Check, FileSearch, Search, X } from 'lucide-react';
import { Button, Input, Select, Table, Tag, type TableColumn } from '@/components/ui';
type EnterpriseAuditStatus = 'pending' | 'approved' | 'rejected';
type EnterpriseAuditRecord = {
id: string;
companyName: string;
creditCode: string;
contactName: string;
contactPhone: string;
submittedAt: 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',
};
const initialEnterpriseAudits: EnterpriseAuditRecord[] = [
{ id: 'ENT-20260319-001', companyName: '北京星云科技有限公司', creditCode: '91110000X12345678A', contactName: '张伟', contactPhone: '13800138000', submittedAt: '2026-03-19 10:23:45', status: 'pending' },
{ id: 'ENT-20260319-002', companyName: '上海蓝海科技有限公司', creditCode: '91310000X87654321B', contactName: '李娜', contactPhone: '13900139000', submittedAt: '2026-03-18 15:45:12', status: 'pending' },
{ id: 'ENT-20260318-001', companyName: '广州飞跃文化传媒有限公司', creditCode: '91440100X11223344C', contactName: '王强', contactPhone: '13700137000', submittedAt: '2026-03-17 09:12:30', status: 'approved' },
{ id: 'ENT-20260317-001', companyName: '深圳前海贸易有限公司', creditCode: '91440300X55667788D', contactName: '陈杰', contactPhone: '13600136000', submittedAt: '2026-03-16 11:30:22', status: 'rejected' },
];
export function AdminEnterpriseAuditPage() {
const [keyword, setKeyword] = useState('');
const [status, setStatus] = useState('all');
const [records, setRecords] = useState(initialEnterpriseAudits);
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],
);
function updateStatus(id: string, nextStatus: EnterpriseAuditStatus) {
setRecords((items) => items.map((item) => (item.id === id ? { ...item, status: nextStatus } : item)));
}
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={() => updateStatus(record.id, 'approved')} type="button"></button>
<button className="audit-link audit-link--danger" onClick={() => updateStatus(record.id, 'rejected')} type="button"></button>
</>
) : null}
<button className="audit-link" type="button"></button>
</div>
),
},
];
return (
<section className="page-stack admin-audit-page">
<div className="breadcrumb-line"><span></span><span>/</span><strong></strong></div>
<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} 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>
</section>
);
}