基于上一篇的内容,本篇将所有功能进行了完善:
- 顶部一级导航栏和左侧二级侧边栏联动
- 通过浏览器url访问页面时 同步两个菜单的选中项
// SideBar.js
import { Menu } from 'antd'
function SideBar(props) {
return (
<Menu
onClick={props.onClick}
defaultSelectedKeys={[props.selectKey]}
defaultOpenKeys={[props.openKey]}
mode="inline"
items={props.items}
/>
);
}
export default SideBar
// MainLayout.js
import { Outlet } from 'react-router-dom'
import { Layout } from 'antd'
import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'
import { useNavigate, useLocation } from "react-router-dom"
import { useState, useEffect } from 'react'
import HeadBar from '../../components/HeadBar'
import SideBar from '../../components/SideBar'
import { headerStyle, contentStyle, siderStyle, footerStyle } from './layoutStyles.js'
const { Header, Footer, Sider, Content } = Layout
const menuList = [
{
label: 'home',
key: 'home',
icon: <MailOutlined />,
sidelist: []
},
{
label: 'about',
key: 'about',
icon: <AppstoreOutlined />,
sidelist: [
{
key: 'aboutMe',
icon: <AppstoreOutlined />,
label: 'aboutMe'
},
{
key: 'myReading',
icon: <MailOutlined />,
children: [
{
key: 'englishBooks',
icon: <AppstoreOutlined />,
label: 'englishBooks'
},
{
key: 'chineseBooks',
icon: <SettingOutlined />,
label: 'chineseBooks'
},
],
label: 'myReading'
}
]
}
]
function MainLayout() {
/**
* 整个页面的Menu跳转逻辑分两块:
* 当通过url访问页面时,menu的默认选中根据url走
* 当点击menu item 的时候,根据点击的item 跳转页面。
*
* 侧边栏的逻辑:当location发生变化时,查看当前url对应的顶部导航栏是否有侧边栏
* 如果有:
* 显示侧边栏组件 向侧边栏组件传值:defaultSelectedKeys defaultOpenKeys items onClick事件
* 如果没有:
* 隐藏侧边栏组件
*/
const navigate = useNavigate()
const location = useLocation()
const [headBarCurrent, changeHeadBarCurrent] = useState('')
const [clickChange, changeClickChange] = useState(null)
const [showSideMenu,changeShowSideMenu] = useState(false)
const [sideBarType,changeSideBarType] = useState({selectKey:'',openKey:''})
const [sideBarList,changeSideBar] = useState()
// 匹配和路径对应的key
function getKey(key='',item,path,result=[]){
item.map(e=>{
if (e.children && e.children.length) {
getKey(e.key,e.children, path, result)
} else if (path.indexOf(e.key) !== -1) {
result.push(e.key,key)
}
})
return result
}
useEffect(() => { // 当location发生变化时 需要更改菜单选中值 并判断有无侧边栏
const path = location.pathname
menuList.map(e => {
const key = e.key
if (path.indexOf(key) !== -1) { // 获取对应的顶层导航栏
changeHeadBarCurrent(key)
const side = e.sidelist
if(side.length){ // 顶层导航栏有子菜单的情况下 检索对应项
const sideMenuKey = getKey('',side,path)
changeSideBar(side) // 设置子菜单的数据
changeSideBarType({
selectKey:sideMenuKey[0],
openKey:sideMenuKey[1] ? sideMenuKey[1] : sideMenuKey[0] // 如果没有父级,说明没有子级菜单
})
console.log('=== sideBarType ===',sideBarType)
changeShowSideMenu(true) // 显示侧边栏
} else {
changeShowSideMenu(false) // 不显示侧边栏
}
}
})
}, [location])
useEffect(() => { // 在菜单点击事件发生时
if (clickChange) { // 跳转页面
navigate(clickChange)
}
}, [clickChange])
const onSideBarClick = (e) =>{
const url = '/'+headBarCurrent+'/'+e.key
changeClickChange(url) // 告知触发了菜单点击事件 需要跳转页面了
}
const onHeadBarClick = (e) => { // 当顶部导航栏触发点击的时候 需要更新两个值
changeHeadBarCurrent(e.key) // 告知菜单选中值发生了变化
const url = '/' + e.key
changeClickChange(url) // 告知触发了菜单点击事件 需要跳转页面了
}
return (
<div>
<Layout>
<Header style={headerStyle}><HeadBar menuList={menuList} current={headBarCurrent} onClick={onHeadBarClick} /></Header>
<Layout>
{showSideMenu ? <Sider style={siderStyle}><SideBar items={sideBarList} onClick={onSideBarClick} selectKey={sideBarType.selectKey} openKey={sideBarType.openKey} /></Sider> : '' }
<Layout>
<Content style={contentStyle}>
<Outlet />
</Content>
<Footer style={footerStyle}>footer</Footer>
</Layout>
</Layout>
</Layout>
</div>
)
}
export default MainLayout
// router
const router = createBrowserRouter([
{
path: "/",
element: <MainLayout />,
children: [
{
index: true, // match on parent, i.e. "/"
element: <Navigate to="/home" replace /> // redirect
},
{
path: "home",
element: <Home />,
},
{
path: "about",
element: <About />,
children:[
{
index:true,
element:<Navigate to="/about/aboutMe" replace />
},
{
path:'aboutMe',
element:<AboutMe />
},
{
path:'englishBooks',
element:<EnglishBooks />
},
{
path:'chineseBooks',
element:<ChineseBooks />
}
]
},
],
}
]);