Files
lislgosms/src/apps/admin/AdminChannelReportPage.tsx
T

111 lines
5.1 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { Plus, Search } from 'lucide-react';
import { adminApi, type AdminChannel, type ChannelReportField } from '@/api/adminApi';
import { Breadcrumb, Button, Input, Modal, Select, Table, Textarea, Tag, type TableColumn } from '@/components/ui';
export function AdminChannelReportPage() {
const [channels, setChannels] = useState<AdminChannel[]>([]);
const [fields, setFields] = useState<ChannelReportField[]>([]);
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 [description, setDescription] = useState('');
const [error, setError] = useState('');
function loadData(nextChannelId = channelId) {
Promise.all([adminApi.listChannels(), adminApi.listChannelReportFields(nextChannelId || undefined)])
.then(([channelItems, fieldItems]) => {
setChannels(channelItems.filter((item) => item.status !== 'deleted'));
setFields(fieldItems);
setError('');
})
.catch((failure: Error) => setError(failure.message || '通道报备配置加载失败'));
}
useEffect(() => {
loadData('');
}, []);
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' })
.then(() => {
setModalOpen(false);
setCode('');
setName('');
setFieldType('string');
setDescription('');
loadData();
})
.catch((failure: Error) => setError(failure.message || '报备字段保存失败'));
}
const columns: Array<TableColumn<ChannelReportField>> = [
{ key: 'channel', title: '通道', width: '220px', render: (record) => channels.find((item) => item.id === record.channelId)?.name ?? record.channelId },
{ 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: 'required', title: '必填', width: '90px', render: (record) => <Tag tone={record.required ? 'warning' : 'info'}>{record.required ? '是' : '否'}</Tag> },
{ key: 'description', title: '说明', render: (record) => record.description ?? '-' },
];
return (
<section className="page-stack admin-system-page admin-drainage-page">
<div className="page-heading">
<div>
<Breadcrumb items={['报备管理', '通道报备配置']} />
<h1>通道报备配置</h1>
</div>
<Button disabled={!channelId} icon={<Plus size={16} />} onClick={() => setModalOpen(true)}>新增字段</Button>
</div>
{error ? <p className="form-error">{error}</p> : null}
<div className="surface admin-drainage-toolbar">
<Select
onChange={(event) => {
setChannelId(event.target.value);
loadData(event.target.value);
}}
options={[{ label: '全部通道', value: '' }, ...channels.map((item) => ({ label: item.name, value: item.id }))]}
value={channelId}
/>
<Input onChange={(event) => setKeyword(event.target.value)} placeholder="搜索字段代码、名称或说明" prefix={<Search size={16} />} value={keyword} />
<Button icon={<Search size={16} />} onClick={() => loadData()}>查询</Button>
</div>
<div className="surface admin-system-table-card admin-drainage-table-card">
<Table columns={columns} data={filteredFields} emptyText="暂无通道报备字段" rowKey="id" />
</div>
<Modal
footer={<><Button onClick={() => setModalOpen(false)} variant="ghost">取消</Button><Button disabled={!channelId || !code || !name} 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}
/>
<Textarea label="说明" onChange={(event) => setDescription(event.target.value)} rows={4} value={description} />
</div>
</Modal>
</section>
);
}