diff --git a/src/pages/System/Role/index.tsx b/src/pages/System/Role/index.tsx index 23a9f2c..3cdfc62 100644 --- a/src/pages/System/Role/index.tsx +++ b/src/pages/System/Role/index.tsx @@ -8,6 +8,168 @@ import TextArea from 'antd/lib/input/TextArea'; import { proTableValueEnum } from '@/utils/CommonUtils'; import tableProps from '@/utils/tableProps'; +// 处理树形选择的级联逻辑 +const handleTreeCascadeCheck = ( + treeData: any[], + currentChecked: {checked: React.Key[], halfChecked: React.Key[]}, + clickedKey: React.Key, + isChecked: boolean +): {checked: React.Key[], halfChecked: React.Key[]} => { + const newChecked = new Set([...currentChecked.checked]); + const newHalfChecked = new Set([...currentChecked.halfChecked]); + + // 递归获取所有子节点ID + const getAllChildrenIds = (nodes: any[]): React.Key[] => { + const childIds: React.Key[] = []; + const traverse = (nodeList: any[]) => { + nodeList.forEach(node => { + childIds.push(node.key); + if (node.children && node.children.length > 0) { + traverse(node.children); + } + }); + }; + traverse(nodes); + return childIds; + }; + + // 查找节点及其子节点 + const findNodeAndChildren = (nodes: any[], targetKey: React.Key): {node: any, children: React.Key[]} | null => { + for (const node of nodes) { + if (node.key === targetKey) { + const children = node.children ? getAllChildrenIds(node.children) : []; + return { node, children }; + } + if (node.children) { + const result = findNodeAndChildren(node.children, targetKey); + if (result) return result; + } + } + return null; + }; + + const nodeInfo = findNodeAndChildren(treeData, clickedKey); + + if (!nodeInfo) return currentChecked; + + if (isChecked) { + // 选中节点:将节点及其所有子节点添加到checked,从halfChecked中移除 + newChecked.add(clickedKey); + newHalfChecked.delete(clickedKey); + + nodeInfo.children.forEach(childId => { + newChecked.add(childId); + newHalfChecked.delete(childId); + }); + } else { + // 取消选中节点:将节点及其所有子节点从checked和halfChecked中移除 + newChecked.delete(clickedKey); + newHalfChecked.delete(clickedKey); + + nodeInfo.children.forEach(childId => { + newChecked.delete(childId); + newHalfChecked.delete(childId); + }); + } + + // 重新计算父节点状态 + const updateParentStates = (nodes: any[]) => { + nodes.forEach(node => { + if (node.children && node.children.length > 0) { + // 先递归处理子节点 + updateParentStates(node.children); + + // 统计子节点状态 + let checkedCount = 0; + let totalCount = node.children.length; + + node.children.forEach((child: any) => { + if (newChecked.has(child.key)) { + checkedCount++; + } else if (newHalfChecked.has(child.key)) { + checkedCount += 0.5; // 半选中算0.5 + } + }); + + // 更新父节点状态 + if (checkedCount === totalCount) { + // 所有子节点都选中 → 父节点完全选中 + newChecked.add(node.key); + newHalfChecked.delete(node.key); + } else if (checkedCount > 0) { + // 部分子节点选中 → 父节点半选中 + newChecked.delete(node.key); + newHalfChecked.add(node.key); + } else { + // 无子节点选中 → 父节点未选中 + newChecked.delete(node.key); + newHalfChecked.delete(node.key); + } + } + }); + }; + + updateParentStates(treeData); + + return { + checked: Array.from(newChecked), + halfChecked: Array.from(newHalfChecked) + }; +}; + +// 计算Tree组件的checked和halfChecked状态 +const calculateTreeCheckState = (treeData: any[], selectedIds: string[]): {checked: string[], halfChecked: string[]} => { + const checkedKeys: string[] = []; + const halfCheckedKeys: string[] = []; + + // 递归处理节点,返回当前子树的选中状态 + const traverse = (nodes: any[]): {hasChecked: boolean, allChecked: boolean} => { + let hasAnyChecked = false; + let allNodesChecked = nodes.length > 0; // 初始假设所有节点都选中 + + for (const node of nodes) { + if (node.children && node.children.length > 0) { + // 父节点:递归处理子节点 + const childResult = traverse(node.children); + + if (childResult.allChecked && childResult.hasChecked) { + // 所有子节点都完全选中 → 父节点完全选中 + checkedKeys.push(node.key); + hasAnyChecked = true; + } else if (childResult.hasChecked) { + // 部分子节点选中 → 父节点半选中 + halfCheckedKeys.push(node.key); + hasAnyChecked = true; + allNodesChecked = false; + } else { + // 子节点都未选中 → 父节点未选中 + allNodesChecked = false; + } + } else { + // 叶子节点:直接检查是否在selectedIds中 + if (selectedIds.includes(node.key)) { + checkedKeys.push(node.key); + hasAnyChecked = true; + } else { + allNodesChecked = false; + } + } + } + + return { + hasChecked: hasAnyChecked, + allChecked: allNodesChecked + }; + }; + + traverse(treeData); + + return { + checked: checkedKeys, + halfChecked: halfCheckedKeys + }; +}; + const entrust: React.FC<{}> = () => { //获取字典 const getDict: any = getDicData(); @@ -16,7 +178,7 @@ const entrust: React.FC<{}> = () => { const [title, setTitle] = useState(''); const [open, setOpen] = useState(false); - const [checkedKeys, setCheckedKeys] = useState([]); + const [checkedKeys, setCheckedKeys] = useState<{checked: React.Key[], halfChecked: React.Key[]}>({checked: [], halfChecked: []}); const [currentRoleId, setCurrentRoleId] = useState(null); const dictData = JSON.parse(getDict); console.log(dictData) @@ -106,26 +268,32 @@ function createSelect(data: any) { }; // 新增菜单树加载函数 - const loadMenuTree = async () => { + const loadMenuTree = async (): Promise => { setMenuLoading(true); try { const res = await getMenuTreeAll(); if (res?.code === 200 && Array.isArray(res.data)) { - setMenuOptions(formatMenuOptions(res.data)); + const formattedOptions = formatMenuOptions(res.data); + setMenuOptions(formattedOptions); + setMenuLoading(false); + return formattedOptions; } else { setMenuOptions([]); message.error('菜单数据获取失败'); + setMenuLoading(false); + return []; } } catch (e) { setMenuOptions([]); message.error('菜单数据获取异常'); + setMenuLoading(false); + return []; } - setMenuLoading(false); }; const handleAdd = async () => { form.resetFields(); - setCheckedKeys([]); + setCheckedKeys({checked: [], halfChecked: []}); await loadMenuTree(); setOpen(true); setTitle('添加角色'); @@ -134,11 +302,15 @@ function createSelect(data: any) { const handleUpdate = async (record: any) => { form.resetFields(); const role = await getDataById(record.roleId); - await loadMenuTree(); - setCheckedKeys(role.data.menuIds || []); + const currentMenuOptions = await loadMenuTree(); + console.log("menuOptions", currentMenuOptions, role.data.menuIds) + // 计算正确的checked和halfChecked状态 + const checkState = calculateTreeCheckState(currentMenuOptions, role.data.menuIds || []); + console.log("checkState", checkState) + setCheckedKeys(checkState); form.setFieldsValue({ ...role.data, - menuIds: role.data.menuIds || [], + menuIds: [...checkState.checked, ...checkState.halfChecked], }); setCurrentRoleId(record.roleId); setOpen(true); @@ -148,7 +320,7 @@ function createSelect(data: any) { const closeModal = async () => { actionRef.current?.reload(); form.resetFields(); - setCheckedKeys([]); + setCheckedKeys({checked: [], halfChecked: []}); setOpen(false); }; @@ -181,7 +353,7 @@ function createSelect(data: any) { const checkSupModal = ( { - setCheckedKeys(checkedKeys as React.Key[]); - form.setFieldsValue({ menuIds: checkedKeys }); + onCheck={(newCheckedKeys, info) => { + console.log("onCheck - info:", info); + console.log("onCheck - newCheckedKeys:", newCheckedKeys); + + // 处理树形级联选择逻辑 + const updatedCheckState = handleTreeCascadeCheck( + menuOptions, + checkedKeys, + info.node.key, + info.checked + ); + + console.log("onCheck - updatedCheckState:", updatedCheckState); + setCheckedKeys(updatedCheckState); + const allSelected = [...updatedCheckState.checked, ...updatedCheckState.halfChecked]; + form.setFieldsValue({ menuIds: allSelected }); }} treeData={menuOptions} />