如何根据前端传递的ids去获取到需要的菜单?
在后台管理系统的开发过程中会遇到 给定一个ids数组 去获取到ids中存在的menuList菜单
递归和选择保留的思路:
/**
* title:过滤菜单项,保留特定的菜单项及其包含的子菜单项。
* author: Joon
* date:2024-08-05
* @param {Array} menuList - 包含所有菜单项的数组,每个菜单项为一个对象。
* @param {Array} menuIds - 需要保留的菜单项的 id 列表。
* @returns {Array} 返回一个过滤后的菜单数组,仅包含指定 id 的菜单项及其有效的子菜单项。
*/
function filterMenu(menuList, menuIds) {
return menuList.reduce((filteredMenu, menuItem) => {
// 递归处理子菜单
if (menuItem.children) {
menuItem.children = filterMenu(menuItem.children, menuIds);
}
// 检查当前菜单项是否在保留列表中,或其是否有有效子菜单
if (menuIds.includes(menuItem.id) || (menuItem.children && menuItem.children.length > 0)) {
filteredMenu.push(menuItem);
}
return filteredMenu;
}, []);
}
// 示例菜单列表
const menuList = [
{
id: 1, name: "Menu 1", children: [
{
id: 2, name: "Submenu 1-1", children: [
{ id: 3, name: "Submenu 1-1-1" },
{ id: 4, name: "Submenu 1-1-2" }
]
},
{ id: 5, name: "Submenu 1-2" }
]
},
{ id: 6, name: "Menu 2" }
];
// 需要保留的菜单项 id 列表
const menuIds = [1, 2, 4, 6];
// 过滤后的菜单
const filteredMenu = filterMenu(menuList, menuIds);
/**
* 打印菜单及其子菜单的名称。
*
* @param {Array} menu - 要打印的菜单数组。
*/
function printMenu(menu) {
for (let item of menu) {
console.log(item.name);
if (item.children && item.children.length > 0) {
printMenu(item.children);
}
}
}
// 打印过滤后的菜单项及其子菜单
printMenu(filteredMenu);
打印结果:
PS C:\Users\Joon\Desktop\过滤> node .\index.js
Menu 1
Submenu 1-1
Submenu 1-1-2
Menu 2
PS C:\Users\Joon\Desktop\过滤>
食用方法:
这里以Koa为例省略了router等其他逻辑的编写 !
async getRoleAndMenu(roleId) {
// 查询角色信息
const roleQuery = 'SELECT * FROM role WHERE id = ?';
const [roleResult] = await conn.query(roleQuery, [roleId]);
if (roleResult.length === 0) {
throw new Error('角色不存在');
}
// 查询角色的权限菜单ID列表
const menuIdsQuery = `
SELECT rm.roleId, JSON_ARRAYAGG(rm.menuId) AS menuIds
FROM role_menu rm
WHERE rm.roleId = ?
GROUP BY rm.roleId;
`;
const [menuIdsResult] = await conn.query(menuIdsQuery, [roleId]);
const menuIds = menuIdsResult[0]?.menuIds || [];
// 获取所有的菜单树
const allMenus = await menuService.getMenuList();
// 根据menuIds过滤出需要的菜单树
function filterMenu(menuList, menuIds) {
return menuList.reduce((filteredMenu, menuItem) => {
if (menuItem.children) {
menuItem.children = filterMenu(menuItem.children, menuIds);
}
// 保留菜单项,如果它的ID在menuIds中,或者它的子菜单不为空
if (menuIds.includes(menuItem.id) || (menuItem.children && menuItem.children.length > 0)) {
filteredMenu.push(menuItem);
}
return filteredMenu;
}, []);
}
const filteredMenu = filterMenu(allMenus, menuIds);
// 返回角色信息及权限菜单树
return {
role: roleResult[0],
menuList: filteredMenu
};
}
解析:
该方法主要利用了递归和选择保留的思路来过滤菜单树,具体步骤如下:
- 递归处理子菜单:
- 函数
filterMenu
接受两个参数:menuList
和menuIds
。menuList
是包含所有菜单项的数组,而menuIds
是需要保留的菜单项 ID 列表。 - 对于每个菜单项
menuItem
,如果它有子菜单(children
),就递归调用filterMenu
对子菜单进行相同的过滤处理。
- 函数
- 条件判断和添加:
- 如果当前菜单项的 ID 在
menuIds
中,或者它包含有被保留的子菜单项,则将该菜单项加入到filteredMenu
中。 - 具体地说,这里有两个判断条件:
menuIds.includes(menuItem.id)
:检查当前菜单项的 ID 是否在menuIds
中。(menuItem.children && menuItem.children.length > 0)
:检查当前菜单项是否有有效的子菜单项。如果子菜单项在递归调用中返回了非空数组,意味着该菜单项或其子菜单项需要被保留。
- 如果当前菜单项的 ID 在
- 最终输出:
- 该函数最终返回一个过滤后的菜单数组
filteredMenu
,仅包含指定 ID 的菜单项及其有效的子菜单项。
- 该函数最终返回一个过滤后的菜单数组
通过这种方式,我们不仅能保留需要的菜单项,还能保留包含有这些菜单项的父菜单项。这种方法确保了在菜单结构中,只有需要的部分和必要的父级结构被保留。
示例解析
在示例代码中,我们有一个包含多层级的菜单树结构,menuList
是所有菜单项的列表,而 menuIds
是需要保留的菜单项的 ID 列表。通过 filterMenu
函数,我们能够过滤出只包含 menuIds
中指定的菜单项及其有效的父级菜单项和子菜单项的结构。
实现细节
- 递归调用:通过递归调用
filterMenu
,我们能够遍历和处理菜单树的每一个节点和子节点。 - 条件判断:判断条件确保我们只保留那些存在于
menuIds
中的菜单项,或包含这些菜单项的父级菜单项。
这个方法简洁而有效,非常适合处理层级结构的菜单数据。希望这些补充和修改能够帮助你更好地理解和传达这一方法的工作原理。