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

如何根据前端传递的ids去获取到需要的菜单?

在后台管理系统的开发过程中会遇到 给定一个ids数组 去获取到ids中存在的menuList菜单

递归选择保留的思路:

js 复制代码
/**
 * 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);

打印结果:

shell 复制代码
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等其他逻辑的编写 !

js 复制代码
  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 中的菜单项,或包含这些菜单项的父级菜单项。

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

相关推荐
无限进步_7 分钟前
C++异常机制:抛出、捕获与栈展开
开发语言·c++·安全
软件技术NINI10 分钟前
泉州html+css 4页
前端·javascript·css·html
小白学大数据11 分钟前
深度探索:Python 爬虫实现豆瓣音乐全站采集
开发语言·爬虫·python·数据分析
Xin_ye1008614 分钟前
C# 零基础到精通教程 - 第八章:面向对象编程(进阶)——继承与多态
开发语言·c#
kyriewen14 分钟前
我用AI把公司10万行代码屎山重构了,CTO看了代码后说:你提前转正
前端·javascript·ai编程
m0_7488394919 分钟前
R包grafify:简单操作实现高效统计绘图
开发语言·r语言
Evand J23 分钟前
【课题推荐与代码介绍】卡尔曼滤波器正反向估计算法原理与MATLAB实现
开发语言·算法·matlab
奋斗的小方24 分钟前
Java基础篇09:项目实战
java·开发语言
froginwe1138 分钟前
Vue.js 监听属性
开发语言
c++逐梦人39 分钟前
五种IO模型与⾮阻塞IO
开发语言·网络