295 lines
12 KiB
JavaScript
295 lines
12 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(false)
|
|||
|
|
const [expandMenuShow, setExpandMenuShow] = useState(false)
|
|||
|
|
|
|||
|
|
/** 菜单页展开与收起 */
|
|||
|
|
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'
|
|||
|
|
>
|
|||
|
|
{
|
|||
|
|
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 className='sider__menuExpand-footer'>
|
|||
|
|
<Scrollbars
|
|||
|
|
autoHide
|
|||
|
|
autoHideTimeout={1000}
|
|||
|
|
autoHideDuration={200}
|
|||
|
|
>
|
|||
|
|
<div className='sider__menuExpand-footer--scroll'>{currMenu?.REMARK}</div>
|
|||
|
|
</Scrollbars>
|
|||
|
|
<span className='sider__menuExpand-footer--title'>脚注</span>
|
|||
|
|
</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))
|