474 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			474 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import React, { useState, useEffect, useMemo, useRef } from "react";
 | 
						||
import { connect } from "dva";
 | 
						||
import { withRouter, matchPath } from "dva/router";
 | 
						||
import { Scrollbars } from "react-custom-scrollbars";
 | 
						||
import { Icon } from "antd";
 | 
						||
import EnergyIcon from "../utils/energyIcon";
 | 
						||
import { $consts } from "../plugins";
 | 
						||
import "./sider1.less";
 | 
						||
 | 
						||
const Sider = (props) => {
 | 
						||
  const [activeKeepMenu, setActiveKeepMenu] = useState(null);
 | 
						||
  const [shortMenuShow, setShortMenuShow] = useState(true);
 | 
						||
  const [expandMenuShow, setExpandMenuShow] = useState(true);
 | 
						||
 | 
						||
  /** 菜单页展开与收起 */
 | 
						||
  const timer = useRef(-1);
 | 
						||
  const handleExpandMenuShow = () => {
 | 
						||
    clearTimeout(timer.current);
 | 
						||
    setExpandMenuShow(true);
 | 
						||
  };
 | 
						||
  const handleExpandMenuHide = () => {
 | 
						||
    clearTimeout(timer.current);
 | 
						||
    timer.current = setTimeout(() => {
 | 
						||
      setExpandMenuShow(false);
 | 
						||
    }, 200);
 | 
						||
  };
 | 
						||
 | 
						||
  /** 控制三级菜单收缩和展开 */
 | 
						||
  const [menuShrink, setMenuShrink] = useState({});
 | 
						||
  const handleMenuShrink = (menuId) => {
 | 
						||
    const data = Object.assign({}, menuShrink);
 | 
						||
    data[menuId] ? (data[menuId] = "") : (data[menuId] = menuId);
 | 
						||
    setMenuShrink(data);
 | 
						||
  };
 | 
						||
 | 
						||
  /** 回到首页 */
 | 
						||
  const navToHome = () => {
 | 
						||
    setActiveMenu(null);
 | 
						||
    props.history.push({ pathname: "/home" });
 | 
						||
  };
 | 
						||
 | 
						||
  /** 子菜单路由 */
 | 
						||
  const navToMenu = (menu) => {
 | 
						||
    props.history.push({ pathname: `/main/${menu.ID}` });
 | 
						||
  };
 | 
						||
 | 
						||
  /** 后台设置:四级菜单 */
 | 
						||
  const navToBackend = (menu) => {
 | 
						||
    props.history.push({ pathname: `/backend/${menu.ID}` });
 | 
						||
  };
 | 
						||
 | 
						||
  /** 一级菜单显示菜单 */
 | 
						||
  const [currMenu, setCurrMenu] = useState(null);
 | 
						||
  useEffect(() => {
 | 
						||
    const menus = props.login.loginInfo?.Menus || [];
 | 
						||
    menus.length && !currMenu && setCurrMenu(menus[0].Node);
 | 
						||
  }, [props.login.loginInfo?.Menus]);
 | 
						||
 | 
						||
  /** 已激活菜单 */
 | 
						||
  const [activeMenu, setActiveMenu] = useState(null);
 | 
						||
  const handleActiveMenu = (menu) => {
 | 
						||
    setActiveMenu(menu);
 | 
						||
    navToMenu(menu);
 | 
						||
  };
 | 
						||
 | 
						||
  /** 页面刷新时候,更新菜单的选中状态 */
 | 
						||
  useEffect(() => {
 | 
						||
    const { pathname } = props.location;
 | 
						||
    const mathHome = matchPath(pathname, {
 | 
						||
      path: $consts["ROUTE/HOME"],
 | 
						||
      exact: true,
 | 
						||
      strict: true,
 | 
						||
    });
 | 
						||
    if (mathHome) {
 | 
						||
      activeMenu && setActiveMenu(null);
 | 
						||
    }
 | 
						||
    const mathMain = matchPath(pathname, {
 | 
						||
      path: $consts["ROUTE/MAIN"],
 | 
						||
      exact: true,
 | 
						||
      strict: true,
 | 
						||
    });
 | 
						||
    if (mathMain) {
 | 
						||
      const { flatMenus } = props.login;
 | 
						||
      const { menuId } = mathMain.params;
 | 
						||
      const find = flatMenus.find((item) => item.ID === menuId);
 | 
						||
      if (find) {
 | 
						||
        props.dispatch({
 | 
						||
          type: "app/updateActivatedMenu",
 | 
						||
          payload: {
 | 
						||
            currActivatedTab: find.ID,
 | 
						||
            currActivatedMenu: find,
 | 
						||
          },
 | 
						||
        });
 | 
						||
        !activeMenu && setActiveMenu(find);
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }, [props.location.pathname, props.login.flatMenus, activeMenu]);
 | 
						||
 | 
						||
  /** 常用菜单 */
 | 
						||
  const favorMenus = useMemo(() => {
 | 
						||
    const { flatMenus } = props.login;
 | 
						||
    return flatMenus.filter((menu) => menu.IS_RESIDENT);
 | 
						||
  }, [props.login.flatMenus]);
 | 
						||
 | 
						||
  /** 一级菜单 */
 | 
						||
  const topMenus = useMemo(() => {
 | 
						||
    const menus = props.login.loginInfo?.Menus || [];
 | 
						||
    // 菜单宽度 82 + margin 40
 | 
						||
    const menuWidth = menus.length
 | 
						||
      ? menus.length * 82 + (menus.length - 1) * 40
 | 
						||
      : 0;
 | 
						||
    // 叶子菜单需要换行展示个数
 | 
						||
    const leafMenuSections =
 | 
						||
      menus.length < 3 ? 1 : menus.length === 3 ? 2 : menus.length - 2;
 | 
						||
    return {
 | 
						||
      menus,
 | 
						||
      width: menuWidth < 120 ? 120 : menuWidth,
 | 
						||
      leafMenuSections,
 | 
						||
    };
 | 
						||
  }, [props.login.loginInfo?.Menus]);
 | 
						||
 | 
						||
  /** 二三级子菜单 */
 | 
						||
  const subMenus = useMemo(() => {
 | 
						||
    const { menus } = topMenus;
 | 
						||
    const find = menus.find((menu) => menu.Node.ID === currMenu?.ID);
 | 
						||
    return find?.Children || [];
 | 
						||
  }, [currMenu, topMenus]);
 | 
						||
 | 
						||
  /** 三级子菜单默认收缩与展开 */
 | 
						||
  const { IS_MENU_SHRINK = false } = props.login.baseConfig;
 | 
						||
  useEffect(() => {
 | 
						||
    // IS_MENU_SHRINK为true时, 收缩所有三级菜单
 | 
						||
    if (IS_MENU_SHRINK) {
 | 
						||
      const { menus } = topMenus;
 | 
						||
      const data = {};
 | 
						||
      menus.forEach(
 | 
						||
        (menu) =>
 | 
						||
          menu.Children &&
 | 
						||
          menu.Children.forEach(
 | 
						||
            (child) => (data[child.Node.ID] = child.Node.ID)
 | 
						||
          )
 | 
						||
      );
 | 
						||
      setMenuShrink(data);
 | 
						||
    }
 | 
						||
  }, [IS_MENU_SHRINK]);
 | 
						||
 | 
						||
  /** 登录页隐藏 */
 | 
						||
  if (props.matchLogin) return null;
 | 
						||
 | 
						||
  return (
 | 
						||
    <div
 | 
						||
      //   onMouseEnter={() => setShortMenuShow(true)}
 | 
						||
      //   onMouseLeave={() => setShortMenuShow(false)}
 | 
						||
      className={`sider ${shortMenuShow ? "shortMenuShow" : ""}`}
 | 
						||
    >
 | 
						||
      {/** 所有菜单 */}
 | 
						||
      <div
 | 
						||
        // onMouseEnter={handleExpandMenuShow}
 | 
						||
        // onMouseLeave={handleExpandMenuHide}
 | 
						||
        onClick={navToHome}
 | 
						||
        className="sider__menuAll"
 | 
						||
      >
 | 
						||
        <img
 | 
						||
          src={require("../assets/layout/menu-all.png")}
 | 
						||
          alt=""
 | 
						||
          className="sider__menuAll-icon"
 | 
						||
        />
 | 
						||
        <div className="sider__menu-font">
 | 
						||
          <span className="sider__menu-font--text">首页</span>
 | 
						||
          <Icon type="right" className="sider__menu-font--icon" />
 | 
						||
        </div>
 | 
						||
      </div>
 | 
						||
      {/** 常用菜单 */}
 | 
						||
      <Scrollbars
 | 
						||
        autoHide
 | 
						||
        autoHideTimeout={1000}
 | 
						||
        autoHideDuration={200}
 | 
						||
        className="sider__scrollbars"
 | 
						||
      >
 | 
						||
         <div className="sider__menuExpand-header">
 | 
						||
            {topMenus.menus.map((menu, index) => {
 | 
						||
              const active = menu.Node.ID === activeMenu?.ID;
 | 
						||
              const activeKeep = menu.Node.ID === activeKeepMenu?.ID;
 | 
						||
              const wrapClass = `sider__menuExpand-menu ${
 | 
						||
                index === topMenus.menus.length - 1 ? "lastChild" : ""
 | 
						||
              }`;
 | 
						||
              const iconClass = `sider__menuExpand-menu--itemIcon ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              const fontClass = `sider__menuExpand-menu--itemText text-ellipsis ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              const indicatorClass = `sider__menuExpand-menu--indicator ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              return (
 | 
						||
                <div
 | 
						||
                  key={`${menu.Node.ID}_${index}`}
 | 
						||
                //   onMouseEnter={() => {
 | 
						||
                //     setCurrMenu(menu.Node);
 | 
						||
                //     setActiveKeepMenu(menu.Node);
 | 
						||
                //   }}
 | 
						||
                  onClick={() =>{handleActiveMenu(menu.Node);setCurrMenu(menu.Node);
 | 
						||
                    setActiveKeepMenu(menu.Node);} }
 | 
						||
                  className={wrapClass}
 | 
						||
                >
 | 
						||
                  <div className="sider__menuExpand-menu--item">
 | 
						||
                    <Icon
 | 
						||
                      type={menu.Node.ICON ? menu.ICON : "file-text"}
 | 
						||
                      className={iconClass}
 | 
						||
                    />
 | 
						||
                    <div className={fontClass}>{menu.Node.NAME}</div>
 | 
						||
                  </div>
 | 
						||
                  <div className={indicatorClass} />
 | 
						||
                </div>
 | 
						||
              );
 | 
						||
            })}
 | 
						||
          </div>
 | 
						||
          {/** 二三级菜单 */}
 | 
						||
          <div className="sider__menuExpand-body">
 | 
						||
            <Scrollbars autoHide autoHideTimeout={1000} autoHideDuration={200}>
 | 
						||
              <div className="sider__menuExpand-body--scroll">
 | 
						||
                {subMenus.map((menu, index) => {
 | 
						||
                  const isLevel4Menu = menu.Children?.find(
 | 
						||
                    (mc) => mc.Children?.length
 | 
						||
                  );
 | 
						||
                  const childMenus = isLevel4Menu
 | 
						||
                    ? [menu]
 | 
						||
                    : menu.Children || [];
 | 
						||
                  return (
 | 
						||
                    <div
 | 
						||
                      key={`${menu.Node.ID}_${index}`}
 | 
						||
                      className="sider__menuExpand-subMenu"
 | 
						||
                      style={{
 | 
						||
                        flex: `0 0 ${(1 / topMenus.leafMenuSections) * 100}%`,
 | 
						||
                      }}
 | 
						||
                    >
 | 
						||
                      {/** 二级菜单 */}
 | 
						||
                      <div
 | 
						||
                        onClick={() => handleMenuShrink(menu.Node.ID)}
 | 
						||
                        className="sider__menuExpand-subMenu--title"
 | 
						||
                      >
 | 
						||
                        {menu.Node.NAME}
 | 
						||
                        <Icon
 | 
						||
                          type={!menuShrink[menu.Node.ID] ? "down" : "up"}
 | 
						||
                          className="sider__menuExpand-subMenu--title--icon"
 | 
						||
                        />
 | 
						||
                      </div>
 | 
						||
                      {/** 三级菜单 */}
 | 
						||
                      {!menuShrink[menu.Node.ID]
 | 
						||
                        ? childMenus.map((child, childIndex) => {
 | 
						||
                            const active = child.Node.ID === activeMenu?.ID;
 | 
						||
                            const iconClass = `sider__menuExpand-menu--itemIcon ${
 | 
						||
                              active ? "active" : ""
 | 
						||
                            }`;
 | 
						||
                            const fontClass = `sider__menuExpand-menu--itemText text-ellipsis ${
 | 
						||
                              active ? "active" : ""
 | 
						||
                            }`;
 | 
						||
                            return (
 | 
						||
                              <div
 | 
						||
                                key={`${child.Node.ID}_${index}_${childIndex}`}
 | 
						||
                                onClick={() =>
 | 
						||
                                  isLevel4Menu
 | 
						||
                                    ? navToBackend(child.Node)
 | 
						||
                                    : handleActiveMenu(child.Node)
 | 
						||
                                }
 | 
						||
                                className="sider__menuExpand-subMenu--menu"
 | 
						||
                              >
 | 
						||
                                <Icon
 | 
						||
                                  type={
 | 
						||
                                    child.Node.ICON
 | 
						||
                                      ? child.Node.ICON
 | 
						||
                                      : "file-text"
 | 
						||
                                  }
 | 
						||
                                  className={iconClass}
 | 
						||
                                />
 | 
						||
                                <div className={fontClass}>
 | 
						||
                                  {child.Node.NAME}
 | 
						||
                                </div>
 | 
						||
                              </div>
 | 
						||
                            );
 | 
						||
                          })
 | 
						||
                        : null}
 | 
						||
                    </div>
 | 
						||
                  );
 | 
						||
                })}
 | 
						||
              </div>
 | 
						||
            </Scrollbars>
 | 
						||
          </div>
 | 
						||
        {favorMenus.map((menu, index) => {
 | 
						||
          const active = menu.ID === activeMenu?.ID;
 | 
						||
          const wrapClass = `sider__menu ${
 | 
						||
            index === favorMenus.length - 1 ? "lastChild" : ""
 | 
						||
          }`;
 | 
						||
          const iconClass = `sider__menu-icon ${active ? "active" : ""}`;
 | 
						||
          const fontClass = `sider__menu-font--text ${active ? "active" : ""}`;
 | 
						||
          return (
 | 
						||
            <div
 | 
						||
              key={`${menu.ID}_${index}`}
 | 
						||
              onClick={() => handleActiveMenu(menu)}
 | 
						||
              className={wrapClass}
 | 
						||
            >
 | 
						||
              <Icon
 | 
						||
                type={menu.ICON ? menu.ICON : "file-text"}
 | 
						||
                className={iconClass}
 | 
						||
              />
 | 
						||
              <div className="sider__menu-font">
 | 
						||
                <span className={fontClass}>{menu.NAME}</span>
 | 
						||
              </div>
 | 
						||
            </div>
 | 
						||
          );
 | 
						||
        })}
 | 
						||
        
 | 
						||
        
 | 
						||
      </Scrollbars>
 | 
						||
      {/** 展开菜单 */}
 | 
						||
      <div
 | 
						||
        // onMouseEnter={expandMenuShow ? handleExpandMenuShow : handleExpandMenuHide}
 | 
						||
        // onMouseLeave={handleExpandMenuHide}
 | 
						||
        className={`sider__menuExpand ${
 | 
						||
          expandMenuShow ? "expandMenuShow" : ""
 | 
						||
        }`}
 | 
						||
        style={{
 | 
						||
          width: expandMenuShow
 | 
						||
            ? topMenus.width + 48 < 300
 | 
						||
              ? 300
 | 
						||
              : topMenus.width + 48
 | 
						||
            : 0,
 | 
						||
          overflow: expandMenuShow ? "visible" : "hidden",
 | 
						||
        }} // 加 padding: 24
 | 
						||
      >
 | 
						||
        {/* 留白, 扩大上方onMouseEnter的范围 */}
 | 
						||
        <div
 | 
						||
          className={`${
 | 
						||
            expandMenuShow
 | 
						||
              ? "sider__menuExpand-divempty"
 | 
						||
              : "sider__menuExpand-divempty-hide"
 | 
						||
          }`}
 | 
						||
        ></div>
 | 
						||
        {/** inner 加 padding,否则加在 sider__menuExpand 上的话,width 为 0,也会有 padding 的宽度 */}
 | 
						||
        <div
 | 
						||
          className="sider__menuExpand-inner"
 | 
						||
          style={{
 | 
						||
            width: topMenus.width + 48 < 300 ? 300 : topMenus.width + 48,
 | 
						||
          }}
 | 
						||
        >
 | 
						||
          {/** 一级菜单 */}
 | 
						||
          <div className="sider__menuExpand-header">
 | 
						||
            {topMenus.menus.map((menu, index) => {
 | 
						||
              const active = menu.Node.ID === activeMenu?.ID;
 | 
						||
              const activeKeep = menu.Node.ID === activeKeepMenu?.ID;
 | 
						||
              const wrapClass = `sider__menuExpand-menu ${
 | 
						||
                index === topMenus.menus.length - 1 ? "lastChild" : ""
 | 
						||
              }`;
 | 
						||
              const iconClass = `sider__menuExpand-menu--itemIcon ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              const fontClass = `sider__menuExpand-menu--itemText text-ellipsis ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              const indicatorClass = `sider__menuExpand-menu--indicator ${
 | 
						||
                active ? "active" : ""
 | 
						||
              } ${activeKeep ? "active" : ""}`;
 | 
						||
              return (
 | 
						||
                <div
 | 
						||
                  key={`${menu.Node.ID}_${index}`}
 | 
						||
                  onMouseEnter={() => {
 | 
						||
                    setCurrMenu(menu.Node);
 | 
						||
                    setActiveKeepMenu(menu.Node);
 | 
						||
                  }}
 | 
						||
                  onClick={() => handleActiveMenu(menu.Node)}
 | 
						||
                  className={wrapClass}
 | 
						||
                >
 | 
						||
                  <div className="sider__menuExpand-menu--item">
 | 
						||
                    <Icon
 | 
						||
                      type={menu.Node.ICON ? menu.ICON : "file-text"}
 | 
						||
                      className={iconClass}
 | 
						||
                    />
 | 
						||
                    <div className={fontClass}>{menu.Node.NAME}</div>
 | 
						||
                  </div>
 | 
						||
                  <div className={indicatorClass} />
 | 
						||
                </div>
 | 
						||
              );
 | 
						||
            })}
 | 
						||
          </div>
 | 
						||
          {/** 二三级菜单 */}
 | 
						||
          <div className="sider__menuExpand-body">
 | 
						||
            <Scrollbars autoHide autoHideTimeout={1000} autoHideDuration={200}>
 | 
						||
              <div className="sider__menuExpand-body--scroll">
 | 
						||
                {subMenus.map((menu, index) => {
 | 
						||
                  const isLevel4Menu = menu.Children?.find(
 | 
						||
                    (mc) => mc.Children?.length
 | 
						||
                  );
 | 
						||
                  const childMenus = isLevel4Menu
 | 
						||
                    ? [menu]
 | 
						||
                    : menu.Children || [];
 | 
						||
                  return (
 | 
						||
                    <div
 | 
						||
                      key={`${menu.Node.ID}_${index}`}
 | 
						||
                      className="sider__menuExpand-subMenu"
 | 
						||
                      style={{
 | 
						||
                        flex: `0 0 ${(1 / topMenus.leafMenuSections) * 100}%`,
 | 
						||
                      }}
 | 
						||
                    >
 | 
						||
                      {/** 二级菜单 */}
 | 
						||
                      <div
 | 
						||
                        onClick={() => handleMenuShrink(menu.Node.ID)}
 | 
						||
                        className="sider__menuExpand-subMenu--title"
 | 
						||
                      >
 | 
						||
                        {menu.Node.NAME}
 | 
						||
                        <Icon
 | 
						||
                          type={!menuShrink[menu.Node.ID] ? "down" : "up"}
 | 
						||
                          className="sider__menuExpand-subMenu--title--icon"
 | 
						||
                        />
 | 
						||
                      </div>
 | 
						||
                      {/** 三级菜单 */}
 | 
						||
                      {!menuShrink[menu.Node.ID]
 | 
						||
                        ? childMenus.map((child, childIndex) => {
 | 
						||
                            const active = child.Node.ID === activeMenu?.ID;
 | 
						||
                            const iconClass = `sider__menuExpand-menu--itemIcon ${
 | 
						||
                              active ? "active" : ""
 | 
						||
                            }`;
 | 
						||
                            const fontClass = `sider__menuExpand-menu--itemText text-ellipsis ${
 | 
						||
                              active ? "active" : ""
 | 
						||
                            }`;
 | 
						||
                            return (
 | 
						||
                              <div
 | 
						||
                                key={`${child.Node.ID}_${index}_${childIndex}`}
 | 
						||
                                onClick={() =>
 | 
						||
                                  isLevel4Menu
 | 
						||
                                    ? navToBackend(child.Node)
 | 
						||
                                    : handleActiveMenu(child.Node)
 | 
						||
                                }
 | 
						||
                                className="sider__menuExpand-subMenu--menu"
 | 
						||
                              >
 | 
						||
                                <Icon
 | 
						||
                                  type={
 | 
						||
                                    child.Node.ICON
 | 
						||
                                      ? child.Node.ICON
 | 
						||
                                      : "file-text"
 | 
						||
                                  }
 | 
						||
                                  className={iconClass}
 | 
						||
                                />
 | 
						||
                                <div className={fontClass}>
 | 
						||
                                  {child.Node.NAME}
 | 
						||
                                </div>
 | 
						||
                              </div>
 | 
						||
                            );
 | 
						||
                          })
 | 
						||
                        : null}
 | 
						||
                    </div>
 | 
						||
                  );
 | 
						||
                })}
 | 
						||
              </div>
 | 
						||
            </Scrollbars>
 | 
						||
          </div>
 | 
						||
        </div>
 | 
						||
        {/** 左边的 border */}
 | 
						||
        <div className="sider__menuExpand-divider" />
 | 
						||
        {/* 留白, 扩大右侧onMouseEnter的范围 */}
 | 
						||
        <div
 | 
						||
          className={`${
 | 
						||
            expandMenuShow
 | 
						||
              ? "sider__menuExpand-divempty-right"
 | 
						||
              : "sider__menuExpand-divempty-hide"
 | 
						||
          }`}
 | 
						||
        ></div>
 | 
						||
      </div>
 | 
						||
    </div>
 | 
						||
  );
 | 
						||
};
 | 
						||
 | 
						||
export default withRouter(connect(({ login }) => ({ login }))(Sider));
 |