一、React 结合 Antd 实现权限列表
- 引入所需相关的组件和文件,如下所示:
import React, { Component } from 'react'
import { Link, withRouter } from 'react-router-dom'
import { Menu, Icon } from 'antd'
import logo from '../../assets/images/logo.png'
import menuList from '../../config/menuConfig'
import memoryUtils from '../../utils/memoryUtils'
import './index.less'
const { SubMenu } = Menu
- 创建
LeftNav
左侧菜单组件,由于LeftNav
不是路由组件,需要变为路由组件,可以使用react-router-dom
中的withRouter
。withRouter
是一个高阶组件,包装非路由组件, 返回一个新的组件,新的组件向非路由组件传递三个属性:history/location/match
,代码如下所示:
class LeftNav extends Component {}
export default withRouter(LeftNav)
- 我们需要先渲染左侧菜单列表,先准备了一个菜单数据的配置文件
menuConfig.js
,用于动态渲染,代码如下所示:
const menuList = [
{
title: '首页',
key: '/home',
icon: 'home',
isPublic: true,
},
{
title: '商品',
key: '/products',
icon: 'appstore',
children: [
{
title: '品类管理',
key: '/category',
icon: 'bars'
},
{
title: '商品管理',
key: '/product',
icon: 'tool'
},
]
},
{
title: '用户管理',
key: '/user',
icon: 'user'
},
{
title: '角色管理',
key: '/role',
icon: 'safety',
},
{
title: '图形图表',
key: '/charts',
icon: 'area-chart',
children: [
{
title: '柱形图',
key: '/charts/bar',
icon: 'bar-chart'
},
{
title: '折线图',
key: '/charts/line',
icon: 'line-chart'
},
{
title: '饼图',
key: '/charts/pie',
icon: 'pie-chart'
},
]
},
{
title: '订单管理',
key: '/order',
icon: 'windows',
},
]
export default menuList
- 我们可以在
getMenuNodes
方法中传入这个配置文件,根据menu
的数据数组生成对应的标签数组,使用reduce()
+ 递归调用。判断menuList
的item
项是否有children
,如果没有,向pre
添加<Menu.Item>
。反之,向pre
添加<SubMenu>
。同时,查找一个与当前请求路径匹配的子Item
,如果存在, 说明当前item
的子列表需要打开,代码如下所示:
getMenuNodes = (menuList) => {
const path = this.props.location.pathname
return menuList.reduce((pre, item) => {
if (this.hasAuth(item)) {
if (!item.children) {
pre.push((
<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon}></Icon>
<span>{item.title}</span>
</Link>
</Menu.Item>
))
} else {
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
if (cItem) {
this.openKey = item.key
}
pre.push((
<SubMenu key={item.key} title={
<span>
<Icon type={item.icon}></Icon>
<span>{item.title}</span>
</span>
}>
{ this.getMenuNodes(item.children) }
</SubMenu>
))
}
}
return pre
}, [])
}
- 在
componentWillMount
中,为第一个render()
准备数据,代码如下所示:
componentWillMount () {
this.menuNodes = this.getMenuNodes(menuList)
}
- 在
render
中,通过this.props.location.pathname
得到当前请求的路由路径。判断当前请求的是商品或其子路由界面,然后得到需要打开菜单项的key
。在return
中,使用Menu
组件。mode
是菜单类型,现在支持垂直、水平、和内嵌模式三种。theme
是 主题颜色。selectedKeys
是当前选中的菜单项key
数组。defaultOpenKeys
是初始展开的SubMenu
菜单项key
数组。this.menuNodes
就是左侧菜单的数据,代码如下所示:
render () {
let path = this.props.location.pathname
if (path.indexOf('/product')===0) {
path = '/product'
}
const openKey = this.openKey
return (
<div className="left-nav">
<Link to="/" className="left-nav-header">
<img src={logo} alt="logo"></img>
<h1>React 后台</h1>
</Link>
<Menu
mode="inline"
theme="dark"
selectedKeys={[path]}
defaultOpenKeys={[openKey]}
>
{ this.menuNodes }
</Menu>
</div>
)
}
- 在左侧菜单的数据能够正常渲染后,就需要判断它的权限。在
hasAuth
方法中,判断当前登陆用户对item
是否有权限。通过item
结构获取菜单的key
和isPublic
,通过memoryUtils
获取当用户的menus
和username
,权限菜单列表和用户名称。对此,我们有四个判断条件,如果当前用户是admin
;如果当前item
是公开的;当前用户有此item
的权限:key
有没有menus
中;如果当前用户有此item
的某个子item
的权限。所以,根据username === 'admin' || isPublic || menus.indexOf(key)!== -1
判断。如果当前用户有此item
的某个子item
的权限,就进行下一步的查找渲染。这个方法在getMenuNodes
中进行使用,如果当前用户有item
对应的权限, 才需要显示对应的菜单项,代码如下所示:
hasAuth = (item) => {
const { key, isPublic } = item
const menus = memoryUtils.user.role.menus
const username = memoryUtils.user.username
if (username === 'admin' || isPublic || menus.indexOf(key)!== -1) {
return true
} else if (item.children) {
return !!item.children.find(child => menus.indexOf(child.key)!== -1)
}
return false
}
二、React 结合 Antd 实现权限列表的实现
-
React
结合Antd
实现权限列表的实现,完整代码如下所示:
import React, { Component } from 'react'
import { Link, withRouter } from 'react-router-dom'
import { Menu, Icon } from 'antd'
import logo from '../../assets/images/logo.png'
import menuList from '../../config/menuConfig'
import memoryUtils from '../../utils/memoryUtils'
import './index.less'
const { SubMenu } = Menu
class LeftNav extends Component {
hasAuth = (item) => {
const { key, isPublic } = item
const menus = memoryUtils.user.role.menus
const username = memoryUtils.user.username
if (username === 'admin' || isPublic || menus.indexOf(key)!== -1) {
return true
} else if (item.children) {
return !!item.children.find(child => menus.indexOf(child.key)!== -1)
}
return false
}
getMenuNodes = (menuList) => {
const path = this.props.location.pathname
return menuList.reduce((pre, item) => {
if (this.hasAuth(item)) {
if (!item.children) {
pre.push((
<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon}></Icon>
<span>{item.title}</span>
</Link>
</Menu.Item>
))
} else {
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
if (cItem) {
this.openKey = item.key
}
pre.push((
<SubMenu key={item.key} title={
<span>
<Icon type={item.icon}></Icon>
<span>{item.title}</span>
</span>
}>
{ this.getMenuNodes(item.children) }
</SubMenu>
))
}
}
return pre
}, [])
}
componentWillMount () {
this.menuNodes = this.getMenuNodes(menuList)
}
render () {
let path = this.props.location.pathname
if (path.indexOf('/product')===0) {
path = '/product'
}
const openKey = this.openKey
return (
<div className="left-nav">
<Link to="/" className="left-nav-header">
<img src={logo} alt="logo"></img>
<h1>React 后台</h1>
</Link>
<Menu
mode="inline"
theme="dark"
selectedKeys={[path]}
defaultOpenKeys={[openKey]}
>
{ this.menuNodes }
</Menu>
</div>
)
}
}
export default withRouter(LeftNav)