328 lines
12 KiB
TypeScript
328 lines
12 KiB
TypeScript
import React, { useState, useEffect, useCallback } from 'react';
|
|
import {
|
|
Table, Form, Input, Select, Button, Modal, message, Space, Tag, DatePicker, Popconfirm, Radio
|
|
} from 'antd';
|
|
import type { TableColumnsType } from 'antd';
|
|
import {
|
|
SearchOutlined, ReloadOutlined, PlusOutlined, EditOutlined, DeleteOutlined, DownloadOutlined, ExclamationCircleOutlined, SyncOutlined
|
|
} from '@ant-design/icons';
|
|
import {
|
|
listDictType, getDictType, addDictType, updateDictType, delDictType, refreshCache
|
|
} from '../../api/system/dict';
|
|
import { saveAs } from 'file-saver';
|
|
import dayjs from 'dayjs';
|
|
import 'dayjs/locale/zh-cn';
|
|
import zhCN from 'antd/es/date-picker/locale/zh_CN';
|
|
import { parseTime } from '../../utils/ruoyi';
|
|
import { Link } from 'react-router-dom';
|
|
|
|
const { RangePicker } = DatePicker;
|
|
dayjs.locale('zh-cn');
|
|
|
|
// Mock Dictionaries
|
|
const sysNormalDisableDict = [
|
|
{ value: '0', label: '正常' },
|
|
{ value: '1', label: '停用' },
|
|
];
|
|
|
|
const DictPage: React.FC = () => {
|
|
const [queryForm] = Form.useForm();
|
|
const [dictForm] = Form.useForm();
|
|
const [loading, setLoading] = useState(false);
|
|
const [typeList, setTypeList] = useState<any[]>([]);
|
|
const [total, setTotal] = useState(0);
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
|
const [dateRange, setDateRange] = useState<[dayjs.Dayjs, dayjs.Dayjs] | null>(null);
|
|
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
const [modalTitle, setModalTitle] = useState('');
|
|
const [currentDict, setCurrentDict] = useState<any>({});
|
|
|
|
const [queryParams, setQueryParams] = useState({
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
dictName: undefined,
|
|
dictType: undefined,
|
|
status: undefined,
|
|
});
|
|
|
|
const getList = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const formattedQueryParams = { ...queryParams };
|
|
if (dateRange && dateRange.length === 2) {
|
|
formattedQueryParams['beginTime'] = dateRange[0].format('YYYY-MM-DD');
|
|
formattedQueryParams['endTime'] = dateRange[1].format('YYYY-MM-DD');
|
|
} else {
|
|
formattedQueryParams['beginTime'] = undefined;
|
|
formattedQueryParams['endTime'] = undefined;
|
|
}
|
|
const response = await listDictType(formattedQueryParams);
|
|
setTypeList(response.rows);
|
|
setTotal(response.total);
|
|
} catch (error) {
|
|
console.error('Failed to fetch dictionary type list:', error);
|
|
message.error('获取字典类型列表失败');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [queryParams, dateRange]);
|
|
|
|
useEffect(() => {
|
|
getList();
|
|
}, [getList]);
|
|
|
|
const handleQuery = () => {
|
|
setQueryParams(prev => ({ ...prev, pageNum: 1 }));
|
|
};
|
|
|
|
const resetQuery = () => {
|
|
queryForm.resetFields();
|
|
setDateRange(null);
|
|
setQueryParams({
|
|
pageNum: 1,
|
|
pageSize: 10,
|
|
dictName: undefined,
|
|
dictType: undefined,
|
|
status: undefined,
|
|
});
|
|
getList();
|
|
};
|
|
|
|
const resetDictForm = () => {
|
|
dictForm.resetFields();
|
|
setCurrentDict({});
|
|
};
|
|
|
|
const handleAdd = () => {
|
|
resetDictForm();
|
|
setModalTitle('添加字典类型');
|
|
setModalVisible(true);
|
|
};
|
|
|
|
const handleUpdate = async (row: any) => {
|
|
resetDictForm();
|
|
try {
|
|
const dictId = row.dictId || selectedRowKeys[0];
|
|
const response = await getDictType(dictId);
|
|
const detail = (response && typeof response === 'object' && 'data' in response)
|
|
? response.data ?? {}
|
|
: response ?? {};
|
|
setCurrentDict(detail);
|
|
dictForm.setFieldsValue({
|
|
...detail,
|
|
});
|
|
setModalTitle('修改字典类型');
|
|
setModalVisible(true);
|
|
} catch (error) {
|
|
message.error('获取字典类型详情失败');
|
|
}
|
|
};
|
|
|
|
const handleDelete = (row?: any) => {
|
|
const dictIds = row?.dictId ? [row.dictId] : selectedRowKeys;
|
|
if (dictIds.length === 0) {
|
|
message.warning('请选择要删除的字典类型');
|
|
return;
|
|
}
|
|
Modal.confirm({
|
|
title: '确认删除',
|
|
icon: <ExclamationCircleOutlined />,
|
|
content: `是否确认删除字典编号为"${dictIds.join(',')}"的数据项?`,
|
|
onOk: async () => {
|
|
try {
|
|
await delDictType(dictIds.join(','));
|
|
message.success('删除成功');
|
|
setSelectedRowKeys([]);
|
|
getList();
|
|
} catch (error) {
|
|
message.error('删除失败');
|
|
}
|
|
},
|
|
});
|
|
};
|
|
|
|
const handleExport = async () => {
|
|
const hide = message.loading('正在导出数据...', 0);
|
|
try {
|
|
const response = await listDictType({ ...queryParams, pageNum: undefined, pageSize: undefined });
|
|
const header = ['字典编号', '字典名称', '字典类型', '状态', '备注', '创建时间'];
|
|
const data = response.rows.map((dict: any) => [
|
|
dict.dictId, dict.dictName, dict.dictType,
|
|
sysNormalDisableDict.find((d) => d.value === String(dict.status ?? ''))?.label ?? '',
|
|
dict.remark, parseTime(dict.createTime),
|
|
]);
|
|
|
|
const csvContent = [header, ...data]
|
|
.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(','))
|
|
.join('\n');
|
|
|
|
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
saveAs(blob, `dict_type_${dayjs().format('YYYYMMDDHHmmss')}.csv`);
|
|
hide();
|
|
message.success('导出成功');
|
|
} catch {
|
|
hide();
|
|
message.error('导出失败');
|
|
}
|
|
};
|
|
|
|
const handleRefreshCache = async () => {
|
|
try {
|
|
await refreshCache();
|
|
message.success('刷新缓存成功');
|
|
} catch (error) {
|
|
message.error('刷新缓存失败');
|
|
}
|
|
};
|
|
|
|
const submitDictForm = async (values: any) => {
|
|
try {
|
|
const dictData = { ...currentDict, ...values };
|
|
if (dictData.dictId !== undefined) {
|
|
await updateDictType(dictData);
|
|
message.success('修改成功');
|
|
} else {
|
|
await addDictType(dictData);
|
|
message.success('新增成功');
|
|
}
|
|
setModalVisible(false);
|
|
getList();
|
|
} catch (error) {
|
|
console.error('Submit dictionary type form failed:', error);
|
|
message.error('操作失败');
|
|
}
|
|
};
|
|
|
|
const columns: TableColumnsType<any> = [
|
|
{ title: '字典编号', dataIndex: 'dictId', align: 'center' },
|
|
{ title: '字典名称', dataIndex: 'dictName', align: 'center', ellipsis: true },
|
|
{
|
|
title: '字典类型', dataIndex: 'dictType', align: 'center', ellipsis: true,
|
|
render: (text: string, record: any) => (
|
|
<Link to={`/system/dict-data/index/${record.dictId}`}>{text}</Link>
|
|
),
|
|
},
|
|
{
|
|
title: '状态', dataIndex: 'status', align: 'center',
|
|
render: (text: string) => <Tag color={text === '0' ? 'green' : 'red'}>{sysNormalDisableDict.find(d => d.value === text)?.label}</Tag>,
|
|
},
|
|
{ title: '备注', dataIndex: 'remark', align: 'center', ellipsis: true },
|
|
{ title: '创建时间', dataIndex: 'createTime', align: 'center', width: 180, render: (text: string) => parseTime(text) },
|
|
{
|
|
title: '操作', key: 'operation', align: 'center', width: 180,
|
|
render: (_, record) => (
|
|
<Space size="small">
|
|
<Button type="link" icon={<EditOutlined />} onClick={() => handleUpdate(record)}>修改</Button>
|
|
<Popconfirm
|
|
title="是否确认删除该字典类型?"
|
|
onConfirm={() => handleDelete(record)}
|
|
okText="是"
|
|
cancelText="否"
|
|
>
|
|
<Button type="link" danger icon={<DeleteOutlined />}>删除</Button>
|
|
</Popconfirm>
|
|
</Space>
|
|
),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="app-container">
|
|
<Form form={queryForm} layout="inline" className="search-form" onFinish={handleQuery} initialValues={queryParams}>
|
|
<Form.Item label="字典名称" name="dictName">
|
|
<Input placeholder="请输入字典名称" allowClear onPressEnter={handleQuery} style={{ width: 240 }} />
|
|
</Form.Item>
|
|
<Form.Item label="字典类型" name="dictType">
|
|
<Input placeholder="请输入字典类型" allowClear onPressEnter={handleQuery} style={{ width: 240 }} />
|
|
</Form.Item>
|
|
<Form.Item label="状态" name="status">
|
|
<Select placeholder="字典状态" allowClear style={{ width: 240 }}>
|
|
{sysNormalDisableDict.map(dict => (
|
|
<Select.Option key={dict.value} value={dict.value}>{dict.label}</Select.Option>
|
|
))}
|
|
</Select>
|
|
</Form.Item>
|
|
<Form.Item label="创建时间">
|
|
<RangePicker
|
|
value={dateRange}
|
|
onChange={(dates) => setDateRange(dates)}
|
|
locale={zhCN}
|
|
format="YYYY年MM月DD日"
|
|
placeholder={['开始日期', '结束日期']}
|
|
style={{ width: 240 }}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item>
|
|
<Button type="primary" icon={<SearchOutlined />} htmlType="submit">搜索</Button>
|
|
<Button icon={<ReloadOutlined />} onClick={resetQuery} style={{ marginLeft: 8 }}>重置</Button>
|
|
</Form.Item>
|
|
</Form>
|
|
|
|
<Space className="mb8">
|
|
<Button type="primary" ghost icon={<PlusOutlined />} onClick={handleAdd}>新增</Button>
|
|
<Button type="primary" ghost icon={<EditOutlined />} disabled={selectedRowKeys.length !== 1} onClick={() => handleUpdate(selectedRowKeys)}>修改</Button>
|
|
<Button type="danger" ghost icon={<DeleteOutlined />} disabled={selectedRowKeys.length === 0} onClick={() => handleDelete()}>删除</Button>
|
|
<Button type="warning" ghost icon={<DownloadOutlined />} onClick={handleExport}>导出</Button>
|
|
<Button type="danger" ghost icon={<SyncOutlined />} onClick={handleRefreshCache}>刷新缓存</Button>
|
|
</Space>
|
|
|
|
<Table
|
|
columns={columns}
|
|
dataSource={typeList}
|
|
rowKey="dictId"
|
|
loading={loading}
|
|
rowSelection={{
|
|
selectedRowKeys,
|
|
onChange: (keys) => setSelectedRowKeys(keys),
|
|
}}
|
|
pagination={{
|
|
current: queryParams.pageNum,
|
|
pageSize: queryParams.pageSize,
|
|
total: total,
|
|
showSizeChanger: true,
|
|
showQuickJumper: true,
|
|
showTotal: (total) => `共 ${total} 条`,
|
|
onChange: (page, pageSize) => {
|
|
setQueryParams(prev => ({ ...prev, pageNum: page, pageSize: pageSize }));
|
|
},
|
|
}}
|
|
/>
|
|
|
|
{/* Add/Edit Dictionary Type Modal */}
|
|
<Modal
|
|
title={modalTitle}
|
|
open={modalVisible}
|
|
onOk={dictForm.submit}
|
|
onCancel={() => setModalVisible(false)}
|
|
width={500}
|
|
forceRender
|
|
>
|
|
<Form form={dictForm} labelCol={{ span: 5 }} wrapperCol={{ span: 16 }} onFinish={submitDictForm} initialValues={{ status: '0' }}>
|
|
<Form.Item name="dictId" hidden>
|
|
<Input />
|
|
</Form.Item>
|
|
<Form.Item label="字典名称" name="dictName" rules={[{ required: true, message: '请输入字典名称' }]}>
|
|
<Input placeholder="请输入字典名称" />
|
|
</Form.Item>
|
|
<Form.Item label="字典类型" name="dictType" rules={[{ required: true, message: '请输入字典类型' }]}>
|
|
<Input placeholder="请输入字典类型" />
|
|
</Form.Item>
|
|
<Form.Item label="状态" name="status">
|
|
<Radio.Group>
|
|
{sysNormalDisableDict.map(dict => (
|
|
<Radio key={dict.value} value={dict.value}>{dict.label}</Radio>
|
|
))}
|
|
</Radio.Group>
|
|
</Form.Item>
|
|
<Form.Item label="备注" name="remark">
|
|
<Input.TextArea placeholder="请输入内容" />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DictPage;
|