1、获取菜单对象
static findCurrentItem(items, currentState, currentItem) { _.forEach(items, function (item) { if (item.state === currentState) { currentItem.push(item); } else if (MenuService.hasChildItems(item)) { MenuService.findCurrentItem(item.childItems, currentState, currentItem); } }) } static findParentItem(items, currentItem, parentItem) { const fatherItem = _.find(items, {id: currentItem.fatherId}); if (_.isEmpty(fatherItem)) { _.forEach(items, function (item) { if (MenuService.hasChildItems(item)) { MenuService.findParentItem(item.childItems, currentItem, parentItem); } }) } else { parentItem.push(fatherItem); } } static hasChildItems(item) { return !!item.childItems && item.childItems.length > 0; } getCurrentItem(menus, currentState) { const currentItem = []; MenuService.findCurrentItem(menus, currentState, currentItem); return currentItem[0]; } getParentItem(menus, currentItem) { const parentItem = []; MenuService.findParentItem(menus, currentItem, parentItem); return parentItem[0]; }
2、获取实际页面module
getModules(menuTree) { const modules = []; _.forIn(menuTree, function (value, key) { if (!_.isEmpty(value)) { const moduleItem = {}; const moduleItems = []; _.set(moduleItem, 'type', key); MenuService.findModuleItem(moduleItems, value); _.set(moduleItem, 'state', moduleItems[0]); modules.push(moduleItem); } }); return modules; } static findModuleItem(moduleItems, menuTreeValue) { _.forEach(menuTreeValue, function (item) { if (item.state.indexOf('.') !== -1) { moduleItems.push(_.get(item, 'state')); } else if (MenuService.hasChildItems(item)) { MenuService.findModuleItem(moduleItems, item.childItems); } }) }
3、获取默认路由
getDefaultState(menuTree) { const modules = this.getModules(menuTree); if (!_.isEmpty(modules)) { return _.get(modules[0], 'state'); } }
4、获取menus
getMenu() { return new Promise((resolve, reject) => { axiosService.request({ url: '/api/menus', method: 'GET' }).then(res => { const menuTree = {}; const navigators = {}; _.forEach(res.data, function (item) { _.set(menuTree, item.state, item.childItems); _.set(navigators, item.state, item.name); }); const menu = { menuTree, navigators, defaultState: this.getDefaultState(menuTree), modules: this.getModules(menuTree) }; typeof resolve === 'function' && resolve(menu); }).catch(error => { typeof reject === 'function' && reject(error); }) }) } getSidebarMenu(menuTree, nav) { return _.result(menuTree, nav); }
menuTree: 集合,里面包含一个nav,一个对应该nav的菜单item
[{"nav01": [nav01.menus01]},{"nav02": [nav01.menus02]}];
navigators: 一个nav,一个对应nav的名字
[{"nav01": "nav01.name"},{"nav02": "nav01.name"}];
modules: 一个nav,一个对应nav的第一个路由
[{"nav01": "nav01.defaultState01"},{"nav02": "nav01.defaultState02"}];
redux menu data:
import React from "react"; import {connect} from "react-redux"; import {withRouter} from "react-router-dom"; import {Icon} from "antd"; import _ from "lodash"; import "./index.scss" const mapStateToProps = state => { return { menuData: state.MenuReducer.data } }; class Sidebar extends React.Component { constructor(props) { super(props); this.currentState = props.location.pathname; this.menus = _.result(props.menuData.menuTree, props.nav); } componentDidMount() { const defaultNavItem = this.getDefaultNavItem(); if (defaultNavItem === undefined) { this.props.history.replace('/forbidden'); return; } this.setActiveNavItem(defaultNavItem); this.openNavItem(defaultNavItem); if (this.hasChildItems(defaultNavItem)) { this.setActiveChildNavItem(defaultNavItem.childItems); } } getDefaultNavItem() { const currentState = this.currentState; return _.find(this.menus, function (navItem) { if (navItem.state === currentState || _.some(navItem.childItems, {state: currentState})) { return navItem; } }) } setActiveNavItem(navItem) { if (this.hasChildItems(navItem)) { this.clearParentActiveStatus(); } else { this.clearActiveStatusWithChildItems(); navItem.isActive = true; if (!!navItem.state) { this.props.history.replace(navItem.state); } } } setActiveChildNavItem(childNavItems) { const currentState = this.currentState; this.clearActiveStatusWithChildItems(); if (_.isArray(childNavItems)) { childNavItems.forEach(function (navItem) { navItem.isActive = navItem.state === currentState; }); } else { childNavItems.isActive = true; } } openNavItem(navItem) { navItem.isOpen = this.hasChildItems(navItem); this.forceUpdate(); } onOpenNavItem(navItem) { if (this.hasChildItems(navItem)) { navItem.isOpen = !navItem.isOpen; } else { navItem.isOpen = false; } this.forceUpdate(); } clearParentActiveStatus() { this.menus.forEach(function (navItem) { navItem.isActive = false; }) } clearActiveStatusWithChildItems() { this.menus.forEach(function (navItem) { navItem.isActive = false; navItem.childItems.forEach(function (childItem) { childItem.isActive = false; }) }) } hasChildItems(navItem) { return !!navItem.childItems && navItem.childItems.length > 0; } menuIcon(navItem) { return <Icon type={navItem.isOpen ? 'caret-down' : 'caret-right'}/> } openOrActiveClass(navItem) { const basic = "nav-item"; const openClass = navItem.isOpen ? "is-open" : ""; const activeClass = navItem.isActive ? "active" : ""; return basic + " " + openClass + " " + activeClass; } activeClass(navItem) { const basic = "child-nav-item"; const activeClass = navItem.isActive ? "active" : ""; return basic + " " + activeClass; } render() { return ( <aside className="app-sidebar"> <ul className="list-unstyled menu"> { this.menus && this.menus.map((navItem, index) => { return ( <li key={'li_' + index} className={this.openOrActiveClass(navItem)}> <span key={'span_' + index} className="item-name nav-item-content" onClick={() => { this.setActiveNavItem(navItem); this.onOpenNavItem(navItem) }}> {this.hasChildItems(navItem) ? this.menuIcon(navItem) : null} {navItem.name} </span> { navItem.isOpen ? <ul key={'subMenu_ul'} className="list-unstyled sub-menus"> { navItem.childItems.map((childItem, itemIndex) => { return ( <li key={'submenu_li_' + itemIndex} className={this.activeClass(childItem)} onClick={() => { this.setActiveChildNavItem(childItem); this.setActiveNavItem(childItem) }}> <a className="item-name">{childItem.name}</a> </li> ) }) } </ul> : null } </li> ) }) } </ul> </aside> ) } } export default withRouter(connect(mapStateToProps)(Sidebar));
scss:
@import "../../styles/varibles"; .app-sidebar { overflow: hidden; width: 180px; > ul > li { position: relative; font-size: $font-lg; border-bottom: $border; border-right: $border; border-left: $border; &:first-child { border-top: $border; border-top-right-radius: $border-radius; border-top-left-radius: $border-radius; } &:last-child { border-bottom-right-radius: $border-radius; border-bottom-left-radius: $border-radius; } } .active { color: $primary-color; font-weight: bold; border-left: 3px solid $primary-color; background-color: $item-active-bg-color; a { font-weight: bold; color: $primary-color; } } .nav-item { .item-name { margin-left: 30px; height: 50px; line-height: 50px; } .anticon { position: absolute; height: 50px; line-height: 50px; left: 7px; font-size: $font-sm; color: $title-color; } &.is-open { .anticon { color: $primary-color; } .nav-item-content { color: $title-color; font-weight: bold; } } &:hover { .anticon, .nav-item-content { color: $primary-color; } } &:active { .nav-item-content { color: $primary-color; font-weight: bold; } } .sub-menu { border-top: none; font-size: $font-sm; .item-name { height: 40px; line-height: 40px; } .child-nav-item.active { .item-name { color: $primary-color; font-weight: bold; } } } } }