代码先锋网 代码片段及技术文章聚合

基于antd的Menu组件实现菜单查询

技术标签: antd  菜单查询  Menu组件  react

最近因项目需要对antd开发的项目实现菜单可筛选的功能,根据输入值,模糊查询所匹配的菜单并显示,因项目对国际化有要求,但项目中所有国际化资源文件均是以文件的形式提供,数据库仅存有国际化的key值,因此只能在前端对国际化后的菜单集合做的菜单做过滤,使用递归方式实现,需求描述如下。

1. 找出所有非叶子节点匹配的菜单项,将其子、子子节点直接显示
2. 找出所有叶子节点匹配项,将其父、祖父等祖籍节点直接显示

方式一,对传入后的值做筛选处理,修改getNavMenuItems及相关函数

//关键代码:
 /**
   * 获得菜单子节点
   * @memberof SiderMenu
   */
  getNavMenuItems = (menusData, parent) => {
    if (!menusData || (Object.prototype.toString.call(menusData) != '[object Array]')) {
      return [];
    }
    return menusData
      .filter(item => item.name && !item.hideInMenu)
      .map(item => {
        // make dom
        const str = "设备";
        if(str == undefined || str.length < 1) { //查询为空时,全部显示
          const ItemDom = this.getSubMenuOrItem2(item);
          return this.checkPermissionItem(item.authority, ItemDom);
        } else {
          const parentList = this.checkParentMenu(item, str)
          const list = parentList.flat(Infinity);
          if(list != null && list.length > 0) {  //祖籍节点匹配,该祖籍节点下子节点全部显示
            const ItemDom = this.getSubMenuOrItem2(item);
            return this.checkPermissionItem(item.authority, ItemDom);
          } else {  //子节点匹配,祖籍节点全部显示
            const ItemDom = this.getSubMenuOrItem(item, str);
            return this.checkPermissionItem(item.authority, ItemDom);
          }
        }
      })
      .filter(item => item);
  };
  
  /**
   * 检测子菜单是否含有筛选关键字
   */
  checkSubMenu = (children, str) => {
    let list = [];
      children.filter(item => item.name && !item.hideInMenu)
      .map(item => {
        if (item.children != undefined && item.children.length > 0 && !item.hideInMenu) {
          list.push(this.checkSubMenu(item.children, str));
        } else {
          const name = formatMessage({ id: item.locale });
          if (name.indexOf(str) != -1) {
            list.push(true);
          }
        }
      })
      return list;
  }
  
  /**
   * 菜单打平,将树形菜单列表化
   */
  flatMapMenus = (menuData) => {
    let list = [];
    menuData.filter(item => item.name && !item.hideInMenu)
      .map(item => {
        list.push(item);
        if (item.children != undefined && item.children.length > 0 && !item.hideInMenu) {
          list.push(this.flatMapMenus(item.children));
        } 
      })
      return list;
  }

  /**
   * 检测父菜单是否含有筛选关键字
   */
  checkParentMenu = (item, str) => {
    let list = [];
    if(item != null && item.name && !item.hideInMenu) {
      const name = formatMessage({ id: item.locale });
        if (name.indexOf(str) != -1) {
          list.push(true);
        } else {
          if(item.parent != null) {
            const name = formatMessage({ id: item.parent.locale });
            if (name.indexOf(str) != -1) {
              list.push(true);
            }
            const { menuData } = this.props;
            const menu = this.flatMapMenus(menuData).flat(Infinity).filter(menu => menu.id == item.parent.id);
            list.push(this.checkParentMenu(menu.length > 0 ? menu[0] : null, str));
          } 
        }
    }
    return list;
  }

  //菜单全部显示
  getSubMenuOrItem2 = item => {
    if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) {
      const name = formatMessage({ id: item.locale });
      return (
        <SubMenu
          title={
            item.icon ? (
              <span>
                {getIcon(item.icon)}
                <span>{name}</span>
              </span>
            ) : (
                name
              )
          }
          key={item.path}
        >
          {this.getNavMenuItems(item.children)}
        </SubMenu>
      );
    }
    return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
  };

  /**
   * 检测子菜单匹配,显示祖籍菜单、匹配的子菜单
   */
  getSubMenuOrItem = (item, str) => {
    if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) {
      const checkList = this.checkSubMenu(item.children, str);
      const flatList = checkList.flat(Infinity);
      const list = flatList.map(flag => flag == true ? true : false).filter(flag => flag);
      const name = formatMessage({ id: item.locale });
      if (list != undefined && list.length > 0) {
        return (
          <SubMenu
            title={
              item.icon ? (
                <span>
                  {getIcon(item.icon)}
                  <span>{name}</span>
                </span>
              ) : (
                  name
                )
            }
            key={item.path}
          >
            {this.getNavMenuItems(item.children)}
          </SubMenu>
        );
      } else {

      }
    }
    if (formatMessage({ id: item.locale }).indexOf(str) != -1)  {
      return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>;
    }
  };

  方式二,对传入前的值做筛选(推荐):
  1. 对传入的树形菜单集合做筛选,筛选出所有满足的节点,直接压入集合并返回,代码如下:

  searchMenus = (menus, str) => {
    if(str == undefined || str.length < 1){
      return menus;
    }
    let list = [];
    menus
    .filter(item => item.name && !item.hideInMenu)
    .map(item => {
      if(formatMessage({ id: item.locale }).indexOf(str) != -1) {
        list.push(item);
      } else {
        if(item.children && item.children.length) {
          const child = this.searchMenus(item.children, str);
          const node = {
            ...item,
            children: child
          };
          if (child && child.length) {
            list.push(node);
          }
        }
      }
    })
    return list;
  }

render方法渲染:

render() {
    const { openKeys, theme, mode, location: { pathname,hash }, openUrls } = this.props;
    let selectedKeys = this.getSelectedMenuKeys();
    if (this.isEmpty(selectedKeys) && openKeys) {
      selectedKeys = [hash===''?pathname:pathname+hash];
    }
    let props = {};
    if (openKeys) {
      props = {
        openKeys: openUrls,
      };
    }

    const { handleOpenChange, style, menuData } = this.props;
    const list = this.searchMenus(menuData, "管理");
    console.log(list);
    return (
      <Menu
        key="Menu"
        mode={mode}
        theme={theme}
        onOpenChange={handleOpenChange}
        selectedKeys={selectedKeys}
        style={style}
        {...props}
      >
        {this.getNavMenuItems(list)} //方式二直接将传入的值menuData,改为筛选后的list
      </Menu>
    );
  }

  总结:两种方式都可以实现基于antd的菜单查询功能,推荐使用第二种方式,区别参考代码

版权声明:本文为bin_zi_123原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/bin_zi_123/article/details/99449446

智能推荐

菜单的使用Menu

这里菜单的简单使用: 在res目录下新建menu文件夹(res-new-directory)menu,在menu下新建mian文件(new-menu resource file) main.xml 重写onCreateOptionsMenu()方法和onCreateOptionsMenu()方法...

关于React 使用antd组件递归实现左侧菜单导航树(MenusTree)的示例

一、菜单组件Demo 这里本人采用的是蚂蚁金服(antd)组件库里的{Menu}组件写的一个左侧菜单树的小Demo(整套开发环境是React+Redux+webpack)...

级联菜单的递归实现Antd

map()+递归调用 reduce()+递归调用...

猜你喜欢

基于antd DatePicker的年份组件

最近在开发的时候,发现antd的datepicker组件,设置mode="year"时候,年份组件交互上有一些bug问题,后来自己基于antd上的picker,进行了改装,能进行年份选择的效果 组件代码如下: 调用方式: onchange回调方法: 这里使用的方法是通过组件外部的props的value来控制年份所选的值 (由于业务原因) 其实在yearpicker内部 通过内部...

基于antd封装的固话组件

最近基于Ant Design Pro框架,着手一个项目的研发,在一个业务模块中,需要用户输入座机号,最开始,就在Form中放一个Input,让用户输入,但是这样就碰到一个问题:如何保证用户的输入是相同的,数据落库如何保持一致? 业务场景 在一个表单中,用户需要输入座机号,至少包含区号和座机号,分机号为非必填字段。 问题分析 进过对业务场景的分析,在一个输入框中,通过正则来判断座机号,似乎有点麻烦,...

React 在函数式组件中使用antd的Menu组件时默认展开的问题

React 在函数式组件中使用antd的Menu组件时默认展开的问题 文章转载自这里 期望效果: 我想在更改/更新defaultOpenKeys值时打开(展开)特定菜单的子菜单。 我所尝试的: const [submenu, setSubmenu] = useState([]); useEffect(() => { setSubmenu([“sub1”]); }, [...

Menu实现菜单选择

创建menu: 右键res>>New>>Directory,输入文件名menu,然后右键menu文件夹>>New>>Menu resource file然后输入文件名即可. 实现过程: (1)布局文件.xml增加菜单项 例:增加一个名为function的菜单选项 (2)加载布局.java 重写onCreateOptionsMenu()方法(ctrl+...

Python 脚本实现 Menu 菜单

本文转至 余子越的博客 ,文章 Python 脚本实现 Menu 菜单,欢迎访问yuchaoshui.com 了解更多信息! 一、说明 二、代码 一、说明    在操作系统上执行某些脚本时,会有一些 menu 选择菜单, 如果用 Python 来实现,可以尝试用下面的思路试试。 二、代码 本文转至 余子越的博客 ,文章 Python 脚本实现 Menu 菜单,欢迎访问yucha...