《React后台管理系统实战:六》角色管理:

49 篇文章 6 订阅

一、基础部分

1.角色管理静态页面role/index.jsx

import React,{Component} from 'react'
import {
    Card,
    Button,
    Table,
    message
} from 'antd'
import {PAGE_SIZE} from '../../../utils/constans' //常量:分页每页显示多少条数据

export default class Role extends Component{
    state={
        roles:[
            {"_id": "5ca9eaa1b49ef916541160d3",
            "name": "测试",
            "create_time": 1554639521749,
            "__v": 0,
            "auth_time": 1558679920395,
            "auth_name": "test007"},

            {"_id": "5ca9eaa1b49ef916541160d4",
            "name": "产品",
            "create_time": 1554639521749,
            "__v": 0,
            "auth_time": 1558679920395,
            "auth_name": "test008"},
        ], //Table datasource
    }

    //初始化表格列标题,及对应的数据源,dataIndex:对应api返回的数据名
    initColumns=()=>{
        this.columns=[
            {title:'角色名称',dataIndex:'name'},
            {title:'创建时间',dataIndex:'create_time'},
            {title:'授权时间',dataIndex:'auth_time'},
            {title:'授权人',dataIndex:'auth_name'},           
        ]
    }

    componentWillMount(){
        this.initColumns()//运行初始表格列标题,及对应的数据源函数,把表格列数据赋值到this.columus上
    }


    render(){
        const {roles}=this.state

        //card的左侧 (Button的disabled:按钮不可用)
        const title=(
            <span>
                <Button type='primary' style={{marginRight:8}}>创建角色</Button>
                <Button type='primary' disabled>设置角色权限</Button>
            </span>
        )
                
        return(
            <Card title={title}>
                <Table
                bordered /**边框 */
                rowKey='_id' /**表格行 key 的取值,可以是字符串或一个函数 */
                dataSource={roles} /**数据源 */
                columns={this.columns} /**列标题,及对应的数据源 */
                pagination={{defaultPageSize:PAGE_SIZE}} /**分页设置默认分页数量 */
                rowSelection={{type:'radio'}} /**第行前面加一个单选框antd文档找使用方法 */
                 />
            </Card>
        )
    }
}

效果 http://localhost:3000/role

在这里插入图片描述

2. api请求:获取所有角色列表

注:在请求前要先向数据库添加一些角色数据,可用getman,或直接数据库添加

1. api/index.js

//请求所有角色列表
export const reqRoles=()=>ajax(BASE+'/manage/role/list')

2. role/index.jsx

import {reqRoles} from '../../../api' //【1】

state={//【2】清空模拟数据
    roles:[], //所有角色列表:连接Table datasource
}

//【3】获取角色列表数据,设置到state中
    getRoles=async()=>{
        const result=await reqRoles()
        if(result.status===0){
            const roles=result.data //把返回值赋值给roles
            this.setState({
                roles //定义的变量和state的roles同名,所以简写
            })
        }
    }

componentDidMount(){
        this.getRoles() //【4】函数:获取角色列表设置到state中
    }

   

效果:同1步静态,但授权两项为空

http://localhost:3000/role
在这里插入图片描述

3.点击一行的任意位置选中单选框

onRow用法:https://3x.ant.design/components/table-cn/#onRow-用法
rowSelection用法:https://3x.ant.design/components/table-cn/#rowSelection

1.实现

【0】Table的onRow控制点击当前行的行为
【1】点击角色列表对应行的行为
【2】设置state:选中的role
【3】把当前点击的行赋值到state里的role
【4】取出state的role
【6】Table的selectedRowKeys根据4确定哪个是被选中状态;

state={
         roles:[], //所有角色列表:连接Table datasource
         role:{},//【2】选中的role
    }

    //【1】点击角色列表对应行的行为
    onRow=(role)=>{
        return{
            onClick: event => { //点击行时执行以下
                console.log('row onClick()', role)
                this.setState({ //【3】把当前点击的行赋值到state里的role
                    role
                })
            }
        }
    }


//render和return间
 const {roles,role}=this.state //【4】取出role
//return后:
<Card title={title}>
                <Table
                bordered /**边框 */
                rowKey='_id' /**指定数据主键 */
                dataSource={roles} /**数据源 */
                columns={this.columns} /**列标题,及对应的数据源 */
                pagination={{defaultPageSize:PAGE_SIZE}} /**分页设置默认分页数量 */
                rowSelection={{type:'radio',selectedRowKeys:[role._id]} } /**【6】selectedRowKeys根据4确定哪个是被选中状态(请求的数据里有一个:"_id": "5e6b147106c64115446c4fb2",);   第行前面加一个单选框antd文档找使用方法 */
                onRow={this.onRow} /**【0】控制点击当前行的行为 */
                 />
            </Card>

效果:点一行的任意地方都会选中单选框

在这里插入图片描述

2. 选中一行后,设置权限按钮变可点状态

const {roles,role}=this.state //【1】取出role

        //card的左侧 (Button的disabled:按钮不可用【2】如果_id不存在则按钮不可用)
        const title=(
            <span>
                <Button type='primary' style={{marginRight:8}}>创建角色</Button>
                <Button type='primary' disabled={!role._id}>设置角色权限</Button>
            </span>
        )
效果 点任意一行 设置角色权限按钮变如下

在这里插入图片描述

二、添加角色弹窗

1.引入modal弹窗

import {
    Card,
    Button,
    Table,
    Modal, //【1】弹窗
    message
} from 'antd'

state={
         roles:[], //所有角色列表:连接Table datasource
         role:{},//选中的role
         isShowAdd: false, //【2】是否显示添加界面
    }

//【render】

 //card的左侧 (Button的disabled:按钮不可用)
        const title=(
            <span>
                {/* 【3】点创建角色:显示创建角色的弹窗 */}
                <Button type='primary' style={{marginRight:8}} onClick={()=>{this.setState({isShowAdd:true})}}>创建角色</Button>
                <Button type='primary' disabled={!role._id}>设置角色权限</Button>
            </span>
        )
//【return】

 {/* 【4】弹窗 */}
                 <Modal 
                 title='添加角色'
                 visible={isShowAdd} /*弹窗可见状态*/
                 onOk={this.addRole} /*点ok提交信息*/
                 onCancel={()=>{this.setState({isShowAdd:false})}} /*点取消*/
                 >
                 </Modal>

2.创建角色页面把子组件form传给父组件index.jsx—当前role/addForm.jsx

import React,{Component} from 'react'
import{Form,Input} from 'antd'
import PropTypes from 'prop-types' //【1】传值模块

const Item =Form.Item

class AddForm extends Component{
    static propTypes={
        setForm:PropTypes.func.isRequired //【2】父组件传过来的接收子组件form对象的函数
    }

    componentWillMount () {
        //【3】运行接收到的父组件传过来函数,把form传给父组件
        this.props.setForm(this.props.form)
      }

    render(){
        // 取出form的表单验证方法
        const { getFieldDecorator } = this.props.form
        // 【2.1】指定Item布局的配置对象
        const formItemLayout = {
        labelCol: { span: 4 },  // 左侧label的宽度
        wrapperCol: { span: 15 }, // 右侧包裹的宽度
        }

        return(
            <Form>
                {/* 【2.2】把formItemLayout放入Item */}
                <Item label='角色名称' {...formItemLayout}>
                {
                    getFieldDecorator('roleName', {
                    initialValue: '',
                    rules: [
                        {required: true, message: '角色名称必须输入'}
                    ]
                    })(
                    <Input placeholder='请输入角色名称'/>
                    )
                }
                </Item>
            </Form>
        )
    }
}
export default Form.create()(AddForm) //包装AddForm组件使具体form相关方法

3.role/index.jsx向addForm.jsx发送setForm函数接收其传过来的from表单

{/* 弹窗 */}
<Modal 
title='添加角色'
visible={isShowAdd} /*弹窗可见状态*/
onOk={this.addRole} /*点ok提交信息*/
onCancel={()=>{this.setState({isShowAdd:false})}} /*点取消*/
>
{/* 【1】传递子组件form的函数setForm:(接收一个参数form,令当前组件的form=传过来的form*/}
    <AddForm setForm={(form) => this.form = form} />
</Modal>

4.添加角色api接口api/index.jsx

// 添加角色
export const reqAddRole=(roleName)=>ajax(BASE+'/manage/role/add',{roleName},'POST')

5.引入接口,并执行添加角色role/index.jsx

import {reqRoles,reqAddRole} from '../../../api' //【1】添加角色api

//state下
//【2】点添加角色弹窗的ok按钮:添加角色
    addRole=()=>{
        this.form.validateFields(async(err,value)=>{
            if(!err){
                console.log(value)
                //隐藏确认框
                this.setState({isShowAdd:false})
                //收集数据
                const {roleName}=value
                this.form.resetFields()//清空表单内数据,方便下次使用

                //添加角色请求
                const result=await reqAddRole(roleName)
                if(result.status===0){
                    message.success('角色添加成功')
                    //取出返回的新增role值
                    const role=result.data
                    //更新roles状态,使新增的角色显示出来(基于原本状态数据更新)
                    /*不建议的写法(虽然也能实现)
                    const roles = this.state.roles
				       roles.push(role)
				        this.setState({
				          roles
				        })*/
                    this.setState(state=>({
                        roles:[...state.roles,role]
                    }))
                    
                }else{
                    message.error('角色添加失败')
                }
            }
        })
    }

★知识点:setState的真正用法

this.setState((state,props)=>({
                        roles:[...state.roles,role]
                    }))

效果:http://localhost:3000/role

输入角色,点ok后提交角色,显示添加角色成功;点cacel关闭弹窗
在这里插入图片描述

优化点cancel后,再点创建角色上次的东西还在里面

this.form.resetFields()//【优化】取消时顺便清空表单方便下次使用

{/* 添加角色弹窗 */}
                 <Modal 
                 title='添加角色'
                 visible={isShowAdd} /*弹窗可见状态*/
                 onOk={this.addRole} /*点ok提交信息*/
                 onCancel={()=>{
                     this.setState({isShowAdd:false})
                     this.form.resetFields()//【优化】取消时顺便清空表单方便下次使用
                    }} /*点取消*/
                 >
                     {/* 传递子组件form的函数setForm:(接收一个参数form,令当前组件的form=传过来的form) */}
                     <AddForm setForm={(form) => this.form = form} />
                 </Modal>

三、设置角色权限

1.设置权限弹窗role/index.jsx

import AuthForm from './authForm' //【4】设置权限弹窗的表单

//class ...
state={
         roles:[], //所有角色列表:连接Table datasource
         role:{},//选中的role
         isShowAdd: false, //是否显示添加角色弹窗
         isShowAuth:false, //【0】是否显示设置权限弹窗
    }


//render 
const {roles,role,isShowAdd,isShowAuth}=this.state //【1】取出isShowAuth
//card的左侧 (Button的disabled:按钮不可用)
        const title=(
            <span>
                {/* 【3】点设置权限:显示对应弹窗onClick={()=>{this.setState({isShowAuth:true})}}*/}
                <Button type='primary' style={{marginRight:8}} onClick={()=>{this.setState({isShowAdd:true})}}>创建角色</Button>
                <Button type='primary' disabled={!role._id} onClick={()=>{this.setState({isShowAuth:true})}}>设置角色权限</Button>
            </span>
        )


//return内
{/* 【2】设置权限弹窗 */}
                 <Modal 
                 title='设置权限'
                 visible={isShowAuth} /*弹窗可见状态*/
                 onOk={this.addAuth} /*点ok提交信息*/
                 onCancel={()=>{
                     this.setState({isShowAuth:false})
                     
                    }} /*点取消*/
                 >
                     {/*  */}
                     <AuthForm  />
                 </Modal>

2.创建设置权限弹窗内容authForm.jsx

import React,{Component} from 'react'
import {Form,Input,Tree} from 'antd'

const Item=Form.Item

export default class AuthForm extends Component{


    render(){
        // 指定Item布局的配置对象
        const formItemLayout = {
            labelCol: { span: 4 },  // 左侧label的宽度
            wrapperCol: { span: 15 }, // 右侧包裹的宽度
        }
        return(
            <div>
                <Item label='角色名称' {...formItemLayout}>
                    <Input />
                </Item>
            </div>
        )
    }
}

效果:http://localhost:3000/role

在这里插入图片描述

3.显示选中的角色名称

1.role/index.jsx把role传递给authForm.jsx

//render
const {roles,role,isShowAdd,isShowAuth}=this.state //【1】娶出role

//return...
{/*【2】把role传递给子组件 */}
<AuthForm role={role}  />

2.authForm.jsx接收index传过来的role数据并显示

import PropTypes from 'prop-types' //【1】

//class ..
static propTypes={//【2】
        role:PropTypes.object
    }

//render
//【3】
const {role}=this.props

//return
<Item label='角色名称' {...formItemLayout}>
     {/* 【4】显示选中的角色名,并让它呈不可编辑的状态 */}
     <Input value={role.name} disabled />
</Item>
效果:http://localhost:3000/role

点设置角色权限
在这里插入图片描述

4.显示树形结构

antd Tree用法:https://3x.ant.design/components/tree-cn/

4.1显示静态tree

import menuList from '../../../config/menuConfig' //【1】导入菜单列表
const { TreeNode } = Tree //【2】拿出TreeNode
//class

//return
 {/* 【3】到antd复制一个tree进行修改 */}
 <Tree
     checkable
     defaultExpandAll={true} /*默认展开所有节点*/
 >
     <TreeNode title="parent 1" key="0-0">
     <TreeNode title="parent 1-0" key="0-0-0" >
         <TreeNode title="leaf" key="0-0-0-0" />
         <TreeNode title="leaf" key="0-0-0-1" />
     </TreeNode>
     <TreeNode title="parent 1-1" key="0-0-1">
         <TreeNode title='sss' key="0-0-1-0" />
     </TreeNode>
     </TreeNode>
 </Tree>

效果:

在这里插入图片描述

4.2 显示菜单列表Tree

import menuList from '../../../config/menuConfig' //【1】导入菜单列表
 //【2】获取菜单列表
    getTreeNodes=(menuList)=>{
        //代替map函数:reduce((初始值pre,当前正在处理的数组item)={},初始值[])
        return menuList.reduce((pre,item)=>{
            pre.push(
                <TreeNode title={item.title} key={item.key}>
                {/* 如果有children则调用本函数,把children再运行一次 */}
                {item.children ? this.getTreeNodes(item.children):null}
                </TreeNode>
            )
            return pre
        },[])
    }
    //【3】在页面加载前调用一次菜单
    componentWillMount(){
        this.treeNodes=this.getTreeNodes(menuList)
    }return{/* 到antd复制一个tree进行修改 */}
                <Tree
                    checkable
                    defaultExpandAll={true} /*默认展开所有节点*/
                >
                    {/* 【4】外面包个根节点,平台权限,内调用3步的treeNodes */}
                    <TreeNode title='平台权限' key='all'>
                        {this.treeNodes}
                    </TreeNode>
                    
                </Tree>

效果:

在这里插入图片描述

5.获取当前Tree的选中状态,及改变选中状态authForm.jsx

#【classconstructor(props){
        super(props)
        //【1】根据传入的角色生成初始状态
        const {menus} = this.props.role
        this.state={
            checkedKeys:menus
        }
    }

static propTypes={// 【0】接收父传值
        role:PropTypes.object
    }
    
//【5】更新Tree的选中状态
    onCheck=(checkedKeys)=>{
        console.log('oncheck:',checkedKeys)
        this.setState({checkedKeys})
    }
    
#【render()//【2】取出Tree要选中的节点
        const {checkedKeys}=this.state

#【return{/* 到antd复制一个tree进行修改 */}
                <Tree
                    checkable
                    defaultExpandAll={true} /*默认展开所有节点*/
                    checkedKeys={checkedKeys} /*【3】控制哪些节点为选中状态*/
                    onCheck={this.onCheck} /*【4】点击后更改选中状态*/
                >
                    {/* 外面包个根节点,平台权限,内调用3步的treeNodes */}
                    <TreeNode title='平台权限' key='all'>
                        {this.treeNodes}
                    </TreeNode>
                    
                </Tree>

效果:选中节点后状态会对应变化

在这里插入图片描述

6. 更新角色(父子传值等)

1.role/index.jsx更新角色

import {reqRoles,reqAddRole,reqUpdateRole} from '../../../api' //【7】引入更新角色函数requpdaterole;   添加角色api
//class
constructor (props) {
        super(props)
        //【1】创建一个auth的ref用于父子组件传值
        this.auth = React.createRef()
      }

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

        const role=this.state.role
        //【5】得到最新的menus => 到authForm.jsx里传值过来(getMenus = () => this.state.checkedKeys)
        const menus=this.auth.current.getMenus()
        //【6】把接收过来的菜单传给当前role.menus => 到api/index.js里写更新角色接口函数
        role.menus=menus 
        //【8】发送更新请求
        console.log(role)
        const result = await reqUpdateRole(role)
        if (result.status===0){
            message.success('设置角色权限成功!')
            this.getRoles()
        }else{
            message.error('更新角色权限失败')
        }


    }

//render()
//return
 {/* 设置权限弹窗 */}
                 <Modal 
                 title='设置权限'
                 visible={isShowAuth} /*弹窗可见状态*/
                 onOk={this.updateRole} /*【3】点ok提交信息*/
                 onCancel={()=>{this.setState({isShowAuth:false})}} /*点取消*/
                 >
                     {/*【2】把this.auth传给子组件 把role传递给子组件 */}
                     <AuthForm ref={this.auth} role={role}  />
                 </Modal>

2. authForm.jsx传值

//【1】为父组件提交获取最新menus数据的方法:把state.checkedKeys传给父组件
  getMenus = () => this.state.checkedKeys

3.api/index.js接口

// 更新角色,传过来的参数就是字典格式,所以role参数不用加花括号
export const reqUpdateRole=(role)=>ajax(BASE+'/manage/role/update',role,'POST')
效果:

勾选后会设置权限成功
在这里插入图片描述

7.优化:当更新过一个角色权限后,其它的角色会变的跟这个角色一样,原因是:收到父组件传的props值后,state内数据没更新authForm.jsx

    //【2】根据新传入的role来更新checkedKeys状态当组件接收到新的属性时自动调用
  componentWillReceiveProps (nextProps) {
    console.log('componentWillReceiveProps()', nextProps)
    const menus = nextProps.role.menus
    this.setState({
      checkedKeys: menus
    })
    // this.state.checkedKeys = menus //也可以这样写
  }

效果:更新一个角色后,其它角色不会变的跟当前一样

8.授权时间,授权人优化role/index.jsx

【2】添加授权时间及授权人

import memoryUtils from '../../../utils/memoryUtils' //【1】引入记忆模块用于显示用户名

// 更新角色:点设置权限弹窗里的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 

        //【2】添加授权时间及授权人
        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('更新角色权限失败')
        }


    }

效果:设置权限后,会显示对应时间及授权人

在这里插入图片描述

9.把时间戳格式化成字符串形式方便人类查看

0.附:时间格式函数 utils/dateutils.js

//包含n个日期时间处理的工具函数模块

 //格式化日期
export function formateDate(time) {
    if (!time) return ''
    let date = new Date(time)
    return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
      + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds()
  }

1.格式化时间role/index.jsx

全写:render:(create_time)=>formateDate(create_time)
简写:render:formateDate

import {formateDate} from '../../../utils/dateUtils' //【1】时间格式化

//初始化表格列标题,及对应的数据源,dataIndex:对应api返回的数据名
    initColumns=()=>{
        //【2】调用函数格式化时间戳
        this.columns=[
            {title:'角色名称',dataIndex:'name'},
            {title:'创建时间',dataIndex:'create_time',render:(create_time)=>formateDate(create_time)},
            
            {title:'授权时间',dataIndex:'auth_time',render:formateDate},
            {title:'授权人',dataIndex:'auth_name'},           
        ]
    }

效果:时间会变成可读

在这里插入图片描述

附件:完整代码/role/

index.jsx

import React,{Component} from 'react'
import {
    Card,
    Button,
    Table,
    Modal, //弹窗
    message
} from 'antd'
import {PAGE_SIZE} from '../../../utils/constans'
import {reqRoles,reqAddRole,reqUpdateRole} from '../../../api' //引入更新角色函数requpdaterole;   添加角色api
import AddForm from './addForm' //添加角色弹窗的表单
import AuthForm from './authForm' //设置权限弹窗的表单
import memoryUtils from '../../../utils/memoryUtils' //引入记忆模块用于显示用户名
import {formateDate} from '../../../utils/dateUtils' //【1】时间格式化


export default class Role extends Component{
    
    constructor (props) {
        super(props)
        //创建一个auth的ref用于父子组件传值
        this.auth = React.createRef()
      }

    state={
         roles:[], //所有角色列表:连接Table datasource
         role:{},//选中的role
         isShowAdd: false, //是否显示添加角色弹窗
         isShowAuth:false, //是否显示设置权限弹窗
    }

    //点击角色列表对应行的行为
    onRow=(role)=>{
        return{
            onClick: event => { //点击行时执行以下
                console.log('row onClick()', role)
                this.setState({ //把当前点击的行赋值到state里的role
                    role
                })
            }
        }
    }

    //获取角色列表数据,设置到state中
    getRoles=async()=>{
        const result=await reqRoles()
        if(result.status===0){
            const roles=result.data
            this.setState({
                roles
            })
        }
    }

    //初始化表格列标题,及对应的数据源,dataIndex:对应api返回的数据名
    initColumns=()=>{
        //【2】调用函数格式化时间戳
        this.columns=[
            {title:'角色名称',dataIndex:'name'},
            {title:'创建时间',dataIndex:'create_time',render:(create_time)=>formateDate(create_time)},
            {title:'授权时间',dataIndex:'auth_time',render:formateDate},
            {title:'授权人',dataIndex:'auth_name'},           
        ]
    }
    //点添加角色弹窗的ok按钮:添加角色
    addRole=()=>{
        this.form.validateFields(async(err,value)=>{
            if(!err){
                console.log(value)
                //隐藏确认框
                this.setState({isShowAdd:false})
                //收集数据
                const {roleName}=value
                this.form.resetFields()//清空表单内数据,方便下次使用

                //添加角色请求
                const result=await reqAddRole(roleName)
                if(result.status===0){
                    message.success('角色添加成功')
                    //取出返回的新增role值
                    const role=result.data
                    //更新roles状态,使新增的角色显示出来(基于原本状态数据更新)
                    this.setState(state=>({
                        roles:[...state.roles,role]
                    }))
                    
                }else{
                    message.error('角色添加失败')
                }
            }
        })
    }


    // 更新角色:点设置权限弹窗里的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 

        //【2】添加授权时间及授权人
        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('更新角色权限失败')
        }


    }

    componentWillMount(){
        this.initColumns() //函数:运行初始表格列标题,及对应的数据源函数,把表格列数据赋值到this.columus上
    }

    componentDidMount(){
        this.getRoles() //函数:获取角色列表设置到state中
    }


    render(){
        const {roles,role,isShowAdd,isShowAuth}=this.state //娶出role; 取出isShowAuth

        //card的左侧 (Button的disabled:按钮不可用)
        const title=(
            <span>
                {/* 点创设置权限:显示对应弹窗;      点创建角色:显示创建角色的弹窗 */}
                <Button type='primary' style={{marginRight:8}} onClick={()=>{this.setState({isShowAdd:true})}}>创建角色</Button>
                <Button type='primary' disabled={!role._id} onClick={()=>{this.setState({isShowAuth:true})}}>设置角色权限</Button>
            </span>
        )
                
        return(
            <Card title={title}>
                <Table
                bordered /**边框 */
                rowKey='_id' /**表格行 key 的取值,可以是字符串或一个函数 */
                dataSource={roles} /**数据源 */
                columns={this.columns} /**列标题,及对应的数据源 */
                pagination={{defaultPageSize:PAGE_SIZE}} /**分页设置默认分页数量 */
                rowSelection={{type:'radio',selectedRowKeys:[role._id]} } /**selectedRowKeys根据4确定哪个是被选中状态;   第行前面加一个单选框antd文档找使用方法 */
                onRow={this.onRow} /**控制点击当前行的行为 */
                 />

                {/* 添加角色弹窗 */}
                 <Modal 
                 title='添加角色'
                 visible={isShowAdd} /*弹窗可见状态*/
                 onOk={this.addRole} /*点ok提交信息*/
                 onCancel={()=>{
                     this.setState({isShowAdd:false})
                     this.form.resetFields()//取消时顺便清空表单方便下次使用
                    }} /*点取消*/
                 >
                     {/* 传递子组件form的函数setForm:(接收一个参数form,令当前组件的form=传过来的form) */}
                     <AddForm setForm={(form) => this.form = form} />
                 </Modal>


                 {/* 设置权限弹窗 */}
                 <Modal 
                 title='设置权限'
                 visible={isShowAuth} /*弹窗可见状态*/
                 onOk={this.updateRole} /*点ok提交信息*/
                 onCancel={()=>{this.setState({isShowAuth:false})}} /*点取消*/
                 >
                     {/*把this.auth传给子组件 把role传递给子组件 */}
                     <AuthForm ref={this.auth} role={role}  />
                 </Modal>
            </Card>
        )
    }
}

2.addForm.jsx

import React,{Component} from 'react'
import{Form,Input} from 'antd'
import PropTypes from 'prop-types' //【1】传值模块

const Item =Form.Item

class AddForm extends Component{
    static propTypes={
        setForm:PropTypes.func.isRequired //【2】父组件传过来的接收子组件form对象的函数
    }

    componentWillMount () {
        //【3】运行接收到的父组件传过来函数,把form传给父组件
        this.props.setForm(this.props.form)
      }

    render(){
        // 取出form的表单验证方法
        const { getFieldDecorator } = this.props.form
        // 【2.1】指定Item布局的配置对象
        const formItemLayout = {
        labelCol: { span: 4 },  // 左侧label的宽度
        wrapperCol: { span: 15 }, // 右侧包裹的宽度
        }

        return(
            <Form>
                {/* 【2.2】把formItemLayout放入Item */}
                <Item label='角色名称' {...formItemLayout}>
                {
                    getFieldDecorator('roleName', {
                    initialValue: '',
                    rules: [
                        {required: true, message: '角色名称必须输入'}
                    ]
                    })(
                    <Input placeholder='请输入角色名称'/>
                    )
                }
                </Item>
            </Form>
        )
    }
}
export default Form.create()(AddForm) //包装AddForm组件使具体form相关方法

3.authForm.jsx

import React,{Component} from 'react'
import {Form,Input,Tree} from 'antd'
import PropTypes from 'prop-types' //父子传值 
import menuList from '../../../config/menuConfig' //导入菜单列表

const Item=Form.Item
const { TreeNode } = Tree //拿出TreeNode

export default class AuthForm extends Component{
    constructor(props){
        super(props)
        //根据传入的角色生成初始状态
        const {menus} = this.props.role
        this.state={
            checkedKeys:menus
        }
    }

    static propTypes={// 接收父传值
        role:PropTypes.object
    }

    //获取菜单列表
    getTreeNodes=(menuList)=>{
        //代替map函数:reduce((初始值pre,当前正在处理的数组item)={},初始值[])
        return menuList.reduce((pre,item)=>{
            pre.push(
                <TreeNode title={item.title} key={item.key}>
                {/* 如果有children则调用本函数,把children再运行一次 */}
                {item.children ? this.getTreeNodes(item.children):null}
                </TreeNode>
            )
            return pre
        },[])
    }

    //更新Tree的选中状态
    onCheck=(checkedKeys)=>{
        console.log('oncheck:',checkedKeys)
        this.setState({checkedKeys})
    }


    
  //【1】为父组件提交获取最新menus数据的方法:把state.checkedKeys传给父组件
  getMenus = () => this.state.checkedKeys



    //在页面加载前调用一次菜单
 componentWillMount(){
        this.treeNodes=this.getTreeNodes(menuList)
    }

    //【2】根据新传入的role来更新checkedKeys状态当组件接收到新的属性时自动调用
  componentWillReceiveProps (nextProps) {
    console.log('componentWillReceiveProps()', nextProps)
    const menus = nextProps.role.menus
    this.setState({
      checkedKeys: menus
    })
    // this.state.checkedKeys = menus
  }



    render(){
        //取出Tree要选中的节点
        const {checkedKeys}=this.state
        // 取出role
        const {role}=this.props

        // 指定Item布局的配置对象
        const formItemLayout = {
            labelCol: { span: 4 },  // 左侧label的宽度
            wrapperCol: { span: 15 }, // 右侧包裹的宽度
        }
        return(
            <div>
                <Item label='角色名称' {...formItemLayout}>
                    {/* 显示选中的角色名,并让它成不可编辑的状态 */}
                    <Input value={role.name} disabled />
                </Item>

                {/* 到antd复制一个tree进行修改 */}
                <Tree
                    checkable
                    defaultExpandAll={true} /*默认展开所有节点*/
                    checkedKeys={checkedKeys} /*控制哪些节点为选中状态*/
                    onCheck={this.onCheck} /*点击后更改选中状态*/
                >
                    {/* 外面包个根节点,平台权限,内调用3步的treeNodes */}
                    <TreeNode title='平台权限' key='all'>
                        {this.treeNodes}
                    </TreeNode>
                    
                </Tree>
            </div>
        )
    }
}

3.utils/memoryUtils.jsx

/*
用于在内存中保存数据的工具模块
*/
export default{
    user:{},
}

4.api/index.js

import ajax from './ajax'
import jsonp from 'jsonp'
import {message} from 'antd' //借用antd返回信息组件
// const BASE = 'http://localhost:5000'
const BASE = ''

//导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
//     return ajax('login',{username,password},'POST')
// }

//导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')


//获取产品一级/二级分类列表接口
export const reqCategorys=(parentId)=>ajax(BASE+'/manage/category/list',{parentId})
//添加产品分类接口
export const reqAddCategory=(parentId,categoryName)=>ajax(BASE+'/manage/category/add',{parentId,categoryName},'POST')
//修改产品分类接口
export const reqUpdateCategory=({categoryId,categoryName})=>ajax(BASE+'/manage/category/update',{categoryId,categoryName},'POST')
//根据分类Id获取一个分类
export const reqCategory = (categoryId) => ajax(BASE + '/manage/category/info', {categoryId})
//获取产品列表
export const reqProducts=(pageNum,pageSize)=>ajax(BASE+'/manage/product/list',{pageNum,pageSize})
//产品上下架
export const reqUpdateStatus=(productId,status)=>ajax(BASE+'/manage/product/updateStatus',{productId,status},'POST')

/*搜索商品分页列表 (根据商品名称/商品描述)
searchType(搜索的类型): productName/productDesc*/
export const reqSearchProducts = ({pageNum, pageSize, searchName, searchType}) => ajax(BASE + '/manage/product/search', {
    pageNum,
    pageSize,
    [searchType]: searchName,
  })
  
//添加商品/修改商品:二合一接口,如果参数存在._id则为修改商品,否则为添加商品
export const reqAddUpdatePro=(product)=>ajax(BASE+'/manage/product/'+(product._id?'update':'add'),product,'POST')


// 删除服务器上指定名称图片
export const reqDeletPic=(name)=>ajax(BASE+'/manage/img/delete',{name},'POST')


//请求所有角色列表
export const reqRoles=()=>ajax(BASE+'/manage/role/list')


// 添加角色
export const reqAddRole=(roleName)=>ajax(BASE+'/manage/role/add',{roleName},'POST')
// 更新角色,传过来的参数就是字典格式,所以role参数不用加花括号
export const reqUpdateRole=(role)=>ajax(BASE+'/manage/role/update',role,'POST')


// 天气接口
export const reqWeather=(city) => {    
    const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
    //返回一个promise函数
    return new Promise((resolve,reject) => {
        //发送一个jsonp请求
        jsonp(url,{},(err,data) => {
            //输出请求的数据到控制台
            console.log('jsonp()', err, data)
            //如果请求成功
            if(!err && data.status==='success'){
                //从数据中解构取出图片、天气
                const {dayPictureUrl,weather}=data.results[0].weather_data[0]
                //异步返回图片、天气给调用函数者
                resolve({dayPictureUrl,weather})
            }else{//如果请求失败
                message.error('天气信息获取失败')
            }
        })
    })
}
//reqWeather('上海')

5. utils/storageUtils.js

/*
进行local数据存储管理的工具模块
 */
import store from 'store'
const USER_KEY = 'user_key'
export default {
  /*保存user*/
  saveUser (user) {
    // localStorage.setItem(USER_KEY, JSON.stringify(user))
    store.set(USER_KEY, user)
  },

  /*读取user*/
  getUser () {
    // return JSON.parse(localStorage.getItem(USER_KEY) || '{}')
    return store.get(USER_KEY) || {}
  },

  /*删除user*/
  removeUser () {
    // localStorage.removeItem(USER_KEY)
    store.remove(USER_KEY)
  }
}

效果

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值