import React, { useState, useImperativeHandle, forwardRef, useEffect } from 'react'; import { roleApi, RoleVO } from '../api/Role'; import { menuApi, MenuVO } from '../api/Menu'; import { toast } from 'sonner'; import { Modal, Form, Tag, Card, Switch, Tree, Spin, Space } from 'antd'; import { handleTree } from '../utils/tree'; import type { DataNode } from 'antd/es/tree'; import { useTranslation } from 'react-i18next'; interface RoleAssignMenuFormProps { onSuccess?: () => void; } export interface RoleAssignMenuFormRef { open: (row: RoleVO) => void; } const RoleAssignMenuForm = forwardRef(({ onSuccess }, ref) => { const { t } = useTranslation(); const [dialogVisible, setDialogVisible] = useState(false); const [formLoading, setFormLoading] = useState(false); const [formData, setFormData] = useState<{ id?: number; name: string; code: string; menuIds: number[]; }>({ id: undefined, name: '', code: '', menuIds: [], }); const [menuOptions, setMenuOptions] = useState([]); const [checkedKeys, setCheckedKeys] = useState([]); const [expandedKeys, setExpandedKeys] = useState([]); const [menuExpand, setMenuExpand] = useState(false); const [treeNodeAll, setTreeNodeAll] = useState(false); const [form] = Form.useForm(); // 将菜单数据转换为 Tree 组件需要的格式 const convertMenuToTreeData = (menus: MenuVO[]): DataNode[] => { return menus.map((menu) => ({ title: menu.name, key: menu.id!, children: menu.children ? convertMenuToTreeData(menu.children) : undefined, })); }; // 暴露方法给父组件 useImperativeHandle(ref, () => ({ open: async (row: RoleVO) => { setDialogVisible(true); resetForm(); // 设置数据 setFormData({ id: row.id, name: row.name, code: row.code, menuIds: [], }); // 加载菜单列表 try { const menus = await menuApi.getSimpleMenusList(); const menuData = (menus as any)?.data || menus; const treeData = handleTree(menuData || []); const treeNodes = convertMenuToTreeData(treeData); setMenuOptions(treeNodes); // 获取角色菜单权限列表 setFormLoading(true); try { const menuIds = await roleApi.getRoleMenuList(row.id!); const menuIdsData = (menuIds as any)?.data || menuIds; setFormData(prev => ({ ...prev, menuIds: menuIdsData || [] })); setCheckedKeys((menuIdsData || []).map((id: number) => id)); } catch (error: any) { console.error('获取角色菜单权限失败:', error); } finally { setFormLoading(false); } } catch (error: any) { console.error('加载菜单列表失败:', error); toast.error('加载菜单列表失败'); } }, })); // 提交表单 const submitForm = async () => { try { setFormLoading(true); // 获取所有选中的节点(包括半选中的父节点) const checked = checkedKeys as number[]; const data = { roleId: formData.id!, menuIds: checked, }; await roleApi.assignRoleMenu(data); toast.success('更新成功'); setDialogVisible(false); onSuccess?.(); } catch (error: any) { toast.error(error.message || '操作失败'); } finally { setFormLoading(false); } }; // 重置表单 const resetForm = () => { setTreeNodeAll(false); setMenuExpand(false); setFormData({ id: undefined, name: '', code: '', menuIds: [], }); setCheckedKeys([]); setExpandedKeys([]); form.resetFields(); }; // 全选/全不选 const handleCheckedTreeNodeAll = (checked: boolean) => { setTreeNodeAll(checked); if (checked) { // 获取所有节点的 key const getAllKeys = (nodes: DataNode[]): React.Key[] => { let keys: React.Key[] = []; nodes.forEach((node) => { keys.push(node.key); if (node.children) { keys = keys.concat(getAllKeys(node.children)); } }); return keys; }; setCheckedKeys(getAllKeys(menuOptions)); } else { setCheckedKeys([]); } }; // 展开/折叠全部 const handleCheckedTreeExpand = (expanded: boolean) => { setMenuExpand(expanded); if (expanded) { const getAllKeys = (nodes: DataNode[]): React.Key[] => { let keys: React.Key[] = []; nodes.forEach((node) => { keys.push(node.key); if (node.children) { keys = keys.concat(getAllKeys(node.children)); } }); return keys; }; setExpandedKeys(getAllKeys(menuOptions)); } else { setExpandedKeys([]); } }; // 树节点选中变化 const onCheck = (checked: React.Key[]) => { setCheckedKeys(checked); // 如果全部选中,设置全选状态 const getAllKeys = (nodes: DataNode[]): React.Key[] => { let keys: React.Key[] = []; nodes.forEach((node) => { keys.push(node.key); if (node.children) { keys = keys.concat(getAllKeys(node.children)); } }); return keys; }; const allKeys = getAllKeys(menuOptions); setTreeNodeAll(checked.length === allKeys.length && allKeys.length > 0); }; return ( setDialogVisible(false)} onOk={submitForm} confirmLoading={formLoading} okText={t('common.confirm')} cancelText={t('common.cancel')} width={800} destroyOnClose >
{formData.name} {formData.code} 全选/全不选: 全部展开/折叠: } style={{ maxHeight: 400, overflowY: 'auto' }} >
); }); RoleAssignMenuForm.displayName = 'RoleAssignMenuForm'; export default RoleAssignMenuForm;