refactor(web): 系统设置模块页面表单一致性重构
- 新增 useCrudDrawer hook 封装 CRUD Drawer 通用模式(状态管理/提交/错误处理) - 新增 useListData hook 封装非分页列表数据获取 - 11 个页面统一迁移到 DrawerForm + 共享 hooks,消除重复代码 - 错误处理统一使用 useApiRequest.execute(),移除内联 try-catch - Modal 全部替换为 DrawerForm,保持 UI 一致性 - 净减少 ~1300 行代码(858 增 / 2136 删)
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Space,
|
||||
Modal,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
Popconfirm,
|
||||
message,
|
||||
Typography,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
@@ -27,100 +25,46 @@ import {
|
||||
type CreateDictionaryItemRequest,
|
||||
type UpdateDictionaryItemRequest,
|
||||
} from '../../api/dictionaries';
|
||||
|
||||
// --- Types ---
|
||||
import { useListData } from '../../hooks/useListData';
|
||||
import { useCrudDrawer } from '../../hooks/useCrudDrawer';
|
||||
import { DrawerForm } from '../../components/DrawerForm';
|
||||
import { useApiRequest } from '../../hooks/useApiRequest';
|
||||
|
||||
type DictItem = DictionaryItemInfo;
|
||||
type Dictionary = DictionaryInfo;
|
||||
|
||||
// --- Component ---
|
||||
|
||||
export default function DictionaryManager() {
|
||||
const [dictionaries, setDictionaries] = useState<Dictionary[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [dictModalOpen, setDictModalOpen] = useState(false);
|
||||
const [editDict, setEditDict] = useState<Dictionary | null>(null);
|
||||
const [itemModalOpen, setItemModalOpen] = useState(false);
|
||||
const { data: dictionaries, loading, refresh } = useListData<Dictionary>(async () => {
|
||||
const result = await listDictionaries();
|
||||
return Array.isArray(result) ? result : result.data ?? [];
|
||||
});
|
||||
|
||||
const { execute } = useApiRequest();
|
||||
|
||||
// 字典 CRUD Drawer
|
||||
const dictDrawer = useCrudDrawer<Dictionary>({
|
||||
getId: (r) => r.id,
|
||||
onCreate: async (values) => {
|
||||
await createDictionary(values as unknown as CreateDictionaryRequest);
|
||||
},
|
||||
onUpdate: async (id, values) => {
|
||||
await updateDictionary(id, values as unknown as CreateDictionaryRequest & { version: number });
|
||||
},
|
||||
onSuccess: refresh,
|
||||
});
|
||||
|
||||
// 字典项 Drawer — 因需要 activeDictId,手写状态管理
|
||||
const [itemDrawerOpen, setItemDrawerOpen] = useState(false);
|
||||
const [activeDictId, setActiveDictId] = useState<string | null>(null);
|
||||
const [editItem, setEditItem] = useState<DictItem | null>(null);
|
||||
const [dictForm] = Form.useForm();
|
||||
const [itemForm] = Form.useForm();
|
||||
|
||||
const fetchDictionaries = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await listDictionaries();
|
||||
setDictionaries(Array.isArray(result) ? result : result.data ?? []);
|
||||
} catch {
|
||||
message.error('加载字典列表失败');
|
||||
}
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDictionaries();
|
||||
}, [fetchDictionaries]);
|
||||
|
||||
// --- Dictionary CRUD ---
|
||||
|
||||
const handleDictSubmit = async (values: CreateDictionaryRequest) => {
|
||||
try {
|
||||
if (editDict) {
|
||||
await updateDictionary(editDict.id, { ...values, version: editDict.version });
|
||||
message.success('字典更新成功');
|
||||
} else {
|
||||
await createDictionary(values);
|
||||
message.success('字典创建成功');
|
||||
}
|
||||
closeDictModal();
|
||||
fetchDictionaries();
|
||||
} catch (err: unknown) {
|
||||
const errorMsg =
|
||||
(err as { response?: { data?: { message?: string } } })?.response?.data
|
||||
?.message || '操作失败';
|
||||
message.error(errorMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteDict = async (id: string, version: number) => {
|
||||
try {
|
||||
await deleteDictionary(id, version);
|
||||
message.success('字典已删除');
|
||||
fetchDictionaries();
|
||||
} catch {
|
||||
message.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
const openEditDict = (dict: Dictionary) => {
|
||||
setEditDict(dict);
|
||||
dictForm.setFieldsValue({
|
||||
name: dict.name,
|
||||
code: dict.code,
|
||||
description: dict.description,
|
||||
});
|
||||
setDictModalOpen(true);
|
||||
};
|
||||
|
||||
const openCreateDict = () => {
|
||||
setEditDict(null);
|
||||
dictForm.resetFields();
|
||||
setDictModalOpen(true);
|
||||
};
|
||||
|
||||
const closeDictModal = () => {
|
||||
setDictModalOpen(false);
|
||||
setEditDict(null);
|
||||
dictForm.resetFields();
|
||||
};
|
||||
|
||||
// --- Dictionary Item CRUD ---
|
||||
|
||||
const openAddItem = (dictId: string) => {
|
||||
setActiveDictId(dictId);
|
||||
setEditItem(null);
|
||||
itemForm.resetFields();
|
||||
setItemModalOpen(true);
|
||||
itemForm.setFieldsValue({ sort_order: 0 });
|
||||
setItemDrawerOpen(true);
|
||||
};
|
||||
|
||||
const openEditItem = (dictId: string, item: DictItem) => {
|
||||
@@ -132,75 +76,56 @@ export default function DictionaryManager() {
|
||||
sort_order: item.sort_order,
|
||||
color: item.color,
|
||||
});
|
||||
setItemModalOpen(true);
|
||||
setItemDrawerOpen(true);
|
||||
};
|
||||
|
||||
const handleItemSubmit = async (values: CreateDictionaryItemRequest & { sort_order: number }) => {
|
||||
if (!activeDictId) return;
|
||||
try {
|
||||
if (editItem) {
|
||||
await updateDictionaryItem(activeDictId, editItem.id, { ...values, version: editItem.version } as UpdateDictionaryItemRequest);
|
||||
message.success('字典项更新成功');
|
||||
} else {
|
||||
await createDictionaryItem(activeDictId, values);
|
||||
message.success('字典项添加成功');
|
||||
}
|
||||
closeItemModal();
|
||||
fetchDictionaries();
|
||||
} catch (err: unknown) {
|
||||
const errorMsg =
|
||||
(err as { response?: { data?: { message?: string } } })?.response?.data
|
||||
?.message || '操作失败';
|
||||
message.error(errorMsg);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteItem = async (dictId: string, itemId: string, version: number) => {
|
||||
try {
|
||||
await deleteDictionaryItem(dictId, itemId, version);
|
||||
message.success('字典项已删除');
|
||||
fetchDictionaries();
|
||||
} catch {
|
||||
message.error('删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
const closeItemModal = () => {
|
||||
setItemModalOpen(false);
|
||||
const closeItemDrawer = () => {
|
||||
setItemDrawerOpen(false);
|
||||
setActiveDictId(null);
|
||||
setEditItem(null);
|
||||
itemForm.resetFields();
|
||||
};
|
||||
|
||||
// --- Columns ---
|
||||
const handleItemSubmit = async (values: Record<string, unknown>) => {
|
||||
if (!activeDictId) return;
|
||||
const itemValues = values as unknown as CreateDictionaryItemRequest & { sort_order: number };
|
||||
if (editItem) {
|
||||
await execute(
|
||||
() => updateDictionaryItem(activeDictId, editItem.id, { ...itemValues, version: editItem.version } as UpdateDictionaryItemRequest),
|
||||
'字典项更新成功',
|
||||
);
|
||||
} else {
|
||||
await execute(() => createDictionaryItem(activeDictId, itemValues), '字典项添加成功');
|
||||
}
|
||||
closeItemDrawer();
|
||||
refresh();
|
||||
};
|
||||
|
||||
const handleDeleteDict = async (id: string, version: number) => {
|
||||
await execute(() => deleteDictionary(id, version), '字典已删除');
|
||||
refresh();
|
||||
};
|
||||
|
||||
const handleDeleteItem = async (dictId: string, itemId: string, version: number) => {
|
||||
await execute(() => deleteDictionaryItem(dictId, itemId, version), '字典项已删除');
|
||||
refresh();
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ title: '名称', dataIndex: 'name', key: 'name' },
|
||||
{ title: '编码', dataIndex: 'code', key: 'code' },
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'description',
|
||||
key: 'description',
|
||||
ellipsis: true,
|
||||
},
|
||||
{ title: '说明', dataIndex: 'description', key: 'description', ellipsis: true },
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
render: (_: unknown, record: Dictionary) => (
|
||||
<Space>
|
||||
<Button size="small" onClick={() => openAddItem(record.id)}>
|
||||
添加项
|
||||
</Button>
|
||||
<Button size="small" onClick={() => openEditDict(record)}>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定删除此字典?"
|
||||
onConfirm={() => handleDeleteDict(record.id, record.version)}
|
||||
>
|
||||
<Button size="small" danger>
|
||||
删除
|
||||
</Button>
|
||||
<Button size="small" onClick={() => openAddItem(record.id)}>添加项</Button>
|
||||
<Button size="small" onClick={() => dictDrawer.openEdit(record, (r) => ({
|
||||
name: r.name, code: r.code, description: r.description,
|
||||
}))}>编辑</Button>
|
||||
<Popconfirm title="确定删除此字典?" onConfirm={() => handleDeleteDict(record.id, record.version)}>
|
||||
<Button size="small" danger>删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
@@ -212,31 +137,17 @@ export default function DictionaryManager() {
|
||||
{ title: '值', dataIndex: 'value', key: 'value' },
|
||||
{ title: '排序', dataIndex: 'sort_order', key: 'sort_order', width: 80 },
|
||||
{
|
||||
title: '颜色',
|
||||
dataIndex: 'color',
|
||||
key: 'color',
|
||||
width: 80,
|
||||
render: (color?: string) =>
|
||||
color ? <Tag color={color}>{color}</Tag> : '-',
|
||||
title: '颜色', dataIndex: 'color', key: 'color', width: 80,
|
||||
render: (color?: string) => color ? <Tag color={color}>{color}</Tag> : '-',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
render: (_: unknown, record: DictItem) => (
|
||||
<Space>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={() => openEditItem(dictId, record)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定删除此字典项?"
|
||||
onConfirm={() => handleDeleteItem(dictId, record.id, record.version)}
|
||||
>
|
||||
<Button size="small" danger>
|
||||
删除
|
||||
</Button>
|
||||
<Button size="small" onClick={() => openEditItem(dictId, record)}>编辑</Button>
|
||||
<Popconfirm title="确定删除此字典项?" onConfirm={() => handleDeleteItem(dictId, record.id, record.version)}>
|
||||
<Button size="small" danger>删除</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
),
|
||||
@@ -245,17 +156,9 @@ export default function DictionaryManager() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 16,
|
||||
}}
|
||||
>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>
|
||||
数据字典管理
|
||||
</Typography.Title>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={openCreateDict}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
|
||||
<Typography.Title level={5} style={{ margin: 0 }}>数据字典管理</Typography.Title>
|
||||
<Button type="primary" icon={<PlusOutlined />} onClick={() => dictDrawer.openCreate()}>
|
||||
新建字典
|
||||
</Button>
|
||||
</div>
|
||||
@@ -279,68 +182,52 @@ export default function DictionaryManager() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Dictionary Modal */}
|
||||
<Modal
|
||||
title={editDict ? '编辑字典' : '新建字典'}
|
||||
open={dictModalOpen}
|
||||
onCancel={closeDictModal}
|
||||
onOk={() => dictForm.submit()}
|
||||
{/* 字典 Drawer */}
|
||||
<DrawerForm
|
||||
title={dictDrawer.editingRecord ? '编辑字典' : '新建字典'}
|
||||
open={dictDrawer.open}
|
||||
onClose={dictDrawer.close}
|
||||
onSubmit={dictDrawer.handleSubmit}
|
||||
initialValues={dictDrawer.initialValues}
|
||||
loading={dictDrawer.loading}
|
||||
width={480}
|
||||
columns={1}
|
||||
>
|
||||
<Form form={dictForm} onFinish={handleDictSubmit} layout="vertical">
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="名称"
|
||||
rules={[{ required: true, message: '请输入字典名称' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="code"
|
||||
label="编码"
|
||||
rules={[{ required: true, message: '请输入字典编码' }]}
|
||||
>
|
||||
<Input disabled={!!editDict} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="说明">
|
||||
<Input.TextArea rows={3} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Form.Item name="name" label="名称" rules={[{ required: true, message: '请输入字典名称' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="code" label="编码" rules={[{ required: true, message: '请输入字典编码' }]}>
|
||||
<Input disabled={!!dictDrawer.editingRecord} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="说明">
|
||||
<Input.TextArea rows={3} />
|
||||
</Form.Item>
|
||||
</DrawerForm>
|
||||
|
||||
{/* Dictionary Item Modal */}
|
||||
<Modal
|
||||
{/* 字典项 Drawer */}
|
||||
<DrawerForm
|
||||
title={editItem ? '编辑字典项' : '添加字典项'}
|
||||
open={itemModalOpen}
|
||||
onCancel={closeItemModal}
|
||||
onOk={() => itemForm.submit()}
|
||||
open={itemDrawerOpen}
|
||||
onClose={closeItemDrawer}
|
||||
onSubmit={handleItemSubmit}
|
||||
initialValues={editItem ? { label: editItem.label, value: editItem.value, sort_order: editItem.sort_order, color: editItem.color } : { sort_order: 0 }}
|
||||
loading={false}
|
||||
width={480}
|
||||
columns={1}
|
||||
>
|
||||
<Form form={itemForm} onFinish={handleItemSubmit} layout="vertical">
|
||||
<Form.Item
|
||||
name="label"
|
||||
label="标签"
|
||||
rules={[{ required: true, message: '请输入标签' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="value"
|
||||
label="值"
|
||||
rules={[{ required: true, message: '请输入值' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="sort_order"
|
||||
label="排序"
|
||||
initialValue={0}
|
||||
>
|
||||
<InputNumber min={0} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="color" label="颜色">
|
||||
<Input placeholder="如:blue, red, green 或十六进制色值" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Form.Item name="label" label="标签" rules={[{ required: true, message: '请输入标签' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="value" label="值" rules={[{ required: true, message: '请输入值' }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="sort_order" label="排序" initialValue={0}>
|
||||
<InputNumber min={0} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item name="color" label="颜色">
|
||||
<Input placeholder="如:blue, red, green 或十六进制色值" />
|
||||
</Form.Item>
|
||||
</DrawerForm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user