| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- 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<RoleAssignMenuFormRef, RoleAssignMenuFormProps>(({ 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<DataNode[]>([]);
- const [checkedKeys, setCheckedKeys] = useState<React.Key[]>([]);
- const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
- 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 (
- <Modal
- title="菜单权限"
- open={dialogVisible}
- onCancel={() => setDialogVisible(false)}
- onOk={submitForm}
- confirmLoading={formLoading}
- okText={t('common.confirm')}
- cancelText={t('common.cancel')}
- width={800}
- destroyOnClose
- >
- <Spin spinning={formLoading}>
- <Form
- form={form}
- layout="horizontal"
- labelCol={{ span: 4 }}
- wrapperCol={{ span: 20 }}
- >
- <Form.Item label="角色名称">
- <Tag>{formData.name}</Tag>
- </Form.Item>
- <Form.Item label="角色标识">
- <Tag>{formData.code}</Tag>
- </Form.Item>
- <Form.Item label="菜单权限">
- <Card
- title={
- <Space>
- <span>全选/全不选:</span>
- <Switch
- checked={treeNodeAll}
- onChange={handleCheckedTreeNodeAll}
- checkedChildren="是"
- unCheckedChildren="否"
- />
- <span style={{ marginLeft: 16 }}>全部展开/折叠:</span>
- <Switch
- checked={menuExpand}
- onChange={handleCheckedTreeExpand}
- checkedChildren="展开"
- unCheckedChildren="折叠"
- />
- </Space>
- }
- style={{ maxHeight: 400, overflowY: 'auto' }}
- >
- <Tree
- checkable
- checkedKeys={checkedKeys}
- expandedKeys={expandedKeys}
- onCheck={onCheck}
- onExpand={setExpandedKeys}
- treeData={menuOptions}
- defaultExpandAll={false}
- />
- </Card>
- </Form.Item>
- </Form>
- </Spin>
- </Modal>
- );
- });
- RoleAssignMenuForm.displayName = 'RoleAssignMenuForm';
- export default RoleAssignMenuForm;
|