<script>
<Menu
defaultSelectedKeys={['/home']} //数组类型 让那个key被选中 这个值不应该写死,应该根据路径确定
defaultOpenKeys={['sub1']}
mode="inline" //菜单下拉方式 vertival
theme="dark"
inlineCollapsed={this.state.collapsed}
>
<Menu.Item key="/home">
<Link to="/home">
<Icon type="home" />
<span>首页</span>
</Link>
</Menu.Item>
<SubMenu
key="pro"
title={
<span>
<Icon type="mail" />
<span>商品</span>
</span>
}
>
<Menu.Item key="/category">
<Link to="/category">
<Icon type="folder-open" />
<span>品类管理</span>
</Link>
</Menu.Item>
<Menu.Item key="/products">
<Link to="/products">
<Icon type="filter" />
<span>商品管理</span>
</Link>
</Menu.Item>
</SubMenu>
</Menu>
</script>
每个menu都对应一个路由,所以配对的时候用路径
Menu.Item里的key就用路径来匹配 因为是唯一的
点击menu.item里的内容应该进行路由的跳转 所以用Link包裹起来
优化:不够灵活
一个菜单主要包含图标和文字和key 导航列表应该设置成为一个配置
src/config/menuConfig.js
<script>
const menuList = [
{
title: '首页', // 菜单标题名称
key: '/home', // 对应的path
icon: 'home', // 图标名称
public: true, // 公开的
},
{
title: '商品',
key: '/products',
icon: 'appstore',
children: [ // 子菜单列表
{
title: '品类管理',
key: '/category',
icon: 'bars'
},
{
title: '商品管理',
key: '/product',
icon: 'tool'
},
]
},
{
title: '用户管理',
key: '/user',
icon: 'user'
},
{
title: '角色管理',
key: '/role',
icon: 'safety',
},
{
title: '图形图表',
key: '/charts',
icon: 'area-chart',
children: [
{
title: '柱形图',
key: '/charts/bar',
icon: 'bar-chart'
},
{
title: '折线图',
key: '/charts/line',
icon: 'line-chart'
},
{
title: '饼图',
key: '/charts/pie',
icon: 'pie-chart'
},
]
},
]
export default menuList
</script>
根据menuList生成标签类型,把menu里的内容显示出来
根据数组数据生成标签数组
{menuList.map(item=>(
<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon} />
<span>{item.title}</span>
</Link>
</Menu.Item>
))}
<script>
//根据指定menu数据数组生成<Menu.Item>和SubMenu的数组
//map+函数递归
renderMenu = (menuList) => {
return menuList.map(item => {
if (!item.children) {
return (<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon} />
<span>{item.title}</span>
</Link>
</Menu.Item>
)
}
return(
<SubMenu
key={item.key}
title={
<span>
<Icon type={item.icon} />
<span>{item.title}</span>
</span>
}
>
{this.renderMenu2(item.children)}
</SubMenu>
)
})
}
render() {
return (
<div className={styles.leftNav}>
<header>
<Link to="/" >
后台系统
</Link>
</header>
<Menu
defaultSelectedKeys={['/home']}
// defaultOpenKeys={['sub1']}
mode="inline"
theme="dark"
inlineCollapsed={this.state.collapsed}
>
{this.renderMenu(menuList)}
</Menu>
</div>
);
}
}
</script/>
用reduce方法实现menu
//根据指定menu数据数组生成<Menu.Item>和SubMenu的数组
// reduce+函数递归
renderMenu2=(menuList)=>{
//求奇数和
const arr1=[1,2,3,4]
arr1.reduce((preTotal,item)=>{
// return preTotal + item //这样是所有元素的和 必须把当此统计的结果return 变为下次统计的初始值
return proTotal + (item%2===1?item:0) //返回奇数和 这个是便利回调函数:统计:必须返回当此统计的结果
},0) //0代表的初始总和
}
<script>
renderMenu2=(menuList)=>{
// //求奇数和
// const arr1=[1,2,3,4]
// arr1.reduce((preTotal,item)=>{
// // return preTotal + item //这样是所有元素的和 必须把当此统计的结果return 变为下次统计的初始值
// return proTotal + (item%2===1?item:0) //返回奇数和 这个是便利回调函数:统计:必须返回当此统计的结果
// },0) //0代表的初始总和
return menuList.reduce((pre,item)=>{
//可能向pre添加<Menu.Item></Menu.Item>
if(!item.children){
pre.push(<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon} />
<span>{item.title}</span>
</Link>
</Menu.Item>)
}else{
pre.push( <SubMenu
key={item.key}
title={
<span>
<Icon type={item.icon} />
<span>{item.title}</span>
</span>
}
>
{this.renderMenu2(item.children)}
</SubMenu>)
}
//可能是SubMenu
return pre
},[])
}
</script>
路由器有个模块组件叫withRouter 可以让组件获得路由方法
<script>
import { Link,withRouter } from 'react-router-dom'
const { SubMenu } = Menu;
class leftNav extends Component {
state = {
}
//向外暴露,使用高阶组件withRouter()来包装非路由组件
//新组件向LeftNav传递3个特别属性:history/location/match
//结果:leftNav可以操作路由相关语法了
export default withRouter(leftNav)
</script>
<Menu
defaultSelectedKeys={['/home']} //数组类型 让那个key被选中 这个值不应该写死,应该根据路径确定
//得到当前请求的路由路径:这个是路由的能力
const selectKey = this.props.location.pathname //但是this.props.location是undefied props里没有location
//这是个非路由组件,所以没有history,location 但我们需要路由里面的属性
//这时候路由组件提供了withRouter
<script>
//让子菜单能自动选中
renderMenu2=(menuList)=>{
const path = this.props.location.pathname
return menuList.reduce((pre,item)=>{
//可能向pre添加<Menu.Item></Menu.Item>
if(!item.children){
pre.push(<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon} />
<span>{item.title}</span>
</Link>
</Menu.Item>)
}else{
/*
判断当前item的key是否是我需要的openKey
查找item的所有children中cItem的key,看是否有一个跟请求的path匹配
*/
const cItem = item.children.find(cItem=>cItem.key===path)
if(cItem){
this.openKey = item.key
}
pre.push( <SubMenu
key={item.key}
title={
<span>
<Icon type={item.icon} />
<span>{item.title}</span>
</span>
}
>
{this.renderMenu2(item.children)}
</SubMenu>)
}
//可能是SubMenu
return pre
},[])
}
render() {
//因为下方的this.openKey会拿不到值,因该先把子组件渲染出来拿到openKey
const menuNode = this.renderMenu2(menuList)
//得到当前请求的路由路径:这个是路由的能力
const selectKey = this.props.location.pathname //但是this.props.location是undefied props里没有location
//这是个非路由组件,所以没有history,location 但我们需要路由里面的属性
//这时候路由组件提供了withRouter
return (
<div className={styles.leftNav}>
<header>
<Link to="/" >
后台系统
</Link>
</header>
<Menu
defaultSelectedKeys={[selectKey]} //这个值应该根据路径进行变化,而不是固定的
defaultOpenKeys={[this.openKey]} //没有这个的时候,路径为子菜单的时候,不会展开,看不到,需要请求到子路由的时候让他自动展开
mode="inline"
theme="dark"
inlineCollapsed={this.state.collapsed}
>
{menuNode}
</Menu>
</div>
</script>
优化:从login进入admin会进行两次render
理由是:login页面点击登陆后使用了this.props.history.replace进行跳转进入‘/ ’,这时候LeftNav组件进行第一次渲染,然后进入Content组件,遇到<Redirect to="/home" />又进行匹配 进行跳转
这个时候其实要进入App.js里进行匹配,App.js只有‘/login' 和 ’/ ' ,其实进行的是模糊匹配 ’/home'匹配不到login就会去匹配 '/ '
要设置成精确匹配 使用exact={true} 但是不能使用这个 只用这个 '/home‘就匹配不到
{/**/}
defaultSelectKeys:总是根据据第一次指定的key进行显示
selectedKeys:总是根据最新指定的key进行显示 ;所以用这个