feat: add reconciliation and quality reporting
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Plus, Search, Trash2 } from 'lucide-react';
|
||||
import { Breadcrumb, Button, Input, Modal, Select, Table, Textarea, Tag, type TableColumn } from '@/components/ui';
|
||||
import { adminApi, type DictionaryItem } from '@/api/adminApi';
|
||||
import { adminApi, type CommonReportField, type DictionaryItem } from '@/api/adminApi';
|
||||
|
||||
type DrainageField = DictionaryItem & {
|
||||
code?: string;
|
||||
@@ -10,6 +10,7 @@ type DrainageField = DictionaryItem & {
|
||||
required?: boolean;
|
||||
description?: string | null;
|
||||
usageCount?: number;
|
||||
commonUsageCount?: number;
|
||||
};
|
||||
|
||||
type ReportFieldType = 'string' | 'image' | 'file';
|
||||
@@ -25,6 +26,7 @@ const typeLabels: Record<string, string> = { string: '字符串', image: '图片
|
||||
|
||||
export function AdminDrainageFieldsPage() {
|
||||
const [fields, setFields] = useState<DrainageField[]>([]);
|
||||
const [commonFields, setCommonFields] = useState<CommonReportField[]>([]);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [appliedKeyword, setAppliedKeyword] = useState('');
|
||||
const [type, setType] = useState('all');
|
||||
@@ -36,12 +38,18 @@ export function AdminDrainageFieldsPage() {
|
||||
const [description, setDescription] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const [deleteTarget, setDeleteTarget] = useState<DrainageField | null>(null);
|
||||
const [configuringCommon, setConfiguringCommon] = useState(false);
|
||||
const [commonFieldId, setCommonFieldId] = useState('');
|
||||
const [commonReportType, setCommonReportType] = useState<'signature' | 'drainage'>('signature');
|
||||
const [commonRequired, setCommonRequired] = useState(false);
|
||||
const [commonDeleteTarget, setCommonDeleteTarget] = useState<CommonReportField | null>(null);
|
||||
const codeError = code && !/^[A-Za-z0-9]+$/.test(code) ? '字段代码只能包含阿拉伯数字和英文大小写字母' : '';
|
||||
|
||||
function loadData() {
|
||||
adminApi.listDrainageFields()
|
||||
.then((items) => {
|
||||
Promise.all([adminApi.listDrainageFields(), adminApi.listCommonReportFields()])
|
||||
.then(([items, commonItems]) => {
|
||||
setFields(items as DrainageField[]);
|
||||
setCommonFields(commonItems);
|
||||
setError('');
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '报备字段加载失败'));
|
||||
@@ -74,7 +82,7 @@ export function AdminDrainageFieldsPage() {
|
||||
}
|
||||
|
||||
function deleteField() {
|
||||
if (!deleteTarget || (deleteTarget.usageCount ?? 0) > 0) return;
|
||||
if (!deleteTarget || (deleteTarget.usageCount ?? 0) > 0 || (deleteTarget.commonUsageCount ?? 0) > 0) return;
|
||||
adminApi.deleteDrainageField(deleteTarget.id)
|
||||
.then(() => {
|
||||
setDeleteTarget(null);
|
||||
@@ -83,6 +91,29 @@ export function AdminDrainageFieldsPage() {
|
||||
.catch((failure: Error) => setError(failure.message || '报备字段删除失败'));
|
||||
}
|
||||
|
||||
function createCommonField() {
|
||||
if (!commonFieldId) return;
|
||||
adminApi.createCommonReportField({ drainageFieldId: commonFieldId, reportType: commonReportType, required: commonRequired })
|
||||
.then(() => {
|
||||
setCommonFieldId('');
|
||||
setCommonReportType('signature');
|
||||
setCommonRequired(false);
|
||||
setConfiguringCommon(false);
|
||||
loadData();
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '通用字段配置失败'));
|
||||
}
|
||||
|
||||
function deleteCommonField() {
|
||||
if (!commonDeleteTarget) return;
|
||||
adminApi.deleteCommonReportField(commonDeleteTarget.id)
|
||||
.then(() => {
|
||||
setCommonDeleteTarget(null);
|
||||
loadData();
|
||||
})
|
||||
.catch((failure: Error) => setError(failure.message || '通用字段删除失败'));
|
||||
}
|
||||
|
||||
const columns = useMemo<Array<TableColumn<DrainageField>>>(() => [
|
||||
{ key: 'code', title: '字段代码', width: '160px', render: (record) => <strong>{record.code}</strong> },
|
||||
{ key: 'name', title: '字段名称', width: '190px', render: (record) => record.name ?? '-' },
|
||||
@@ -90,7 +121,17 @@ export function AdminDrainageFieldsPage() {
|
||||
{ key: 'description', title: '描述', render: (record) => record.description ?? '-' },
|
||||
{ key: 'required', title: '是否必填', width: '120px', render: (record) => <Tag tone={record.required ? 'warning' : 'info'}>{record.required ? '必填' : '选填'}</Tag> },
|
||||
{ key: 'usageCount', title: '使用通道数', width: '130px', render: (record) => <Tag tone={(record.usageCount ?? 0) > 0 ? 'warning' : 'neutral'}>{record.usageCount ?? 0}</Tag> },
|
||||
{ key: 'actions', title: '操作', width: '120px', align: 'right', render: (record) => <Button disabled={(record.usageCount ?? 0) > 0} icon={<Trash2 size={14} />} onClick={() => setDeleteTarget(record)} size="sm" variant="danger">删除</Button> },
|
||||
{ key: 'commonUsageCount', title: '通用配置数', width: '130px', render: (record) => <Tag tone={(record.commonUsageCount ?? 0) > 0 ? 'info' : 'neutral'}>{record.commonUsageCount ?? 0}</Tag> },
|
||||
{ key: 'actions', title: '操作', width: '120px', align: 'right', render: (record) => <Button disabled={(record.usageCount ?? 0) > 0 || (record.commonUsageCount ?? 0) > 0} icon={<Trash2 size={14} />} onClick={() => setDeleteTarget(record)} size="sm" variant="danger">删除</Button> },
|
||||
], []);
|
||||
|
||||
const commonColumns = useMemo<Array<TableColumn<CommonReportField>>>(() => [
|
||||
{ key: 'name', title: '字段名称', render: (record) => <strong>{String(record.drainageField.name ?? record.drainageField.code ?? '-')}</strong> },
|
||||
{ key: 'code', title: '字段代码', width: '170px', render: (record) => String(record.drainageField.code ?? '-') },
|
||||
{ key: 'reportType', title: '资料用途', width: '180px', render: (record) => <Tag tone={record.reportType === 'signature' ? 'info' : 'warning'}>{record.reportType === 'signature' ? '签名报备资料' : '引流信息报备资料'}</Tag> },
|
||||
{ key: 'fieldType', title: '字段类型', width: '130px', render: (record) => typeLabels[String(record.drainageField.fieldType ?? '')] ?? String(record.drainageField.fieldType ?? '-') },
|
||||
{ key: 'required', title: '是否必填', width: '120px', render: (record) => <Tag tone={record.required ? 'warning' : 'neutral'}>{record.required ? '必填' : '选填'}</Tag> },
|
||||
{ key: 'actions', title: '操作', width: '120px', align: 'right', render: (record) => <Button icon={<Trash2 size={14} />} onClick={() => setCommonDeleteTarget(record)} size="sm" variant="danger">删除</Button> },
|
||||
], []);
|
||||
|
||||
return (
|
||||
@@ -114,9 +155,34 @@ export function AdminDrainageFieldsPage() {
|
||||
</div>
|
||||
|
||||
<div className="surface admin-system-table-card admin-drainage-table-card">
|
||||
<div className="page-heading">
|
||||
<div>
|
||||
<h2>通用字段配置</h2>
|
||||
<p>通用字段会与企业应用目标通道配置的字段合并,分别用于签名报备资料和引流信息报备资料。</p>
|
||||
</div>
|
||||
<Button icon={<Plus size={18} />} onClick={() => setConfiguringCommon(true)}>配置通用字段</Button>
|
||||
</div>
|
||||
<Table columns={commonColumns} data={commonFields} emptyText="暂无通用字段配置" rowKey="id" />
|
||||
</div>
|
||||
|
||||
<div className="surface admin-system-table-card admin-drainage-table-card">
|
||||
<div className="page-heading"><div><h2>字段库</h2><p>字段定义被通道或通用配置引用后,需要先删除对应配置才能删除字段。</p></div></div>
|
||||
<Table columns={columns} data={filteredFields} emptyText="暂无字段" rowKey="id" />
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
footer={<><Button onClick={() => setConfiguringCommon(false)} variant="ghost">取消</Button><Button disabled={!commonFieldId} onClick={createCommonField}>保存</Button></>}
|
||||
onClose={() => setConfiguringCommon(false)}
|
||||
open={configuringCommon}
|
||||
title="配置通用字段"
|
||||
>
|
||||
<div className="admin-system-modal-form">
|
||||
<Select label="报备字段" onChange={(event) => setCommonFieldId(event.target.value)} options={[{ label: '请选择字段', value: '' }, ...fields.filter((field) => field.status !== 'deleted').map((field) => ({ label: `${field.name}(${field.code})`, value: field.id }))]} value={commonFieldId} />
|
||||
<Select label="资料用途" onChange={(event) => setCommonReportType(event.target.value as 'signature' | 'drainage')} options={[{ label: '签名报备资料', value: 'signature' }, { label: '引流信息报备资料', value: 'drainage' }]} value={commonReportType} />
|
||||
<Select label="是否必填" onChange={(event) => setCommonRequired(event.target.value === 'true')} options={[{ label: '选填', value: 'false' }, { label: '必填', value: 'true' }]} value={String(commonRequired)} />
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
footer={(
|
||||
<>
|
||||
@@ -156,6 +222,16 @@ export function AdminDrainageFieldsPage() {
|
||||
<p>确认删除“{deleteTarget.name}”吗?未被通道使用的字段将从数据库中永久删除。</p>
|
||||
</Modal>
|
||||
) : null}
|
||||
{commonDeleteTarget ? (
|
||||
<Modal
|
||||
footer={<><Button onClick={() => setCommonDeleteTarget(null)} variant="ghost">取消</Button><Button onClick={deleteCommonField} variant="danger">确认删除</Button></>}
|
||||
onClose={() => setCommonDeleteTarget(null)}
|
||||
open
|
||||
title="删除通用字段配置"
|
||||
>
|
||||
<p>确认删除“{String(commonDeleteTarget.drainageField.name ?? commonDeleteTarget.drainageField.code)}”的{commonDeleteTarget.reportType === 'signature' ? '签名报备' : '引流信息报备'}通用配置吗?字段库定义和历史报备资料不会删除。</p>
|
||||
</Modal>
|
||||
) : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user