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

2023-11-17

一、基础页面

1.请求数据

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

{
    "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

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

2.静态页面 user.jsx

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 }

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】把状态里的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请求

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

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

https://3x.ant.design/components/modal-cn/

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.实现:快捷弹窗删除用户

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静态及向子组件传函数

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静态及基础验证、向父组件传值

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添加接口

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

4.功能实现user.jsx

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接收父组件传过来的角色,表单验证完善,邮箱,手机号等正则较验证,

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请求函数

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

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

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

2. user.jsx

//表格的列数据指定
    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

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

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

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

{//如果是修改用户,则不显示密码框(接口未提供密码修改)
             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>
             )}

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

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐