# 菜单权限管理页面修复报告 ## 问题描述 用户反映:**菜单权限管理**功能页面只需要对于菜单的隐藏管理,不需要按钮的管理。按钮的管理是操作权限的内容。 从图片可以看出,当前的"菜单权限管理"页面确实包含了按钮权限的管理,包括: - "创建装车订单"、"编辑"、"删除"、"装车"等操作按钮 - 提示文字写着"勾选菜单和按钮后" - 这些按钮权限的复选框可以被勾选或取消勾选 ## 问题根本原因 ### 1. 权限类型混淆 当前的"菜单权限管理"页面将菜单权限和按钮权限混淆了: - **菜单权限**:应该只包含菜单项(type=0,1),用于控制左侧菜单栏的显示/隐藏 - **按钮权限**:应该包含操作按钮(type=2),用于控制页面内按钮的显示/隐藏 ### 2. 页面功能不明确 **文件**:`pc-cattle-transportation/src/views/permission/menuPermission.vue` **问题**: - 第77行提示文字:"勾选菜单和按钮后,该用户登录系统时可以访问这些菜单页面和执行相应的操作" - 第105-111行显示按钮标签:"按钮" - 第205-220行 `loadMenuTree` 方法加载所有菜单和按钮 - 第223-238行 `loadRoleMenus` 方法加载所有权限(包括按钮) ## 修复方案 ### 1. 修改提示文字 **修改前**: ```html 勾选菜单和按钮后,该用户登录系统时可以访问这些菜单页面和执行相应的操作 ``` **修改后**: ```html 勾选菜单后,该用户登录系统时可以访问这些菜单页面。按钮权限请在"操作权限管理"页面中设置。 ``` ### 2. 过滤菜单树,只显示菜单项 **修改文件**:`pc-cattle-transportation/src/views/permission/menuPermission.vue` **修改内容**: ```javascript // 加载菜单树(只显示菜单,不显示按钮) const loadMenuTree = async () => { permissionLoading.value = true; try { const res = await getMenuTree(); if (res.code === 200) { // 过滤掉按钮权限(type=2),只保留菜单(type=0,1) const filteredTree = filterMenuTree(res.data || []); menuTree.value = filteredTree; console.log('=== 菜单权限管理 - 过滤后的菜单树 ===', filteredTree); } } catch (error) { console.error('加载菜单树失败:', error); ElMessage.error('加载菜单树失败'); } finally { permissionLoading.value = false; } }; // 过滤菜单树,只保留菜单项(type=0,1),移除按钮(type=2) const filterMenuTree = (tree) => { return tree.map(node => { const filteredNode = { ...node }; // 如果当前节点是按钮(type=2),返回null(会被过滤掉) if (node.type === 2) { return null; } // 如果有子节点,递归过滤 if (node.children && node.children.length > 0) { const filteredChildren = filterMenuTree(node.children).filter(child => child !== null); filteredNode.children = filteredChildren; } return filteredNode; }).filter(node => node !== null); }; ``` ### 3. 过滤菜单权限,只加载菜单权限 **修改内容**: ```javascript // 加载角色已分配的菜单(只加载菜单权限,不加载按钮权限) const loadRoleMenus = async (roleId) => { try { const res = await getRoleMenuIds(roleId); if (res.code === 200) { const allMenuIds = res.data || []; // 过滤掉按钮权限,只保留菜单权限 const menuOnlyIds = await filterMenuOnlyIds(allMenuIds); checkedMenuIds.value = menuOnlyIds; console.log('=== 菜单权限管理 - 过滤后的菜单权限 ===', { allMenuIds: allMenuIds, menuOnlyIds: menuOnlyIds }); await nextTick(); if (menuTreeRef.value) { menuTreeRef.value.setCheckedKeys(checkedMenuIds.value); } } } catch (error) { console.error('加载角色菜单失败:', error); ElMessage.error('加载角色菜单失败'); } }; // 过滤菜单ID列表,只保留菜单项(type=0,1),移除按钮(type=2) const filterMenuOnlyIds = async (menuIds) => { try { // 获取所有菜单信息 const menuListRes = await getMenuList(); if (menuListRes.code !== 200) { return menuIds; // 如果获取失败,返回原始列表 } const allMenus = menuListRes.data || []; const menuMap = new Map(allMenus.map(menu => [menu.id, menu])); // 过滤掉按钮权限 const menuOnlyIds = menuIds.filter(id => { const menu = menuMap.get(id); return menu && menu.type !== 2; // 只保留菜单项(type=0,1) }); return menuOnlyIds; } catch (error) { console.error('过滤菜单权限失败:', error); return menuIds; // 如果过滤失败,返回原始列表 } }; ``` ### 4. 修改保存逻辑,只保存菜单权限 **修改内容**: ```javascript // 保存菜单权限(只保存菜单权限,不保存按钮权限) const handleSaveMenuPermissions = async () => { if (!currentRole.value) { ElMessage.warning('请先选择用户'); return; } // 获取选中的节点(包括半选中的父节点) const checkedKeys = menuTreeRef.value.getCheckedKeys(); const halfCheckedKeys = menuTreeRef.value.getHalfCheckedKeys(); const allKeys = [...checkedKeys, ...halfCheckedKeys]; // 过滤掉按钮权限,只保留菜单权限 const menuOnlyIds = await filterMenuOnlyIds(allKeys); console.log('=== 保存菜单权限 ===', { user: currentRole.value, allKeys: allKeys, menuOnlyIds: menuOnlyIds }); saveLoading.value = true; try { const res = await assignRoleMenus({ roleId: currentRole.value.roleId, menuIds: menuOnlyIds, // 只保存菜单权限 }); if (res.code === 200) { ElMessage.success(`菜单权限保存成功,共保存 ${menuOnlyIds.length} 个菜单权限`); } else { ElMessage.error(res.msg || '保存失败'); } } catch (error) { console.error('保存菜单权限失败:', error); ElMessage.error('保存失败'); } finally { saveLoading.value = false; } }; ``` ### 5. 修改一键分配功能,只分配菜单权限 **修改内容**: ```javascript // 一键分配全部菜单权限(只分配菜单权限,不分配按钮权限) const handleQuickAssignAll = async () => { // ... 确认对话框修改为只分配菜单权限 // 获取所有菜单 const menuListRes = await getMenuList(); if (menuListRes.code !== 200) { throw new Error('获取菜单列表失败'); } const allMenus = menuListRes.data || []; // 过滤掉按钮权限,只保留菜单权限 const menuOnlyMenus = allMenus.filter(menu => menu.type !== 2); const menuOnlyIds = menuOnlyMenus.map(menu => menu.id); console.log('=== 一键分配全部菜单权限 ===', { user: currentRole.value, totalMenus: allMenus.length, menuOnlyMenus: menuOnlyMenus.length, menuOnlyIds: menuOnlyIds }); // 分配所有菜单权限 const res = await assignRoleMenus({ roleId: currentRole.value.roleId, menuIds: menuOnlyIds, }); if (res.code === 200) { ElMessage.success(`成功为用户 ${currentRole.value.name} 分配了 ${menuOnlyIds.length} 个菜单权限`); // 重新加载权限显示 await loadRoleMenus(currentRole.value.roleId); } else { ElMessage.error(res.msg || '分配失败'); } }; ``` ### 6. 修改按钮文字 **修改内容**: ```html 一键分配全部权限 一键分配全部菜单权限 ``` ## 修复效果 ### 修复前 - ❌ 菜单权限管理页面包含按钮权限 - ❌ 用户可以勾选/取消勾选按钮权限 - ❌ 提示文字混淆了菜单和按钮权限 - ❌ 保存时会保存按钮权限 ### 修复后 - ✅ 菜单权限管理页面只显示菜单项 - ✅ 用户只能管理菜单权限,不能管理按钮权限 - ✅ 提示文字明确说明菜单权限和按钮权限的区别 - ✅ 保存时只保存菜单权限 - ✅ 按钮权限管理在"操作权限管理"页面中 ## 权限分离逻辑 ### 菜单权限管理页面 ``` 用户选择 → 加载菜单树(过滤掉按钮) → 显示菜单项 → 保存菜单权限 ``` ### 操作权限管理页面 ``` 用户选择 → 加载权限树(包含按钮) → 显示操作按钮 → 保存操作权限 ``` ## 权限类型说明 ### 菜单类型(type字段) - **type=0**:目录(如"系统管理") - **type=1**:菜单(如"装车订单") - **type=2**:按钮(如"编辑"、"删除") ### 权限管理范围 - **菜单权限管理**:只管理 type=0,1 的项目 - **操作权限管理**:管理所有类型(type=0,1,2)的项目 ## 测试验证 ### 测试步骤 1. **清除浏览器缓存** 2. **访问"菜单权限管理"页面** 3. **选择用户,检查权限树** 4. **验证只显示菜单项,不显示按钮** 5. **保存权限,验证只保存菜单权限** ### 预期结果 - **菜单权限管理页面**:只显示菜单项(type=0,1),不显示按钮(type=2) - **操作权限管理页面**:显示所有权限(包括按钮) - **权限分离**:菜单权限和按钮权限独立管理 ## 相关文件 - `pc-cattle-transportation/src/views/permission/menuPermission.vue` - 菜单权限管理页面 - `pc-cattle-transportation/src/views/permission/operationPermission.vue` - 操作权限管理页面 ## 总结 通过过滤菜单树和权限数据,成功将菜单权限管理页面与按钮权限管理分离。修复后的系统能够: 1. **明确权限范围**:菜单权限管理只管理菜单项 2. **清晰的功能分工**:菜单权限和按钮权限独立管理 3. **用户友好的提示**:明确说明权限管理的范围 4. **数据一致性**:确保只保存相应的权限类型 **修复状态**:✅ 已完成 **测试状态**:⏳ 待验证 **部署状态**:✅ 已部署