在后台管理系统中,如何递归和选择保留的思路来过滤菜单

如何根据前端传递的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
    };
  }

解析:

该方法主要利用了递归选择保留的思路来过滤菜单树,具体步骤如下:

  1. 递归处理子菜单
    • 函数 filterMenu 接受两个参数:menuListmenuIdsmenuList 是包含所有菜单项的数组,而 menuIds 是需要保留的菜单项 ID 列表。
    • 对于每个菜单项 menuItem,如果它有子菜单(children),就递归调用 filterMenu 对子菜单进行相同的过滤处理。
  2. 条件判断和添加
    • 如果当前菜单项的 ID 在 menuIds 中,或者它包含有被保留的子菜单项,则将该菜单项加入到 filteredMenu 中。
    • 具体地说,这里有两个判断条件:
      1. menuIds.includes(menuItem.id):检查当前菜单项的 ID 是否在 menuIds 中。
      2. (menuItem.children && menuItem.children.length > 0):检查当前菜单项是否有有效的子菜单项。如果子菜单项在递归调用中返回了非空数组,意味着该菜单项或其子菜单项需要被保留。
  3. 最终输出
    • 该函数最终返回一个过滤后的菜单数组 filteredMenu,仅包含指定 ID 的菜单项及其有效的子菜单项。

通过这种方式,我们不仅能保留需要的菜单项,还能保留包含有这些菜单项的父菜单项。这种方法确保了在菜单结构中,只有需要的部分和必要的父级结构被保留。

示例解析

在示例代码中,我们有一个包含多层级的菜单树结构,menuList 是所有菜单项的列表,而 menuIds 是需要保留的菜单项的 ID 列表。通过 filterMenu 函数,我们能够过滤出只包含 menuIds 中指定的菜单项及其有效的父级菜单项和子菜单项的结构。

实现细节

  • 递归调用:通过递归调用 filterMenu,我们能够遍历和处理菜单树的每一个节点和子节点。
  • 条件判断:判断条件确保我们只保留那些存在于 menuIds 中的菜单项,或包含这些菜单项的父级菜单项。

这个方法简洁而有效,非常适合处理层级结构的菜单数据。希望这些补充和修改能够帮助你更好地理解和传达这一方法的工作原理。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值