react style: 二级菜单

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": []
          }
        ]
      }
    }
  }
]
原文地址:https://www.cnblogs.com/Nyan-Workflow-FC/p/9332280.html