最近在學習react時,用到了很流行的UI框架Ant Design,也瞭解了一下Ant Design Pro,發現它們都有導航組件,Ant Design框架的導航菜單在這裏,Ant Design Pro是權限菜單,權限菜單簡單來講就是根據登陸的權限來展現不一樣的菜單給用戶,好比管理員有給用戶分配不一樣角色的權限,那管理員就能夠看到系統管理等導航菜單,而用戶A只有發佈某些業務的權限,那用戶A就不能看到系統管理的導航菜單等等。不過這不在咱們本文的考慮範圍內,有興趣的同窗能夠自行去看它的API:Authorized權限。css
本次分享的是與用戶權限無關的「React+Ant Design設置左側菜單導航路由的顯示與隱藏」。這個具體的功能以下:
一、若是當前路由沒有子路由且該路由的hidden爲false或不設置該路由的hidden時則直接顯示該路由,若該路由的hidden爲true則不顯示該路由;
二、當子路由只有一個且該子路由的hidden爲false或不設置該子路由的hidden時則顯示其父路由和下拉的子路由;
三、當子路由只有一個且該子路由的hidden爲true同時其父路由的hidden爲false或不設置其父路由的hidden時則顯示其父路由;
四、噹噹前路由有兩個及兩個以上子路由時,若兩個子路由的hidden都爲true時則該路由和其子路由所有隱藏,若但凡是有一個子路由的hidden爲false或不設置該路由的hidden時,則顯示其父路由和該下拉的子路由。react
看起來有點暈是吧,嗯,那就舉一個簡單的例子吧:git
一、好比當前有一個列表頁,用戶能夠查看每一條item的詳情,但詳情這個路由咱們不但願出如今左側菜單吧,由於詳情頁面咱們是要靠傳一些參數而後去請求接口才能顯示出來的,不能讓用戶直接點擊詳情菜單就進頁面去了,不然用戶看到的就只能是一個空白的詳情頁,所以詳情菜單導航是必需要隱藏起來的,用戶只有點擊了列表頁每一條item的詳情連接才能進入到詳情頁。github
如圖:
這種狀況確定是不對的,不能讓用戶直接看到詳情的導航菜單。redux
這種把詳情菜單給隱藏起來的,纔是正確的作法。api
二、再好比,既然一個導航菜單有下拉子菜單了,那麼該導航菜單一定是隻能點擊展開或收起它的子菜單,若是它的子菜單都隱藏了,那它也就沒有展現出來的必要了(注意,這裏有一個特殊的狀況就是若是全部的子路由都隱藏了,若是你還想顯示其父路由,就如同例子1,列表頁只有一個詳情子路由,可是該子路由是隱藏的,那麼就要展現父路由列表頁了,此時能夠設置爲父路由的hidden爲false或不設置hidden;另一種狀況就是全部子路由都隱藏了,而其父路由只是承擔着展開收起的功能,此時父路由也是要隱藏掉的,那麼就必需要設置父路由的hidden爲true了。)。session
那麼接下來就說說實現吧。antd
首先,我用的是Ant Design的Layout的側邊佈局以及自定義觸發器。react-router
其次,我是把左側菜單的配置給單獨拎了出來,便於實現麪包屑導航和左側菜單的默認展開及選中。app
代碼以下:
左側菜單導航配置slideBarConfig.jsx:
const slideBarConfig = [ {name: "列表", icon: "ordered-list", url: "/list", children: [ {name: "詳情", url: "/list/detail", hidden: true}, ]}, {name: "系統管理", icon: "appstore", url: "/system", children: [ {name: "帳號管理", url: "/system/accountManage"}, {name: "角色管理", url: "/system/roleManage"} ]}, {name: "兄弟組件傳值", icon: "hdd", url: "/childToChild", hidden: true,}, {name: "父組件向子組件傳值", icon: "snippets", url: "/parentToChild"}, {name: "子組件向父組件傳值", icon: "copy", url: "/childToParent"}, {name: "狀態管理Redux", icon: "inbox", url: "/redux"} ]; export default slideBarConfig;
處理左側菜單導航路由的顯示與隱藏的關鍵代碼:
getSubmenu = () => { return slideBarConfig.map(item => { if(!item.children || item.children.length === 0){ //若是當前路由沒有子路由且該路由的hidden爲false或不設置該路由的hidden時則直接顯示該路由,若該路由的hidden爲true則不顯示該路由 if(item.hidden) return false return ( <MenuItem key={item.url}> <Link to={item.url} replace> {/*加一個replace是由於當前路由下的 history 不能 push 相同的路徑到 stack 裏。只有開發環境存在,生產環境不存在,目前還沒看到官方有去掉的意思*/} <Icon type={item.icon} /> <span>{item.name}</span> </Link> </MenuItem> ) }else if(item.children && item.children.length === 1){ if(item.hidden) return false let noHiddenRouter = []; let hiddenRouter = []; item.children.map(v => { if(v.hidden){ hiddenRouter.push(v) }else{ noHiddenRouter.push(v) } return true }) if(hiddenRouter.length > 0){ //當子路由只有一個且該子路由的hidden爲true同時其父路由的hidden爲false或不設置其父路由的hidden時則顯示其父路由 return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem> } if(noHiddenRouter.length > 0){ //當子路由只有一個且該子路由的hidden爲false或不設置該子路由的hidden時則顯示其父路由和下拉的子路由 return ( <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}> { noHiddenRouter.map(v => { return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem> }) } </SubMenu> ) } }else if(item.children && item.children.length > 1){ //噹噹前路由有兩個及兩個以上子路由時,若兩個子路由的hidden都爲true時則該路由和其子路由所有隱藏 if(item.hidden) return false let noHiddenRouter = []; item.children.map(v => { if(v.hidden){ return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem> }else{ noHiddenRouter.push(v) return true } }) if(noHiddenRouter.length > 0){ return ( <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}> { noHiddenRouter.map(v => { return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem> }) } </SubMenu> ) } } return true }); }
具體引入到layout.jsx中以下:
import React, { Component } from "react"; import {Layout, Menu, Icon } from 'antd' import { Link } from 'react-router-dom' import slideBarConfig from "@/layout/slideBarConfig" import Top from '@/components/header' import Contents from "@/layout/content" import Http from '@/api/sendRequestApi' import './index.css'; const { Sider, Footer } = Layout const { SubMenu } = Menu; const MenuItem = Menu.Item; class Container extends Component { constructor(props){ super(props) this.state = { collapsed: false, left: 200, } } toggleCollapsed = () => { let { collapsed, left } = this.state; this.setState({ collapsed: !collapsed, }); if(left === 200){ this.setState({ left: 80, }); }else{ this.setState({ left: 200, }); } } logout = () => { Http.logout().then(() => { sessionStorage.clear(); this.props.history.push("/login"); }); } //處理左側菜單 getSubmenu = () => { return slideBarConfig.map(item => { if(!item.children || item.children.length === 0){ //若是當前路由沒有子路由且該路由的hidden爲false或不設置該路由的hidden時則直接顯示該路由,若該路由的hidden爲true則不顯示該路由 if(item.hidden) return false return ( <MenuItem key={item.url}> <Link to={item.url} replace> {/*加一個replace是由於當前路由下的 history 不能 push 相同的路徑到 stack 裏。只有開發環境存在,生產環境不存在,目前還沒看到官方有去掉的意思*/} <Icon type={item.icon} /> <span>{item.name}</span> </Link> </MenuItem> ) }else if(item.children && item.children.length === 1){ if(item.hidden) return false let noHiddenRouter = []; let hiddenRouter = []; item.children.map(v => { if(v.hidden){ hiddenRouter.push(v) }else{ noHiddenRouter.push(v) } return true }) if(hiddenRouter.length > 0){ //當子路由只有一個且該子路由的hidden爲true同時其父路由的hidden爲false或不設置其父路由的hidden時則顯示其父路由 return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem> } if(noHiddenRouter.length > 0){ //當子路由只有一個且該子路由的hidden爲false或不設置該子路由的hidden時則顯示其父路由和下拉的子路由 return ( <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}> { noHiddenRouter.map(v => { return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem> }) } </SubMenu> ) } }else if(item.children && item.children.length > 1){ //噹噹前路由有兩個及兩個以上子路由時,若兩個子路由的hidden都爲true時則該路由和其子路由所有隱藏 if(item.hidden) return false let noHiddenRouter = []; item.children.map(v => { if(v.hidden){ return <MenuItem key={item.url}><Link to={item.url} replace><Icon type={item.icon} /><span>{item.name}</span></Link></MenuItem> }else{ noHiddenRouter.push(v) return true } }) if(noHiddenRouter.length > 0){ return ( <SubMenu key={item.url} title={<span><Icon type={item.icon} /><span>{item.name}</span></span>}> { noHiddenRouter.map(v => { return <MenuItem key={v.url}><Link to={v.url} replace>{v.name}</Link></MenuItem> }) } </SubMenu> ) } } return true }); } render() { let selectedKey = this.props.location.pathname; let openKey = ""; for (let menuObj of slideBarConfig) { if (menuObj.children && menuObj.children.length) { for (let menuList of menuObj.children) { if (menuList.url === selectedKey) { openKey = menuObj.url; } } } } let { collapsed, left } = this.state; return ( <div id='page'> <Layout> <Sider collapsible trigger={null} collapsed={collapsed}> <Menu theme="dark" mode="inline" defaultOpenKeys={[openKey]} selectedKeys={[selectedKey]}> {this.getSubmenu()} </Menu> </Sider> <Layout className="layout-content" style={{marginLeft: left}}> <Top toggle={this.toggleCollapsed} collapsed={collapsed} logout={this.logout}/> <Contents /> <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> </Layout> </Layout> </div> ); } } export default Container;
以上代碼實現的只是顯示和隱藏左側菜單導航的路由,若是用戶直接輸入隱藏的導航菜單地址也仍是能夠訪問到具體的頁面的,不過那也沒辦法,總不能刪掉隱藏的導航菜單吧,除非用戶是真的想搞事情,通常的用戶不會這麼玩的。