Initial CMPP frontend prototype
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Filter, Pencil, Plus, Search, Trash2 } from 'lucide-react';
|
||||
import { Button, Input, Modal, Select, Table, Textarea, Tag, type TableColumn } from '@/components/ui';
|
||||
|
||||
type DrainageFieldType = '字符串' | '整数' | '文件' | '网址' | '电话' | '日期';
|
||||
|
||||
type DrainageField = {
|
||||
id: string;
|
||||
name: string;
|
||||
type: DrainageFieldType;
|
||||
description: string;
|
||||
channels: number;
|
||||
channel: 'sms' | 'mms' | 'all';
|
||||
};
|
||||
|
||||
const typeOptions = [
|
||||
{ label: '全部类型', value: 'all' },
|
||||
{ label: '字符串', value: '字符串' },
|
||||
{ label: '整数', value: '整数' },
|
||||
{ label: '文件', value: '文件' },
|
||||
{ label: '网址', value: '网址' },
|
||||
{ label: '电话', value: '电话' },
|
||||
{ label: '日期', value: '日期' },
|
||||
];
|
||||
|
||||
const channelOptions = [
|
||||
{ label: '全部通道', value: 'all' },
|
||||
{ label: '短信通道', value: 'sms' },
|
||||
{ label: '彩信通道', value: 'mms' },
|
||||
];
|
||||
|
||||
const initialFields: DrainageField[] = [
|
||||
{ id: 'DRF20260630001', name: '应用ID', type: '字符串', description: '在应用集成中创建的短信应用 ID', channels: 1, channel: 'sms' },
|
||||
{ id: 'DRF20260630002', name: '应用密匙', type: '字符串', description: '应用密匙或数字签名', channels: 1, channel: 'sms' },
|
||||
{ id: 'DRF20260630003', name: '短信签名', type: '字符串', description: '短信签名,【】符号可省略', channels: 1, channel: 'sms' },
|
||||
{ id: 'DRF20260630004', name: '短信用途', type: '整数', description: '0-行业通知短信、1-营销推广短信', channels: 1, channel: 'sms' },
|
||||
{ id: 'DRF20260630005', name: '证明材料', type: '文件', description: '上传营业执照、授权书等证明材料', channels: 1, channel: 'all' },
|
||||
{ id: 'DRF20260630006', name: '引流链接', type: '网址', description: '短信内链接地址', channels: 2, channel: 'all' },
|
||||
{ id: 'DRF20260630007', name: '引流号码', type: '电话', description: '引流号码1', channels: 3, channel: 'sms' },
|
||||
{ id: 'DRF20260630008', name: '机主姓名', type: '字符串', description: '引流号码1机主姓名', channels: 3, channel: 'sms' },
|
||||
{ id: 'DRF20260630009', name: 'ICP备案号', type: '字符串', description: '域名ICP备案号', channels: 1, channel: 'all' },
|
||||
{ id: 'DRF20260630010', name: '拨测日期', type: '日期', description: '引流号码拨测日期', channels: 2, channel: 'sms' },
|
||||
];
|
||||
|
||||
function createFieldId() {
|
||||
return `DRF${Date.now()}`;
|
||||
}
|
||||
|
||||
type FieldFormModalProps = {
|
||||
item?: DrainageField;
|
||||
onClose: () => void;
|
||||
onSubmit: (item: DrainageField) => void;
|
||||
};
|
||||
|
||||
function FieldFormModal({ item, onClose, onSubmit }: FieldFormModalProps) {
|
||||
const [form, setForm] = useState<DrainageField>(() => item ?? {
|
||||
id: createFieldId(),
|
||||
name: '',
|
||||
type: '字符串',
|
||||
description: '',
|
||||
channels: 1,
|
||||
channel: 'sms',
|
||||
});
|
||||
|
||||
function updateField<Key extends keyof DrainageField>(key: Key, value: DrainageField[Key]) {
|
||||
setForm((current) => ({ ...current, [key]: value }));
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
footer={(
|
||||
<>
|
||||
<Button onClick={onClose} variant="ghost">取消</Button>
|
||||
<Button onClick={() => onSubmit(form)}>保存</Button>
|
||||
</>
|
||||
)}
|
||||
onClose={onClose}
|
||||
open
|
||||
title={item ? '编辑报备字段' : '添加报备字段'}
|
||||
>
|
||||
<div className="admin-system-modal-form">
|
||||
<Input label="字段名称" onChange={(event) => updateField('name', event.target.value)} value={form.name} />
|
||||
<Select
|
||||
label="字段类型"
|
||||
onChange={(event) => updateField('type', event.target.value as DrainageFieldType)}
|
||||
options={typeOptions.filter((option) => option.value !== 'all')}
|
||||
value={form.type}
|
||||
/>
|
||||
<Select
|
||||
label="适用通道"
|
||||
onChange={(event) => updateField('channel', event.target.value as DrainageField['channel'])}
|
||||
options={channelOptions}
|
||||
value={form.channel}
|
||||
/>
|
||||
<Input label="使用通道数" min={1} onChange={(event) => updateField('channels', Number(event.target.value) || 1)} type="number" value={form.channels} />
|
||||
<Textarea
|
||||
className="admin-system-modal-form__wide"
|
||||
label="描述"
|
||||
onChange={(event) => updateField('description', event.target.value)}
|
||||
rows={4}
|
||||
value={form.description}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export function AdminDrainageFieldsPage() {
|
||||
const [fields, setFields] = useState(initialFields);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const [channel, setChannel] = useState('all');
|
||||
const [type, setType] = useState('all');
|
||||
const [editingField, setEditingField] = useState<DrainageField | null>(null);
|
||||
const [creating, setCreating] = useState(false);
|
||||
|
||||
const filteredFields = useMemo(
|
||||
() => fields.filter((field) => {
|
||||
const matchesKeyword = [field.name, field.type, field.description].some((value) => value.includes(keyword));
|
||||
const matchesChannel = channel === 'all' || field.channel === channel || field.channel === 'all';
|
||||
const matchesType = type === 'all' || field.type === type;
|
||||
return matchesKeyword && matchesChannel && matchesType;
|
||||
}),
|
||||
[channel, fields, keyword, type],
|
||||
);
|
||||
|
||||
function upsertField(nextField: DrainageField) {
|
||||
setFields((current) => {
|
||||
const exists = current.some((item) => item.id === nextField.id);
|
||||
if (exists) {
|
||||
return current.map((item) => (item.id === nextField.id ? nextField : item));
|
||||
}
|
||||
|
||||
return [nextField, ...current];
|
||||
});
|
||||
setEditingField(null);
|
||||
setCreating(false);
|
||||
}
|
||||
|
||||
const columns = useMemo<Array<TableColumn<DrainageField>>>(() => [
|
||||
{ key: 'name', title: '字段名称', width: '190px', render: (record) => <strong>{record.name}</strong> },
|
||||
{ key: 'type', title: '字段类型', width: '160px', render: (record) => <span className="admin-drainage-type">{record.type}</span> },
|
||||
{ key: 'description', title: '描述', render: (record) => record.description },
|
||||
{ key: 'channels', title: '使用通道数', width: '170px', render: (record) => <Tag>{record.channels} 个通道</Tag> },
|
||||
{
|
||||
key: 'actions',
|
||||
title: '操作',
|
||||
width: '140px',
|
||||
align: 'right',
|
||||
render: (record) => (
|
||||
<div className="admin-drainage-actions">
|
||||
<Button icon={<Pencil size={17} />} iconOnly onClick={() => setEditingField(record)} variant="ghost">编辑</Button>
|
||||
<Button
|
||||
icon={<Trash2 size={17} />}
|
||||
iconOnly
|
||||
onClick={() => setFields((current) => current.filter((item) => item.id !== record.id))}
|
||||
variant="danger"
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
], []);
|
||||
|
||||
return (
|
||||
<section className="page-stack admin-system-page admin-drainage-page">
|
||||
<div className="page-heading">
|
||||
<div>
|
||||
<div className="breadcrumb-line">基础配置 / <strong>引流信息报备字段库</strong></div>
|
||||
<h1>引流信息报备字段库</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="surface admin-drainage-toolbar">
|
||||
<Input onChange={(event) => setKeyword(event.target.value)} placeholder="搜索字段名称、代码或描述..." prefix={<Search size={16} />} value={keyword} />
|
||||
<Select
|
||||
onChange={(event) => setChannel(event.target.value)}
|
||||
options={channelOptions}
|
||||
value={channel}
|
||||
/>
|
||||
<Select onChange={(event) => setType(event.target.value)} options={typeOptions} value={type} />
|
||||
<Button icon={<Plus size={18} />} onClick={() => setCreating(true)}>添加字段</Button>
|
||||
</div>
|
||||
|
||||
<div className="surface admin-system-table-card admin-drainage-table-card">
|
||||
<Table columns={columns} data={filteredFields} emptyText="暂无字段" rowKey="id" />
|
||||
<div className="admin-drainage-pagination">
|
||||
<span>10条/页</span>
|
||||
<Button icon={<Filter size={16} />} iconOnly variant="ghost">筛选</Button>
|
||||
<Button size="sm">1</Button>
|
||||
<span>...</span>
|
||||
<Button size="sm" variant="ghost">1</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{creating ? <FieldFormModal onClose={() => setCreating(false)} onSubmit={upsertField} /> : null}
|
||||
{editingField ? <FieldFormModal item={editingField} onClose={() => setEditingField(null)} onSubmit={upsertField} /> : null}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user