React+Ant Design設置左側菜單導航路由的顯示與隱藏(與權限無關)


最近在學習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:

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;

以上代碼實現的只是顯示和隱藏左側菜單導航的路由,如果用戶直接輸入隱藏的導航菜單地址也還是可以訪問到具體的頁面的,不過那也沒辦法,總不能刪掉隱藏的導航菜單吧,除非用戶是真的想搞事情,一般的用戶不會這么玩的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM