feat: inherit channel report requirements

This commit is contained in:
hectorzhao
2026-07-12 16:14:54 +08:00
parent 053bcca990
commit 87ae4a2063
14 changed files with 612 additions and 65 deletions
+22 -23
View File
@@ -1,27 +1,29 @@
import { useEffect, useMemo, useState } from 'react';
import { ArrowLeft, Plus, Search } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { adminApi, type AdminChannel, type ChannelReportField } from '@/api/adminApi';
import { adminApi, type AdminChannel, type ChannelReportField, type DictionaryItem } from '@/api/adminApi';
import { Breadcrumb, Button, Input, Modal, Select, Table, Textarea, Tag, type TableColumn } from '@/components/ui';
export function AdminChannelReportPage() {
const navigate = useNavigate();
const [channels, setChannels] = useState<AdminChannel[]>([]);
const [fields, setFields] = useState<ChannelReportField[]>([]);
const [libraryFields, setLibraryFields] = useState<DictionaryItem[]>([]);
const [channelId, setChannelId] = useState('');
const [keyword, setKeyword] = useState('');
const [modalOpen, setModalOpen] = useState(false);
const [code, setCode] = useState('');
const [name, setName] = useState('');
const [fieldType, setFieldType] = useState('string');
const [drainageFieldId, setDrainageFieldId] = useState('');
const [reportType, setReportType] = useState<'signature' | 'drainage' | 'both'>('signature');
const [required, setRequired] = useState(false);
const [description, setDescription] = useState('');
const [error, setError] = useState('');
function loadData(nextChannelId = channelId) {
Promise.all([adminApi.listChannels(), adminApi.listChannelReportFields(nextChannelId || undefined)])
.then(([channelItems, fieldItems]) => {
Promise.all([adminApi.listChannels(), adminApi.listChannelReportFields(nextChannelId || undefined), adminApi.listDrainageFields()])
.then(([channelItems, fieldItems, libraryItems]) => {
setChannels(channelItems.filter((item) => item.status !== 'deleted'));
setFields(fieldItems);
setLibraryFields(libraryItems.filter((item) => item.status === 'active'));
setError('');
})
.catch((failure: Error) => setError(failure.message || '通道报备配置加载失败'));
@@ -34,12 +36,14 @@ export function AdminChannelReportPage() {
const filteredFields = useMemo(() => fields.filter((field) => !keyword || [field.code, field.name, field.fieldType, field.description].join(' ').includes(keyword)), [fields, keyword]);
function createField() {
adminApi.createChannelReportField({ channelId, code, name, fieldType, description, status: 'active' })
const selected = libraryFields.find((item) => item.id === drainageFieldId);
if (!selected) return;
adminApi.createChannelReportField({ channelId, drainageFieldId, reportType, required, description, status: 'active' })
.then(() => {
setModalOpen(false);
setCode('');
setName('');
setFieldType('string');
setDrainageFieldId('');
setReportType('signature');
setRequired(false);
setDescription('');
loadData();
})
@@ -51,6 +55,7 @@ export function AdminChannelReportPage() {
{ key: 'code', title: '字段代码', width: '160px', render: (record) => <strong>{record.code}</strong> },
{ key: 'name', title: '字段名称', width: '160px', render: (record) => record.name },
{ key: 'type', title: '字段类型', width: '120px', render: (record) => record.fieldType },
{ key: 'reportType', title: '报备用途', width: '140px', render: (record) => record.reportType === 'signature' ? '签名报备' : record.reportType === 'drainage' ? '引流信息报备' : '签名+引流' },
{ key: 'required', title: '必填', width: '90px', render: (record) => <Tag tone={record.required ? 'warning' : 'info'}>{record.required ? '是' : '否'}</Tag> },
{ key: 'description', title: '说明', render: (record) => record.description ?? '-' },
];
@@ -87,26 +92,20 @@ export function AdminChannelReportPage() {
</div>
<Modal
footer={<><Button onClick={() => setModalOpen(false)} variant="ghost"></Button><Button disabled={!channelId || !code || !name} onClick={createField}></Button></>}
footer={<><Button onClick={() => setModalOpen(false)} variant="ghost"></Button><Button disabled={!channelId || !drainageFieldId} onClick={createField}></Button></>}
onClose={() => setModalOpen(false)}
open={modalOpen}
title="新增通道报备字段"
>
<div className="admin-system-modal-form">
<Input label="字段代码" onChange={(event) => setCode(event.target.value)} value={code} />
<Input label="字段名称" onChange={(event) => setName(event.target.value)} value={name} />
<Select
label="字段类型"
onChange={(event) => setFieldType(event.target.value)}
options={[
{ label: '字符串', value: 'string' },
{ label: '数字', value: 'number' },
{ label: '文件', value: 'file' },
{ label: '图片', value: 'image' },
{ label: '网址', value: 'url' },
]}
value={fieldType}
label="报备字段库字段"
onChange={(event) => setDrainageFieldId(event.target.value)}
options={[{ label: '请选择字段', value: '' }, ...libraryFields.map((field) => ({ label: `${field.name ?? field.code}${field.code}`, value: field.id }))]}
value={drainageFieldId}
/>
<Select label="报备用途" onChange={(event) => setReportType(event.target.value as typeof reportType)} options={[{ label: '签名报备', value: 'signature' }, { label: '引流信息报备', value: 'drainage' }, { label: '签名+引流', value: 'both' }]} value={reportType} />
<Select label="是否必填" onChange={(event) => setRequired(event.target.value === 'true')} options={[{ label: '选填', value: 'false' }, { label: '必填', value: 'true' }]} value={String(required)} />
<Textarea label="说明" onChange={(event) => setDescription(event.target.value)} rows={4} value={description} />
</div>
</Modal>