Files
novalon-manage-system/novalon-manage-web/src/pages/config/dict/index.tsx
T
张翔 35ee138f29 refactor(antd): 替换 Modal destroyOnClose 为 destroyOnHidden
antd 新版本将 destroyOnClose 重命名为 destroyOnHidden,
消除控制台废弃警告。涉及 user、menu、notify、dict、config 页面。
2026-05-06 14:16:16 +08:00

109 lines
6.8 KiB
TypeScript

import { useState, useEffect } from 'react'
import { Table, Button, Modal, Form, Input, InputNumber, Select, Tag, Space, message, Popconfirm, Card, Row, Col } from 'antd'
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons'
import type { ColumnsType } from 'antd/es/table'
import { dictApi } from '@/api/dict'
import type { DictType, DictData, CreateDictTypeRequest, CreateDictDataRequest, UpdateDictTypeRequest, UpdateDictDataRequest } from '@/api/dict'
export default function DictManagement() {
const [dictTypes, setDictTypes] = useState<DictType[]>([])
const [dictData, setDictData] = useState<DictData[]>([])
const [selectedType, setSelectedType] = useState<string>('')
const [loading, setLoading] = useState(false)
const [typeModalOpen, setTypeModalOpen] = useState(false)
const [dataModalOpen, setDataModalOpen] = useState(false)
const [editingType, setEditingType] = useState<DictType | null>(null)
const [editingData, setEditingData] = useState<DictData | null>(null)
const [typeForm] = Form.useForm()
const [dataForm] = Form.useForm()
useEffect(() => { loadDictTypes() }, [])
async function loadDictTypes() {
try { const res = await dictApi.getTypes(); setDictTypes(Array.isArray(res) ? res : []) } catch { /* ignored */ }
}
async function loadDictData(dictType: string) {
setLoading(true)
try { const res = await dictApi.getDataByType(dictType); setDictData(Array.isArray(res) ? res : []) } catch { /* ignored */ }
finally { setLoading(false) }
}
function handleSelectType(type: string) { setSelectedType(type); loadDictData(type) }
async function handleTypeSubmit() {
try {
const values = await typeForm.validateFields()
if (editingType) { await dictApi.updateType(editingType.id, values as UpdateDictTypeRequest); message.success('更新成功') }
else { await dictApi.createType(values as CreateDictTypeRequest); message.success('创建成功') }
setTypeModalOpen(false); loadDictTypes()
} catch { /* ignored */ }
}
async function handleDataSubmit() {
try {
const values = await dataForm.validateFields()
if (editingData) { await dictApi.updateData(editingData.id, { ...values, dictType: selectedType } as UpdateDictDataRequest); message.success('更新成功') }
else { await dictApi.createData({ ...values, dictType: selectedType } as CreateDictDataRequest); message.success('创建成功') }
setDataModalOpen(false); loadDictData(selectedType)
} catch { /* ignored */ }
}
const typeColumns: ColumnsType<DictType> = [
{ title: '字典名称', dataIndex: 'dictName', key: 'dictName' },
{ title: '字典类型', dataIndex: 'dictType', key: 'dictType', render: (v: string) => <a onClick={() => handleSelectType(v)}>{v}</a> },
{ title: '状态', dataIndex: 'status', key: 'status', render: (v: number) => <Tag color={v === 1 ? 'green' : 'red'}>{v === 1 ? '正常' : '停用'}</Tag> },
{ title: '操作', key: 'action', render: (_, record) => (
<Space>
<Button type="link" icon={<EditOutlined />} onClick={() => { setEditingType(record); typeForm.setFieldsValue(record); setTypeModalOpen(true) }} />
<Popconfirm title="确认删除?" onConfirm={async () => { await dictApi.deleteType(record.id); message.success('删除成功'); loadDictTypes() }}><Button type="link" danger icon={<DeleteOutlined />} /></Popconfirm>
</Space>
)},
]
const dataColumns: ColumnsType<DictData> = [
{ title: '字典标签', dataIndex: 'dictLabel', key: 'dictLabel' },
{ title: '字典值', dataIndex: 'dictValue', key: 'dictValue' },
{ title: '排序', dataIndex: 'sort', key: 'sort', width: 80 },
{ title: '状态', dataIndex: 'status', key: 'status', render: (v: number) => <Tag color={v === 1 ? 'green' : 'red'}>{v === 1 ? '正常' : '停用'}</Tag> },
{ title: '操作', key: 'action', render: (_, record) => (
<Space>
<Button type="link" icon={<EditOutlined />} onClick={() => { setEditingData(record); dataForm.setFieldsValue(record); setDataModalOpen(true) }} />
<Popconfirm title="确认删除?" onConfirm={async () => { await dictApi.deleteData(record.id); message.success('删除成功'); loadDictData(selectedType) }}><Button type="link" danger icon={<DeleteOutlined />} /></Popconfirm>
</Space>
)},
]
return (
<div style={{ padding: 24 }}>
<Row gutter={16}>
<Col span={10}>
<Card title="字典类型" extra={<Button type="primary" icon={<PlusOutlined />} onClick={() => { setEditingType(null); typeForm.resetFields(); setTypeModalOpen(true) }}></Button>}>
<Table<DictType> rowKey="id" columns={typeColumns} dataSource={dictTypes} pagination={false} size="small" />
</Card>
</Col>
<Col span={14}>
<Card title={`字典数据 ${selectedType ? `- ${selectedType}` : ''}`} extra={selectedType ? <Button type="primary" icon={<PlusOutlined />} onClick={() => { setEditingData(null); dataForm.resetFields(); setDataModalOpen(true) }}></Button> : null}>
{selectedType ? <Table<DictData> rowKey="id" columns={dataColumns} dataSource={dictData} loading={loading} pagination={false} size="small" /> : <p style={{ color: '#999', textAlign: 'center' }}></p>}
</Card>
</Col>
</Row>
<Modal title={editingType ? '编辑字典类型' : '新增字典类型'} open={typeModalOpen} onOk={handleTypeSubmit} onCancel={() => setTypeModalOpen(false)} destroyOnHidden>
<Form form={typeForm} layout="vertical">
<Form.Item name="dictName" label="字典名称" rules={[{ required: true }]}><Input /></Form.Item>
<Form.Item name="dictType" label="字典类型" rules={[{ required: true }]}><Input /></Form.Item>
<Form.Item name="status" label="状态" initialValue={1}><Select options={[{ label: '正常', value: 1 }, { label: '停用', value: 0 }]} /></Form.Item>
<Form.Item name="remark" label="备注"><Input.TextArea /></Form.Item>
</Form>
</Modal>
<Modal title={editingData ? '编辑字典数据' : '新增字典数据'} open={dataModalOpen} onOk={handleDataSubmit} onCancel={() => setDataModalOpen(false)} destroyOnHidden>
<Form form={dataForm} layout="vertical">
<Form.Item name="dictLabel" label="字典标签" rules={[{ required: true }]}><Input /></Form.Item>
<Form.Item name="dictValue" label="字典值" rules={[{ required: true }]}><Input /></Form.Item>
<Form.Item name="sort" label="排序" initialValue={0}><InputNumber min={0} style={{ width: '100%' }} /></Form.Item>
<Form.Item name="status" label="状态" initialValue={1}><Select options={[{ label: '正常', value: 1 }, { label: '停用', value: 0 }]} /></Form.Item>
<Form.Item name="remark" label="备注"><Input.TextArea /></Form.Item>
</Form>
</Modal>
</div>
)
}