1、樣式
@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 { border-left: 3px solid $primary-color; background-color: $item-active-bg-color; a { font-weight: bold; } } .nav-item { .item-name { margin-right: 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; } } } }
2、js文件
import React from "react"; import {withRouter} from "react-router-dom"; import {Icon} from "antd"; import _ from "lodash"; const menus = []; const currentState = ''; class Sidebar extends React.Component { 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 = currentState; return _.find(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 = 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() { menus.forEach(function (navItem) { navItem.isActive = false; }) } clearActiveStatusWithChildItems() { 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"> { 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(Sidebar);
3、數據
[ { "description": "userCanGetMenus", "request": { "method": "GET", "uri": "/api/menus" }, "response": { "status": 200, "json": { "id": "DEMO0000", "fatherId": "00000000", "state": "process", "name": "運營流程", "childItems": [ { "id": "DEMO1000", "fatherId": "DEMO0000", "state": "/process.personal-task-pool", "name": "個人任務池", "childItems": [] }, { "id": "DEMO2000", "fatherId": "DEMO0000", "state": "/process.common-task-pool", "name": "公共任務池", "childItems": [] }, { "id": "DEMO1000", "fatherId": "DEMO0000", "state": "/process.launch", "name": "流程發起", "childItems": [] }, { "id": "DEMO1000", "fatherId": "DEMO0000", "state": "/process.search", "name": "流程查詢", "childItems": [] } ] } } } ]