import React, { useState, useEffect } from 'react'; import { Tree, Button, Form, Input, InputNumber, Select, Switch, Space, Card, Empty, Popconfirm } from 'antd'; import { BookText, Plus, Save, Trash2, FolderTree, FileText, X } from 'lucide-react'; import apiClient from '../../utils/apiClient'; import { buildApiUrl, API_ENDPOINTS } from '../../config/api'; import Toast from '../../components/Toast'; import './DictManagement.css'; const { Option } = Select; const { TextArea } = Input; const DictManagement = () => { const [loading, setLoading] = useState(false); const [dictTypes, setDictTypes] = useState([]); // 字典类型列表 const [selectedDictType, setSelectedDictType] = useState('client_platform'); // 当前选中的字典类型 const [dictData, setDictData] = useState([]); // 当前字典类型的数据 const [treeData, setTreeData] = useState([]); // 树形结构数据 const [selectedNode, setSelectedNode] = useState(null); // 当前选中的节点 const [isEditing, setIsEditing] = useState(false); // 是否处于编辑状态 const [toasts, setToasts] = useState([]); const [form] = Form.useForm(); // Toast helper functions const showToast = (message, type = 'info') => { const id = Date.now(); setToasts(prev => [...prev, { id, message, type }]); }; const removeToast = (id) => { setToasts(prev => prev.filter(toast => toast.id !== id)); }; // 获取所有字典类型 const fetchDictTypes = async () => { try { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.DICT_DATA.TYPES)); if (response.code === '200') { setDictTypes(response.data.types); } } catch (error) { showToast('获取字典类型失败', 'error'); } }; // 获取指定类型的字典数据 const fetchDictData = async (dictType) => { setLoading(true); try { const response = await apiClient.get(buildApiUrl(API_ENDPOINTS.DICT_DATA.BY_TYPE(dictType))); if (response.code === '200') { setDictData(response.data.items); // 转换为 antd Tree 需要的格式 const antdTreeData = buildAntdTreeData(response.data.tree); setTreeData(antdTreeData); // 清空选中节点 setSelectedNode(null); setIsEditing(false); form.resetFields(); } } catch (error) { showToast('获取字典数据失败', 'error'); } finally { setLoading(false); } }; // 将树形数据转换为 antd Tree 组件格式 const buildAntdTreeData = (tree) => { return tree.map(node => ({ title: (
{node.parent_code === 'ROOT' ? : } {node.label_cn} ({node.dict_code})
), key: node.dict_code, data: node, children: node.children && node.children.length > 0 ? buildAntdTreeData(node.children) : [] })); }; useEffect(() => { fetchDictTypes(); }, []); useEffect(() => { if (selectedDictType) { fetchDictData(selectedDictType); } }, [selectedDictType]); // 选中树节点 const handleSelectNode = (selectedKeys, info) => { if (selectedKeys.length > 0) { const nodeData = info.node.data; setSelectedNode(nodeData); setIsEditing(true); // 填充表单 form.setFieldsValue({ dict_type: nodeData.dict_type, dict_code: nodeData.dict_code, parent_code: nodeData.parent_code, label_cn: nodeData.label_cn, label_en: nodeData.label_en, sort_order: nodeData.sort_order, extension_attr: nodeData.extension_attr ? JSON.stringify(nodeData.extension_attr, null, 2) : '', is_default: nodeData.is_default === 1, status: nodeData.status }); } }; // 新增节点 const handleAddNode = () => { setSelectedNode(null); setIsEditing(true); form.resetFields(); form.setFieldsValue({ dict_type: selectedDictType, parent_code: 'ROOT', sort_order: 0, status: 1, is_default: false }); }; // 保存 const handleSave = async () => { try { const values = await form.validateFields(); // 解析 extension_attr JSON if (values.extension_attr) { try { values.extension_attr = JSON.parse(values.extension_attr); } catch (e) { showToast('扩展属性 JSON 格式错误:无法解析JSON', 'error'); return; } } // 转换 is_default 为数字 values.is_default = values.is_default ? 1 : 0; if (selectedNode) { // 更新 const response = await apiClient.put( buildApiUrl(API_ENDPOINTS.DICT_DATA.UPDATE(selectedNode.id)), values ); if (response.code === '200') { showToast('更新成功', 'success'); // 重新加载数据 fetchDictData(selectedDictType); } else { showToast(response.message || '更新失败', 'error'); } } else { // 新增 const response = await apiClient.post( buildApiUrl(API_ENDPOINTS.DICT_DATA.CREATE), values ); if (response.code === '200') { showToast('创建成功', 'success'); // 重新加载数据 fetchDictData(selectedDictType); } else { showToast(response.message || '创建失败', 'error'); } } } catch (error) { if (error.errorFields) { // 表单验证错误 return; } // 显示后端返回的具体错误信息 const errorMsg = error.response?.data?.message || error.message || '操作失败'; showToast(errorMsg, 'error'); } }; // 删除 const handleDelete = async () => { if (!selectedNode) return; try { await apiClient.delete(buildApiUrl(API_ENDPOINTS.DICT_DATA.DELETE(selectedNode.id))); showToast('删除成功', 'success'); // 重新加载数据 setSelectedNode(null); setIsEditing(false); form.resetFields(); fetchDictData(selectedDictType); } catch (error) { showToast('删除失败:' + (error.message || '未知错误'), 'error'); } }; // 取消编辑 const handleCancel = () => { setIsEditing(false); setSelectedNode(null); form.resetFields(); }; // 获取父级选项(用于新增/编辑时选择父级) const getParentOptions = () => { const options = [{ label: 'ROOT(顶级)', value: 'ROOT' }]; dictData.forEach(item => { if (item.parent_code === 'ROOT') { options.push({ label: `${item.label_cn} (${item.dict_code})`, value: item.dict_code }); } }); return options; }; return (

字典管理

管理系统中的码表数据(树形结构)

{/* 左侧面板 */}
字典树
} extra={ } bordered={false} className="dict-tree-card" >
{treeData.length > 0 ? ( ) : ( )}
{/* 右侧面板 */}
{selectedNode ? '编辑字典项' : isEditing ? '新增字典项' : '字典详情'}
} extra={ isEditing && ( {selectedNode && ( )} ) } bordered={false} className="dict-form-card" > {isEditing ? (