最近在学习react时,用到了很流行的UI框架Ant Design,也了解了一下Ant Design Pro,发现它们都有导航组件,Ant Design框架的导航菜单在这里,Ant Design Pro是权限菜单,权限菜单简单来说就是根据登录的权限来展示不同的菜单给用户,比如管理员有给用户分配不同角色的权限,那管理员就可以看到系统管理等导航菜单,而用户A只有发布某些业务的权限,那用户A就不能看到系统管理的导航菜单等等。不过这不在我们本文的考虑范围内,有兴趣的同学可以自行去看它的API:Authorized权限

本次分享的是与用户权限无关的“React+Ant Design设置左侧菜单导航路由的显示与隐藏”。这个具体的功能如下:

1、如果当前路由没有子路由且该路由的hidden为false或不设置该路由的hidden时则直接显示该路由,若该路由的hidden为true则不显示该路由;

2、当子路由只有一个且该子路由的hidden为false或不设置该子路由的hidden时则显示其父路由和下拉的子路由;

3、当子路由只有一个且该子路由的hidden为true同时其父路由的hidden为false或不设置其父路由的hidden时则显示其父路由;

4、当当前路由有两个及两个以上子路由时,若两个子路由的hidden都为true时则该路由和其子路由全部隐藏,若但凡有一个子路由的hidden为false或不设置该路由的hidden时,则显示其父路由和该下拉的子路由。

看起来有点晕是吧,嗯,那就举一个简单的例子吧:

1、比如当前有一个列表页,用户可以查看每一条item的详情,但详情这个路由我们不希望出现在左侧菜单吧,因为详情页面我们是要靠传一些参数然后去请求接口才能显示出来的,不能让用户直接点击详情菜单就进页面去了,否则用户看到的就只能是一个空白的详情页,因此详情菜单导航是必须要隐藏起来的,用户只有点击了列表页每一条item的详情链接才能进入到详情页。

如图:



这种情况肯定是不对的,不能让用户直接看到详情的导航菜单。



这种把详情菜单给隐藏起来的,才是正确的做法。

2、再比如,既然一个导航菜单有下拉子菜单了,那么该导航菜单必定是只能点击展开或收起它的子菜单,如果它的子菜单都隐藏了,那它也就没有展示出来的必要了(注意,这里有一个特殊的情况就是如果所有的子路由都隐藏了,如果你还想显示其父路由,就如同例子1,列表页只有一个详情子路由,但是该子路由是隐藏的,那么就要展示父路由列表页了,此时可以设置为父路由的hidden为false或不设置hidden;另外一种情况就是所有子路由都隐藏了,而其父路由只是承担着展开收起的功能,此时父路由也是要隐藏掉的,那么就必须要设置父路由的hidden为true了。)。

那么接下来就说说实现吧。

首先,我用的是Ant Design的Layout的侧边布局以及自定义触发器

其次,我是把左侧菜单的配置给单独拎了出来,便于实现面包屑导航和左侧菜单的默认展开及选中。

代码如下:

左侧菜单导航配置slideBarConfig.jsx:

  1. const slideBarConfig = [
  2. {name: "列表", icon: "ordered-list", url: "/list", children: [
  3. {name: "详情", url: "/list/detail", hidden: true},
  4. ]},
  5. {name: "系统管理", icon: "appstore", url: "/system", children: [
  6. {name: "账号管理", url: "/system/accountManage"},
  7. {name: "角色管理", url: "/system/roleManage"}
  8. ]},
  9. {name: "兄弟组件传值", icon: "hdd", url: "/childToChild", hidden: true,},
  10. {name: "父组件向子组件传值", icon: "snippets", url: "/parentToChild"},
  11. {name: "子组件向父组件传值", icon: "copy", url: "/childToParent"},
  12. {name: "状态管理Redux", icon: "inbox", url: "/redux"}
  13. ];
  14. export default slideBarConfig;

处理左侧菜单导航路由的显示与隐藏的关键代码:

  1. getSubmenu = () => {
  2. return slideBarConfig.map(item => {
  3. if(!item.children || item.children.length === 0){ //如果当前路由没有子路由且该路由的hidden为false或不设置该路由的hidden时则直接显示该路由,若该路由的hidden为true则不显示该路由
  4. if(item.hidden) return false
  5. return (
  6. <MenuItem key={item.url}>
  7. <Link to={item.url} replace> {/*加一个replace是因为当前路由下的 history 不能 push 相同的路径到 stack 里。只有开发环境存在,生产环境不存在,目前还没看到官方有去掉的意思*/}
  8. <Icon type={item.icon} />
  9. <span>{item.name}</span>
  10. </Link>
  11. </MenuItem>
  12. )
  13. }else if(item.children && item.children.length === 1){
  14. if(item.hidden) return false
  15. let noHiddenRouter = [];
  16. let hiddenRouter = [];
  17. item.children.map(v => {
  18. if(v.hidden){
  19. hiddenRouter.push(v)
  20. }else{
  21. noHiddenRouter.push(v)
  22. }
  23. return true
  24. })
  25. if(hiddenRouter.length > 0){ //当子路由只有一个且该子路由的hidden为true同时其父路由的hidden为false或不设置其父路由的hidden时则显示其父路由
  26. return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem>
  27. }
  28. if(noHiddenRouter.length > 0){ //当子路由只有一个且该子路由的hidden为false或不设置该子路由的hidden时则显示其父路由和下拉的子路由
  29. return (
  30. <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}>
  31. {
  32. noHiddenRouter.map(v => {
  33. return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem>
  34. })
  35. }
  36. </SubMenu>
  37. )
  38. }
  39. }else if(item.children && item.children.length > 1){ //当当前路由有两个及两个以上子路由时,若两个子路由的hidden都为true时则该路由和其子路由全部隐藏
  40. if(item.hidden) return false
  41. let noHiddenRouter = [];
  42. item.children.map(v => {
  43. if(v.hidden){
  44. return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem>
  45. }else{
  46. noHiddenRouter.push(v)
  47. return true
  48. }
  49. })
  50. if(noHiddenRouter.length > 0){
  51. return (
  52. <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}>
  53. {
  54. noHiddenRouter.map(v => {
  55. return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem>
  56. })
  57. }
  58. </SubMenu>
  59. )
  60. }
  61. }
  62. return true
  63. });
  64. }

具体引入到layout.jsx中如下:

  1. import React, { Component } from "react";
  2. import {Layout, Menu, Icon } from 'antd'
  3. import { Link } from 'react-router-dom'
  4. import slideBarConfig from "@/layout/slideBarConfig"
  5. import Top from '@/components/header'
  6. import Contents from "@/layout/content"
  7. import Http from '@/api/sendRequestApi'
  8. import './index.css';
  9. const { Sider, Footer } = Layout
  10. const { SubMenu } = Menu;
  11. const MenuItem = Menu.Item;
  12. class Container extends Component {
  13. constructor(props){
  14. super(props)
  15. this.state = {
  16. collapsed: false,
  17. left: 200,
  18. }
  19. }
  20. toggleCollapsed = () => {
  21. let { collapsed, left } = this.state;
  22. this.setState({
  23. collapsed: !collapsed,
  24. });
  25. if(left === 200){
  26. this.setState({
  27. left: 80,
  28. });
  29. }else{
  30. this.setState({
  31. left: 200,
  32. });
  33. }
  34. }
  35. logout = () => {
  36. Http.logout().then(() => {
  37. sessionStorage.clear();
  38. this.props.history.push("/login");
  39. });
  40. }
  41. //处理左侧菜单
  42. getSubmenu = () => {
  43. return slideBarConfig.map(item => {
  44. if(!item.children || item.children.length === 0){ //如果当前路由没有子路由且该路由的hidden为false或不设置该路由的hidden时则直接显示该路由,若该路由的hidden为true则不显示该路由
  45. if(item.hidden) return false
  46. return (
  47. <MenuItem key={item.url}>
  48. <Link to={item.url} replace> {/*加一个replace是因为当前路由下的 history 不能 push 相同的路径到 stack 里。只有开发环境存在,生产环境不存在,目前还没看到官方有去掉的意思*/}
  49. <Icon type={item.icon} />
  50. <span>{item.name}</span>
  51. </Link>
  52. </MenuItem>
  53. )
  54. }else if(item.children && item.children.length === 1){
  55. if(item.hidden) return false
  56. let noHiddenRouter = [];
  57. let hiddenRouter = [];
  58. item.children.map(v => {
  59. if(v.hidden){
  60. hiddenRouter.push(v)
  61. }else{
  62. noHiddenRouter.push(v)
  63. }
  64. return true
  65. })
  66. if(hiddenRouter.length > 0){ //当子路由只有一个且该子路由的hidden为true同时其父路由的hidden为false或不设置其父路由的hidden时则显示其父路由
  67. return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem>
  68. }
  69. if(noHiddenRouter.length > 0){ //当子路由只有一个且该子路由的hidden为false或不设置该子路由的hidden时则显示其父路由和下拉的子路由
  70. return (
  71. <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}>
  72. {
  73. noHiddenRouter.map(v => {
  74. return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem>
  75. })
  76. }
  77. </SubMenu>
  78. )
  79. }
  80. }else if(item.children && item.children.length > 1){ //当当前路由有两个及两个以上子路由时,若两个子路由的hidden都为true时则该路由和其子路由全部隐藏
  81. if(item.hidden) return false
  82. let noHiddenRouter = [];
  83. item.children.map(v => {
  84. if(v.hidden){
  85. return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem>
  86. }else{
  87. noHiddenRouter.push(v)
  88. return true
  89. }
  90. })
  91. if(noHiddenRouter.length > 0){
  92. return (
  93. <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}>
  94. {
  95. noHiddenRouter.map(v => {
  96. return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem>
  97. })
  98. }
  99. </SubMenu>
  100. )
  101. }
  102. }
  103. return true
  104. });
  105. }
  106. render() {
  107. let selectedKey = this.props.location.pathname;
  108. let openKey = "";
  109. for (let menuObj of slideBarConfig) {
  110. if (menuObj.children && menuObj.children.length) {
  111. for (let menuList of menuObj.children) {
  112. if (menuList.url === selectedKey) {
  113. openKey = menuObj.url;
  114. }
  115. }
  116. }
  117. }
  118. let { collapsed, left } = this.state;
  119. return (
  120. <div id='page'>
  121. <Layout>
  122. <Sider collapsible trigger={null} collapsed={collapsed}>
  123. <Menu theme="dark" mode="inline" defaultOpenKeys={[openKey]} selectedKeys={[selectedKey]}>
  124. {this.getSubmenu()}
  125. </Menu>
  126. </Sider>
  127. <Layout className="layout-content" style={{marginLeft: left}}>
  128. <Top toggle={this.toggleCollapsed} collapsed={collapsed} logout={this.logout}/>
  129. <Contents />
  130. <Footer style={{textAlign: 'center'}}>React-Admin ©2019 Created by 小坏 <a target='_blank' href='https://github.com/zhangZhiHao1996/react-admin-master' rel="nofollow me noopener noreferrer">github地址</a></Footer>
  131. </Layout>
  132. </Layout>
  133. </div>
  134. );
  135. }
  136. }
  137. export default Container;

以上代码实现的只是显示和隐藏左侧菜单导航的路由,如果用户直接输入隐藏的导航菜单地址也还是可以访问到具体的页面的,不过那也没办法,总不能删掉隐藏的导航菜单吧,除非用户是真的想搞事情,一般的用户不会这么玩的。

React+Ant Design设置左侧菜单导航路由的显示与隐藏(与权限无关)的更多相关文章

  1. react ant design TreeNode——树形菜单笔记

     2017-12-04补充说明——树形菜单版本号2.x 设置默认该树形组件展开(默认展开所有树节点) 参考文档的写法: defaultExpandAll={true} //经过测试并不生效, 另外注意 ...

  2. React + Ant Design网页,配置

    第一个React + Ant Design网页(一.配置+编写主页) 引用博主的另外一篇VUE2.0+ElementUI教程, 请移步:  https://blog.csdn.net/u0129070 ...

  3. ant design pro (三)路由和菜单

    一.概述 参看地址:https://pro.ant.design/docs/router-and-nav-cn 二.原文摘要 路由和菜单是组织起一个应用的关键骨架,我们的脚手架提供了一些基本的工具及模 ...

  4. Ant Design Blazor 组件库的路由复用多标签页介绍

    最近,在 Ant Design Blazor 组件库中实现多标签页组件的呼声日益高涨.于是,我利用周末时间,结合 Blazor 内置路由组件实现了基于 Tabs 组件的 ReuseTabs 组件. 前 ...

  5. 【React自制全家桶】一、Webstrom+React+Ant Design+echarts搭建react项目

    前言 一.React是Facebook推出的一个前端框架,之前被用于著名的社交媒体Instagram中,后来由于取得了不错的反响,于是Facebook决定将其开源.出身名门的React也不负众望,成功 ...

  6. (二)React Ant Design Pro + .Net5 WebApi:前端环境搭建

    首先,你需要先装一个Nodejs,这是基础哦.如果没有这方面知识的小伙伴可以在园子里搜索cnpm yarn等关键字,内容繁多,此不赘述,参考链接 一. 简介 1. Ant Design Pro v5 ...

  7. django 权限设置 左侧菜单点击显示,面包屑

    1.左侧菜单点击显示 就是在点击的时候保留点击的功能 方法. 1.加入新的字段,pid来判断 class Permission(models.Model): """ 权限 ...

  8. React / Ant Design Pro 实现Canvas画布实时自适应

    如何实现canvas根据父容器进行自适应? Ant Design的组件都提供了强大的自适应能力,为了对齐父组件,镶嵌在Ant Design组件里的canvas也需要能根据父级容器进行自适应的能力,页面 ...

  9. react ant design路由配置

    最初的时候,只使用了antd中的menu,header和footer都是自己写的组件,在写路由时,总是报如下错误: 相关的路由配置如下: 在网上查的说是组件未暴露出去或者是return 这一行必须有个 ...

随机推荐

  1. “短路求值(Short-Circuit Evaluation)

        // 逻辑与和逻辑或操作符总是先计算其做操作数,只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数. function aa() { if (null) { console ...

  2. js下载后台返回的docx(返回格式:文档流)文件

    原文地址: https://www.jianshu.com/p/a81c68c15fbd PS需要指定responseType类型,不然文件内容会乱码哦 咦?文件名乱码?需要手动设置文件名哦↓ 呀,文 ...

  3. 也写dateUtil.js

    yl.dateUtil = { /** * y 年 * M 月 * d 日 * H 时 h 时(am/pm) * m 分 * s 秒 * S 毫秒 * a 上午/下午(am/pm) * setInte ...

  4. Linux配置环境变量

    自己mark一下 gedit ~/.bashrc 后面记得要 source ~/.bashrc 使之马上生效(其中波浪线 ~ 代表用户主目录,即home/XX,XX是用户的用户名) Linux下配置环 ...

  5. python中的operator.itemgetter函数

    operator.itemgetter函数operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号.看下面的例子 a = [,,] >>> b=) ...

  6. linux中gcc和g++的区别

    1.两者都是编译器 2.gcc编译c语言:g++既可以编译c语言,也可以编译c++语言 3.gcc不能自动链接库文件,一般用g++来链接库文件,非要用gcc的话,一般使用gcc -lstdc++命令 ...

  7. ubuntu 配置apt-get源

    ubantu安装软件速度慢一般是因为系统默认选择的源导致,可以通过手动配置源设置解决. 1. 原文件备份 sudo mv /etc/apt/sources.list /etc/apt/sources. ...

  8. activemq之python使用stomp协议

    参考链接: 安装:https://pypi.org/project/stomp.py/4.1.8/#history https://www.cnblogs.com/andylhc/p/9337945. ...

  9. Python——字符串2.0(实验)

    直接打s,是程序员看到的:打print(),是用户看到的 列表 ] #列表索引,与数组唯一不同:等号左端可修改

  10. 解决SecureCRT中文版"数据库里没找到防火墙'无'"的错误提示

    问题描述: 最近从同事那拷贝到一个中文版的SecureCRT,但是每次打开都会有个防火墙的错误提示,“数据库里没找到防火墙“无”.此会话将尝试不通过防火墙进行连接. 出现这个错误的原因是在Secure ...