0%

《React后台管理系统实战:七》用户管理:获取-添加-修改-删除用户、菜单权限管理

一、基础页面

1.请求数据

数据 http://localhost:5000/manage/user/list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
{
"status": 0,
"data": {
"users": [
{
"_id": "5e7d7953d4a98d1a1c1cf00a",
"username": "jim",
"password": "8a6ec0ea3a19e75020d79132e5d7560d",
"phone": "15858985688",
"email": "jim@qq.com",
"role_id": "",
"create_time": 1585281363052,
"__v": 0
},
{
"_id": "5e7d8802d4a98d1a1c1cf018",
"username": "lucy",
"password": "14a9b5af17202b5f18ce53006251293b",
"phone": "18958989918",
"email": "lucy@qq.com",
"create_time": 1585285122576,
"__v": 0
},
{
"_id": "5e7d886ed4a98d1a1c1cf019",
"username": "jack",
"password": "202cb962ac59075b964b07152d234b70",
"phone": "189588899199",
"email": "jack@qq.com",
"create_time": 1585285230737,
"__v": 0
}
],
"roles": [
{
"menus": [
"/products",
"/category",
"/product"
],
"_id": "5e70267a7df5a91db48e7793",
"name": "产品",
"create_time": 1584408186923,
"__v": 0,
"auth_name": "admin",
"auth_time": 1584598815083
},
{
"menus": [
"/products",
"/category",
"/product"
],
"_id": "5e7185d827294126102b29e2",
"name": "仓管",
"create_time": 1584498136216,
"__v": 0,
"auth_time": 1584663789593,
"auth_name": "admin"
},]
}
}

请求所有列表Api

1
2
// 请求所有用户列表
export const reqUsers=()=>ajax(BASE+'/manage/user/list')

2.静态页面 user.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import React,{Component} from 'react'
import {Card,Button,Table,Modal,message} from 'antd'
import LinkButton from '../../../components/link-button/index'
import {reqUsers} from '../../../api'
import {formateDate} from '../../../utils/dateUtils'

export default class User extends Component{
state ={
users:[],//用户列表
roles:[],//所有角色列表
}

initColumns=()=>{
this.columns=[
{title:'用户名',dataIndex:'username'},
{title:'邮箱',dataIndex:'email'},
{title:'电话',dataIndex:'phone'},
{title:'注册时间',dataIndex:'create_time',render:formateDate}, //完整写法render:()=>{formateDate('create_time')}
{title:'所属角色',dataIndex:'role_id'},
{
title:'操作',
render:()=>(
<span>
<LinkButton onClick={() => this.showUpdate()}>修改</LinkButton>
<LinkButton onClick={() => this.deleteUser()}>删除</LinkButton>
</span>)

}
]
}

//获取用户列表
getUsers=async()=>{
const result=await reqUsers()
if (result.status===0){
const {users,roles}=result.data
this.setState({
users,
roles
})
}
}


componentWillMount () {
this.initColumns()
}

componentDidMount(){
this.getUsers()
}

render(){
//卡片标题
const title=<Button type='primary'>创建用户</Button>
return(
<Card title={title}>
<Table
bordered
rowKey='_id'
dataSource={this.state.users}
columns={this.columns}
pagination={{defaultPageSize:2}}
/>
</Card>
)
}
}

效果:

在这里插入图片描述

3. 显示所属角色名,初级

{title:'所属角色',dataIndex:'role_id', render:(role_id) => this.state.roles.find(role => role._id===role_id).name //name取自roles.name }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
initColumns=()=>{
this.columns=[
{title:'用户名',dataIndex:'username'},
{title:'邮箱',dataIndex:'email'},
{title:'电话',dataIndex:'phone'},
{title:'注册时间',dataIndex:'create_time',render:formateDate}, //render:()=>{formateDate('create_time')}
//【显示角色名】在状态的roles中找
{title:'所属角色',dataIndex:'role_id',
render:(role_id) => this.state.roles.find(role => role._id===role_id).name //name取自roles.name
},
{
title:'操作',
render:()=>(
<span>
<LinkButton onClick={() => this.showUpdate()}>修改</LinkButton>
<LinkButton onClick={() => this.deleteUser()}>删除</LinkButton>
</span>)
}
]
}

3. 2显示所属角色名,高级

思路:

  1. 写一个函数,把状态里的roles[]数据转换成以role._id为键名,role.name为键值的字典
  2. 调用1步函数生成字典
  3. 引用字典
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    //【1】把状态里的roles[]数据转换成以role._id为键名,role.name为键值的字典
    initRoleNames=(roles)=>{
    const roleNames=roles.reduce((pre,role)=>{
    pre[role._id]=role.name
    return pre
    },{})
    //保存到this
    this.roleNames=roleNames
    }


    initColumns=()=>{
    this.columns=[
    {title:'用户名',dataIndex:'username'},
    {title:'邮箱',dataIndex:'email'},
    {title:'电话',dataIndex:'phone'},
    {title:'注册时间',dataIndex:'create_time',render:formateDate}, //完整写法:render:()=>{formateDate('create_time')}
    {title:'所属角色',dataIndex:'role_id',
    render:(role_id)=>this.roleNames[role_id] //【3】根据Id展示字典里对应角色名
    },
    {
    title:'操作',
    render:()=>(
    <span>
    <LinkButton onClick={() => this.showUpdate()}>修改</LinkButton>
    <LinkButton onClick={() => this.deleteUser()}>删除</LinkButton>
    </span>)
    }
    ]
    }


    //获取用户列表
    getUsers=async()=>{
    const result=await reqUsers()
    if (result.status===0){
    const {users,roles}=result.data
    this.initRoleNames(roles)//【2】把获取的角色数据传过去,生成roleNames的字典
    this.setState({
    users,
    roles
    })
    }
    }

    效果:所属角色展示对应名字

    在这里插入图片描述

    二、删除用户功能

    0. api请求

    1
    2
    // 删除指定用户
    export const reqUserDel=(userId)=>ajax(BASE+'/manage/user/delete',{userId},'POST')

    1.antd支持: 用 Modal.confirm() 快捷弹出确认框

    https://3x.ant.design/components/modal-cn/
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import { Modal, Button } from 'antd';

    const { confirm } = Modal;

    function showConfirm() {
    confirm({
    title: 'Do you Want to delete these items?',
    content: 'Some descriptions',
    onOk() {
    console.log('OK');
    },
    onCancel() {
    console.log('Cancel');
    },
    });
    }

    2.实现:快捷弹窗删除用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import {reqUsers,reqUserDel} from '../../../api' //【1】reqUserDel

//表格的列数据指定
initColumns=()=>{
this.columns=[
......以上省略
{
title:'操作',
render:(user)=>(//【2】传入user到删除中
<span>
<LinkButton onClick={() => this.showUpdate(user)}>修改</LinkButton>
<LinkButton onClick={() => this.deleteUser(user)}>删除</LinkButton>
</span>)
}
]
}

//【3】删除指定用户
deleteUser=(user)=>{
//Modal.confirm 用法详见antd文档3.x的 使用 confirm() 可以快捷地弹出确认框
Modal.confirm({
title: `确定要删除${user.username}用户吗`,
content: '删除后将不可恢复',
onOk: async () => { //此为要用async+箭头写法;原:onOk(){//内容}
const result = await reqUserDel(user._id)
if(result.status===0){
message.success('删除用户成功')
this.getUsers()//删除之后更新列表
}
} //onCancel之后什么也不做因此省略
})
}

效果:在用户后点删除效果:

在这里插入图片描述

三、添加用户功能

1.user.jsx静态及向子组件传函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import AddForm from './add-form' //【5】引入添加表单内容

state ={
users:[],//用户列表
roles:[],//所有角色列表
isShow:false,//【0】控制Modal弹窗是否显示
}

//【3】Modal弹窗点ok
handleOk=()=>{}

//【4】Modal弹窗点cancel
handleCancel=()=>{
this.setState({isShow:false})
}

//render下
//卡片标题部分【1】显示Modal弹窗onClick={()=>this.setState({isShow:true})}
const title=<Button type='primary' onClick={()=>this.setState({isShow:true})}>创建用户</Button>


//Card内
{/* 【2】弹窗 【6】引入AddForm组件 并把函数 form =>this.form=form 传过去(用于接收子组件传过来的form)*/}
<Modal
title='添加用户'
visible={this.state.isShow}
onOk={this.handleOk}
onCancel={this.handleCancel}>
<AddForm setForm={form =>this.form=form} />
</Modal>

2.add-form.jsx静态及基础验证、向父组件传值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import React,{PureComponent} from 'react'
import {Form,Input,Select} from 'antd'
import PropTypes from 'prop-types'

const Item=Form.Item
const Option=Select.Option

class AddForm extends PureComponent{
static propTypes={
setForm:PropTypes.func.isRequired, //接收父组件传过来的setForm函数
}

componentWillMount(){
this.props.setForm(this.props.form) //把当前页面的form通过setForm函数传到父组件
}

render(){
/**api请求返回数据
{
"_id": "5cb05b4db6ed8c44f42c9af2",
"username": "test",
"password": "202cb962ac59075b964b07152d234b70",
"phone": "123412342134",
"email": "sd",
"role_id": "5ca9eab0b49ef916541160d4",
"create_time": 1555061581734,
"__v": 0
}
*/

//表单样式控制
const formItemLayout = {
labelCol:{ span: 5,offset:0 },
wrapperCol:{ span: 15,offset:0 }
}

const { getFieldDecorator }=this.props.form //form组件的获取表单验证函数
return(
// 表单样式控制 {...formItemLayout}
<Form {...formItemLayout} >

<Item label='用户名'>
{
getFieldDecorator('username',{
initialValue:'',
rules:[{required:true,message:'用户名必须输入'}]
})(<Input placeholder='请输入用户名' />)
}
</Item>

<Item label='密码'>
{
getFieldDecorator('password',{
initialValue:'',
rules:[{required:true,message:'密码必须输出'}]
})(<Input type='password' placeholder='请输入密码' />)
}
</Item>

<Item label='手机号'>
{
getFieldDecorator('phone',{
initialValue:'',
})(<Input placeholder='请输入手机号' />)
}
</Item>

<Item label='邮箱'>
{
getFieldDecorator('email',{
initialValue:'',
})(<Input placeholder='请输入邮箱' />)
}
</Item>

<Item label='角色'>
{
getFieldDecorator('role_id',{initialValue:''})(
<Select value='2'>
<Option value='1'>A</Option>
<Option value='2'>B</Option>
</Select>
)
}
</Item>

</Form>
)
}
}
//为当前组件添加一个form对象
export default Form.create()(AddForm)

效果:点创建用户弹出如下

在这里插入图片描述

3.功能实现:api添加接口

1
2
//添加用户
export const reqUserAdd=(user)=>ajax(BASE+'/manage/user/add',user,'POST')

4.功能实现user.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import {reqUsers,reqUserDel,reqUserAdd} from '../../../api' //【0】reqUserAdd

//【1】Modal弹窗点ok后添加用户
handleOk=()=>{
//表单验证是否通过函数
this.form.validateFields(async(err,values)=>{
if(!err){//如果本地表单验证通过
this.setState({isShow:false}) //关闭弹窗
//1.收集表单数据
const user=values
console.log(user)
this.form.resetFields() //清空表单方便下次使用
//2.提交表单
const result=await reqUserAdd(user)
//3.更新列表
if(result.status===0){
message.success(`${user.username}添加用户成功`)
this.getUsers() //更新用户列表
}
}
})

}



//【2】Modal弹窗点cancel,关闭弹窗
handleCancel=()=>{
this.setState({isShow:false})
this.form.resetFields() //清空表单方便下次使用
}

//render内card内
<AddForm
setForm={form =>this.form=form} //把函数传给子组件,接收其form相关功能
roles={this.state.roles} //【3】把roles角色传给子组件
/>

5. add-form.jsx接收父组件传过来的角色,表单验证完善,邮箱,手机号等正则较验证,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import React,{PureComponent} from 'react'
import {Form,Input,Select} from 'antd'
import PropTypes from 'prop-types'

const Item=Form.Item
const Option=Select.Option

class AddForm extends PureComponent{
static propTypes={
setForm:PropTypes.func.isRequired, //接收父组件传过来的setForm函数
roles:PropTypes.array.isRequired, //【1】接收父组件传来的角色列表
}

componentWillMount(){
this.props.setForm(this.props.form) //把当前页面的form通过setForm函数传到父组件
}

render(){
/**api请求返回数据
{
"_id": "5cb05b4db6ed8c44f42c9af2",
"username": "test",
"password": "202cb962ac59075b964b07152d234b70",
"phone": "123412342134",
"email": "sd",
"role_id": "5ca9eab0b49ef916541160d4",
"create_time": 1555061581734,
"__v": 0
}
*/

//表单样式控制
const formItemLayout = {
labelCol:{ span: 5,offset:0 },
wrapperCol:{ span: 15,offset:0 }
}

const { getFieldDecorator }=this.props.form //form组件的获取表单验证函数
const {roles}=this.props //【2】解构出roles
return(
// 表单样式控制 {...formItemLayout}
<Form {...formItemLayout} >

<Item label='用户名'>
{
getFieldDecorator('username',{
initialValue:'',
rules:[
{required:true,message:'用户名必须输入'},
{min:4,max:12,message:'用户名必须大于4位小于12位'}
]
})(<Input placeholder='请输入用户名' />)
}
</Item>

<Item label='密码'>
{
getFieldDecorator('password',{
initialValue:'',
rules:[
{required:true,message:'密码必须输出'},
{min:4,max:12,message:'密码必须大于4位小于12位'}
]
})(<Input type='password' placeholder='请输入密码' />)
}
</Item>

<Item label='手机号'>
{
getFieldDecorator('phone',{
initialValue:'',
rules:[
{required:true,pattern: /^1[3|4|5|7|8][0-9]\d{8}$/, message: '请输入正确的手机号'},
]
})(<Input placeholder='请输入手机号' />)
}
</Item>

<Item label='邮箱'>
{
getFieldDecorator('email',{
initialValue:'',
rules:[
{pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
message: '邮箱格式不正确'},
{max: 50,message: '邮箱不得超过50字符'},
]
})(<Input placeholder='请输入邮箱' />)
}
</Item>

<Item label='角色'>
{
getFieldDecorator('role_id',{
rules:[{required:true,message:'角色必须选择'}]
})( //如果要让select的palceholder有效,此处不能写initialValue
<Select placeholder="请选择角色">
{//【3】把角色写入option中
roles.map(role=>{
return <Option key={role._id} value={role._id}>{role.name}</Option>
})
}

</Select>
)
}
</Item>

</Form>
)
}
}
//为当前组件添加一个form对象
export default Form.create()(AddForm)

效果:验证都通过后才能提交添加用户请求

在这里插入图片描述

四、用户更新(修改)

1.api请求函数

1
2
3
4
5
6
7
8
9
10
11
12
13
http://localhost:5000/manage/user/update

### 请求方式:
POST

### 参数类型

|参数 |是否必选 |类型 |说明
|_id |Y |string |ID
|username |N |string |用户名
|phone |N |string |手机号
|email |N |string |邮箱
|role_id |N |string |角色ID

因为和添加用户用的请求参数都是一样的,所以合用一个请求

1
2
//添加/修改用户(如果存在._id说明是更新就用update拼接路径,否则就是添加用户)
export const reqUserAdd=(user)=>ajax(BASE+'/manage/user/'+(user._id?'update':'add'),user,'POST')

2. user.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//表格的列数据指定
initColumns=()=>{
this.columns=[
{title:'用户名',dataIndex:'username'},
{title:'邮箱',dataIndex:'email'},
{title:'电话',dataIndex:'phone'},
{title:'注册时间',dataIndex:'create_time',render:formateDate}, //完整写法:render:()=>{formateDate('create_time')}
{title:'所属角色',dataIndex:'role_id',
render:(role_id)=>this.roleNames[role_id] //根据Id展示对应的角色名; 旧写法(role_id) => this.state.roles.find(role => role._id===role_id).name //name取自roles.name
},
{
title:'操作',
render:(user)=>(//【1】传入user到更新中
<span>
<LinkButton onClick={() => this.showUpdate(user)}>修改</LinkButton>
<LinkButton onClick={() => this.deleteUser(user)}>删除</LinkButton>
</span>)
}
]
}



//Modal弹窗点ok后添加用户
handleOk=()=>{
//表单验证是否通过函数
this.form.validateFields(async(err,values)=>{
if(!err){//如果本地表单验证通过
this.setState({isShow:false}) //关闭弹窗
//1.收集表单数据
const user=values
console.log(user)
//【8】重要:如果是修改(判断user是否存在 )则要把user._id也传过去有_id才被认为是更新用户,否则就是添加了
if(this.user){
user._id=this.user._id
}
this.form.resetFields() //清空表单方便下次使用
//2.提交表单
const result=await reqUserAdd(user)
//3.更新列表
if(result.status===0){//【9】显示对应提示
message.success(`${this.user ? '修改':'添加'}用户成功`)
this.getUsers() //更新用户列表

}
}
})

}



//【2】修改(更新)用户
showUpdate=(user)=>{
this.user=user //保存user到this
this.setState({isShow:true}) //显示更新表单弹窗(此处和添加用的是同一表单弹窗)
}

//【6】显示添加或修改的弹窗表单
showAdd=()=>{
this.user = null //【7】重要:清除修改时建立的user防止点修改后再点添加 其表单信息依然存在
this.setState({isShow:true})
}


//render下
//【5】非常重要:把onclick改为一个单独函数,用来清除修改时建立的user; 卡片标题部分显示Modal弹窗onClick={()=>this.setState({isShow:true})}
const title=<Button type='primary' onClick={this.showAdd}>创建用户</Button>

//【3】非常重要:让user=修改的user或 添加用户时的空对象,否则添加、修改用户用同一窗口,添加用户时会出错,找不到user发生
const user = this.user || {}


//return的card中
{/* 引入AddForm组件 并把函数 form =>this.form=form 传过去(用于接收子组件传过来的form)*/}
<Modal
title={user._id ? '修改用户' : '添加用户'} //【5】如果user id存在就是弹窗标题就是修改用户
visible={this.state.isShow}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<AddForm
setForm={form =>this.form=form}
roles={this.state.roles} //把roles角色传给子组件
user={user} //【4】把user字典传到子组件中,方便其显示在表单对应用户信息
/>
</Modal>

3.add-form.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static propTypes={
setForm:PropTypes.func.isRequired, //接收父组件传过来的setForm函数,用于把当前页面的form功能传给你组件
roles:PropTypes.array.isRequired, //接收父组件传来的角色列表,用于展示在添加用户的下拉表角色选择中
user:PropTypes.object.isRequired, //【1】接收父组件传来的用户信息,用于展示在修改用户时的表单中
}



//render下
const {roles,user}=this.props //解构出roles【2】解构出user
//return下
// 表单样式控制 {...formItemLayout}
<Form {...formItemLayout} >

<Item label='用户名'>
{
getFieldDecorator('username',{
initialValue:user.username, //【3】展示传过来的要修改的用户名
rules:[
{required:true,message:'用户名必须输入'},
{min:4,max:12,message:'用户名必须大于4位小于12位'}
]
})(<Input placeholder='请输入用户名' />)
}
</Item>

<Item label='密码'>
{
getFieldDecorator('password',{
initialValue:'', //【4】不需要展示修改密码
rules:[
{required:true,message:'密码必须输出'},
{min:4,max:12,message:'密码必须大于4位小于12位'}
]
})(<Input type='password' placeholder='请输入密码' />)
}
</Item>

<Item label='手机号'>
{
getFieldDecorator('phone',{
initialValue:user.phone, //【5】展示修改的手机
rules:[
{required:true,pattern: /^1[3|4|5|7|8][0-9]\d{8}$/, message: '请输入正确的手机号'},
]
})(<Input placeholder='请输入手机号' />)
}
</Item>

<Item label='邮箱'>
{
getFieldDecorator('email',{
initialValue:user.email, //【6】修改的邮箱
rules:[
{pattern: /^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z0-9]{2,6}$/,
message: '邮箱格式不正确'},
{max: 50,message: '邮箱不得超过50字符'},
]
})(<Input placeholder='请输入邮箱' />)
}
</Item>

<Item label='角色'>
{
getFieldDecorator('role_id',{
rules:[{required:true,message:'角色必须选择'}],
initialValue:user.role_id,

})( //如果要让select的palceholder有效,此处不能写initialValue
<Select placeholder="请选择角色">
{//把角色写入option中
roles.map(role=>{
return <Option key={role._id} value={role._id}>{role.name}</Option>
})
}

</Select>
)
}
</Item>

</Form>

效果:修改完成

在这里插入图片描述

五、菜单权限管理

left/index.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import React,{Component} from 'react'
import {Link,withRouter} from 'react-router-dom' //withRouter:高阶函数,用于把非路由组件包装成路由组件
import './left.less'
import logo from '../../../assets/images/logo.png'
import { Menu, Icon } from 'antd';
import menuList from '../../../config/menuConfig.js'
import memoryUtils from '../../../utils/memoryUtils'


const { SubMenu } = Menu;

class LeftNav extends Component{

state = {
collapsed: false,
};

// 控制左侧导航收缩
toggleCollapsed = () => {
this.setState({
collapsed: !this.state.collapsed,
});
};

// 根据配置文件自动写入左侧导航到页面
getMenuItem_map=(menuList)=>{
// 得到当前请求的路由路径
const path = this.props.location.pathname

return menuList.map(item=>{
if(!item.children){
return(
<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon}/>
<span>{item.title}</span>
</Link>
</Menu.Item>
)
}else{
// 查找一个与当前请求路径匹配的子Item
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果存在, 说明当前item的子列表需要打开
if (cItem) {
this.openKey = item.key
}

return(
<SubMenu
key={item.key}
title={
<span>
<Icon type={item.icon}/>
<span>{item.title}</span>
</span>
}
>
{this.getMenuItem(item.children)}

</SubMenu>
)
}
})
}



//【2】判断当前登陆用户对item是否有权限
hasAuth = (item) => {
const {key, isPublic} = item //取出key,菜单是否是公共的(无需权限也可见)

const menus = memoryUtils.user.role.menus //得到对应角色拥有的菜单
const username = memoryUtils.user.username //得到当前登录用户名
/*
1. 如果当前用户是admin
2. 如果当前item是公开的
3. 当前用户有此item的权限: key有没有存在于menus中
*/
if(username==='admin' || isPublic || menus.indexOf(key)!==-1) {
return true
} else if(item.children){ // 4. 如果当前用户有此item的某个子item的权限
return !!item.children.find(child => menus.indexOf(child.key)!==-1) //!强制转换成bool类型值
}

return false
}



//【0】getMenuItem用reduce函数重写方便对每一条进行控制
getMenuItem=(menuList)=>{
const path=this.props.location.pathname //得到当前请求路径
return menuList.reduce((pre,item)=>{

// 【1】如果当前用户有item对应的权限, 才需要显示对应的菜单项
if (this.hasAuth(item)) {
if(!item.children){//1.没有子菜单添加:
pre.push((
<Menu.Item key={item.key}>
<Link to={item.key}>
<Icon type={item.icon}/>
<span>{item.title}</span>
</Link>
</Menu.Item>
))
}else{//2.有子菜单

// 查找一个与当前请求路径,是否匹配的子Item
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果存在, 说明当前item的子列表需要展开
if (cItem) {
this.openKey = item.key
}

// 向pre添加<SubMenu>
pre.push((
<SubMenu
key={item.key}
title={
<span>
<Icon type={item.icon}/>
<span>{item.title}</span>
</span>
}
>
{this.getMenuItem(item.children)}
</SubMenu>
))
}
}

return pre
},[])
}




/*
在第一次render()之前执行一次
为第一个render()准备数据(必须同步的)
*/
componentWillMount () {
this.menuNodes = this.getMenuItem(menuList)
}

render(){
// 得到当前请求的路由路径
let path=this.props.location.pathname
// 得到需要打开菜单项的key
const openKey = this.openKey

return (
<div className='left'>
<Link to='/home' className='left-header'>
<img src={logo} alt='logo' />
<h1>深蓝管理后台</h1>
</Link>

<Menu
selectedKeys={[path]}
defaultOpenKeys={[openKey]}
mode="inline"
theme="dark"

>{/*inlineCollapsed={this.state.collapsed}*/}
{this.menuNodes}

</Menu>
</div>
)
}
}

/*用withRouter高阶组件:
包装非路由组件, 返回一个新的组件
新的组件向非路由组件传递3个属性: history/location/match
*/
export default withRouter(LeftNav)

效果:退出重新登录别的账户会根据权限显示其对应菜单

在这里插入图片描述

2.设置如果是自己的角色权限成功后,要求重新登录,以加载对应左导航role/index.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import memoryUtils from '../../../utils/memoryUtils' //引入记忆模块用于显示用户名
import storageUtils from '../../../utils/storageUtils' //引入内存模块用于记住登录状态


// 更新角色:点设置权限弹窗里的ok操作
updateRole=async()=>{//加async
// 隐藏确认框
this.setState({isShowAuth: false})

const role=this.state.role
//得到最新的menus => 到authForm.jsx里传值过来(getMenus = () => this.state.checkedKeys)
const menus=this.auth.current.getMenus()
//把接收过来的菜单传给当前role.menus => 到api/index.js里写更新角色接口函数
role.menus=menus

//添加授权时间及授权人
role.auth_time=Date.now()
role.auth_name = memoryUtils.user.username

//发送更新请求
console.log(role)
const result = await reqUpdateRole(role)
/*if (result.status===0){ //老写法
message.success('设置角色权限成功!')
this.getRoles()
}else{
message.error('更新角色权限失败')
}*/

if (result.status===0) {
// this.getRoles()
// 【1】如果当前更新的是自己角色的权限, 强制退出
if (role._id === memoryUtils.user.role_id) {
memoryUtils.user = {}
storageUtils.removeUser()
this.props.history.replace('/login')
message.success('当前用户角色权限成功,请重新登录')
} else {
message.success('设置角色权限成功')
this.setState({
roles: [...this.state.roles]
})
}

}


}

效果:如果设置的是自己的角色权限,成功后,将强制退出,并提示’当前用户角色权限成功,请重新登录’

六、优化

1.如果是修改用户,则不显示密码框 user/add-form.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{//如果是修改用户,则不显示密码框(接口未提供密码修改)
user._id ? null : (
<Item label='密码'>
{
getFieldDecorator('password',{
initialValue:user.password, //【4】不需要展示修改密码
rules:[
{required:true,message:'密码必须输出'},
{min:4,max:12,message:'密码必须大于4位小于12位'}
]
})(<Input type='password' placeholder='请输入密码' />)
}
</Item>
)}

点修改用户,则不显示密码输入框

在这里插入图片描述