<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进行显示 ;所以用这个