导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1. 初始化项目
  • 2. 配置别名
  • 3. 注册登录
    • 3.1 login/index.js
      • 3.1.1 Login
      • 3.1.2 LoginForm
      • 3.1.3 getFieldItems.js
      • 3.1.4 addresses.js
      • 3.1.4 utils/request.js
    • 3.3 models/login.js
    • 3.4 login/services/login.js
  • 4.验证码
    • 4.1 login/index.js
  • 5.后台管理界面布局模版
    • 5.1 /admin/_layout.js
  • 6. 顶部导航条
    • 6.1 components\AdminHeader\index.js
    • 6.2 components\AdminHeader\index.less
    • 6.3 admin/_layout.js
  • 7.左侧菜单和用户页面
    • 7.1 components/MenuList.js
    • 7.2 admin/_layout.js
  • 8. 用户管理
    • 8.1 用户列表
      • 8.1.1 index.js
      • 8.1.2 constants.js
      • 8.1.3 models\user.js
      • 8.1.4 services\user.js
    • 8.2 新增和编辑用户
      • 8.1.1 index.js
      • 8.1.2 models/user.js
      • 8.1.3 services/user.js
    • 8.3 删除用户
      • 8.3.1 index.js
      • 8.3.2 models\user.js
      • 8.3.3 services\user.js
    • 8.4 全部删除
      • 8.4.1 src\pages\admin\user\index.js
      • 8.4.2 user.js
      • 8.4.3 services\user.js
    • 8.5 搜索
      • 8.5.1 src\pages\admin\user\index.js
      • 8.5.2 user.js
      • 8.5.3 services\user.js
    • 8.6 点行选择行
      • 8.6.1 src\pages\admin\user\index.js
  • 9 角色管理
    • 9.1 role\index.js
    • 9.2 role\constants.js
    • 9.3 models\role.js
    • 9.4 services\role.js
  • 10. 设置权限
    • 10.1 service\role.js
    • 10.2 role/index.js
    • 10.3 models\role.js
    • 10.4 role.js
  • 11.给角色加用户
    • 11.1 roles/index.js
    • 11.2 models/roles.js
    • 11.3 services/roles.js

1. 初始化项目 #

  • create-umi-app
  • ant.design
$ mkdir myapp && cd myapp
$ yarn create umi
$ cnpm i styled-components jsonwebtoken -S
$ npm start

2. 配置别名 #

.webpackrc.js

const {resolve}=require('path');
export default {
    "alias":{
        "@":resolve("src")
    }
}

3. 注册登录 #

注册

3.1 login/index.js #

3.1.1 Login #

import React,{Component} from 'react';
import {Layout,Form,Input,Radio,Cascader,Select,AutoComplete,Checkbox,Button,message} from 'antd';
import styled from 'styled-components';
import {connect} from 'dva';//react-redux 是用来连接仓库和组件
import addresses from '../../utils/addresses';
import getFieldItems from '../../utils/getFieldItems';
const { Header, Footer, Sider, Content } = Layout;
const FormItem = Form.Item;
const {Option} = Select;
class Login extends Component{
    handleSubmit=event => {
        event.preventDefault();
        this.userForm.props.form.validateFields((err,values) => {
                if (err) {message.error('输入不合法!');
                } else {
                    this.props.dispatch({type: this.props.isLogin?'login/login':'login/signup',payload:values});
                }
        });
    }
    changeLoginStatus = ()=>{
        this.props.dispatch({type:'login/save',payload:{isLogin:!this.props.isLogin}});
    }
   render(){
       return (
           <Layout>
            <Content>
              <LoginForm
                 isLogin={this.props.isLogin}
                 changeLoginStatus={this.changeLoginStatus}
                 handleSubmit={this.handleSubmit}
                 wrappedComponentRef={inst => this.userForm=inst}
                 handleSubmit={this.handleSubmit}
              />
            </Content>
           </Layout>
       )
   }
}

3.1.2 LoginForm #

class LoginForm extends Component{
    state ={gender:1,autoCompleteResult:[],repasswordDirty:false}
    handleWebsiteChange = (value)=>{//1 [1.com,1.cn,1.org]
      let autoCompleteResult=[];
      if(value){
        autoCompleteResult = [".com",".cn",".org"].map(domain=>value+domain);
      }
      this.setState({autoCompleteResult});
    }
    compareWithRepassword = (rule,value,callback)=>{
        const form = this.props.form;
        if(value&& this.state.repasswordDirty){
            form.validateFields(['repassword'],{force:true});
        }
        callback();
    }
    compareWithPassword = (rule,value,callback)=>{//自定义校验器 规则 最新的值 回调
       const form = this.props.form;
       if(value && value !== form.getFieldValue('password')){
            callback('密码和确认密码不一致');
       }else{
           callback();
       }
    }
    repasswordChange = (event)=>{
        this.setState({repasswordDirty:this.state.repasswordDirty||event.target.value.length>0});
    }
    render(){
        //一旦把组件用Form.create包裹
        let {form:{getFieldDecorator},isLogin,handleSubmit} = this.props;
        let formTailItemLayout = {
            wrapperCol:{offset:4,span:20}
        }
        let countrySelector = getFieldDecorator('prefix',{
            initialValue:'086'
        })(
            <Select style={{width:70}}>
                <Option value="086">086</Option>
                <Option value="087">087</Option>
                <Option value="088">088</Option>
            </Select>
        );
        let websiteOptions = this.state.autoCompleteResult.map(item=>(
            <AutoComplete.Option key={item}>{item}</AutoComplete.Option>
        ));
        let websiteField = (
            <AutoComplete onChange={this.handleWebsiteChange}>
                {websiteOptions}
            </AutoComplete>
        )
        let genderField = (
            <Radio.Group >
                    <Radio value={1}>男</Radio>
                    <Radio value={0}>女</Radio>
            </Radio.Group>
        )
        let filedItems = getFieldItems(getFieldDecorator,[
            {visible:true,label:"用户名",name:"username",required:true,input:<Input/>},
            {visible:true,label:"密码",name:"password",required:true,input:<Input onChange={this.repasswordChange}/>,rules:[
                {validator:this.compareWithRepassword},
                {min:1,message:'密码长度最短1位'},
                {max:8,message:'密码长度最长8位'}
            ]},
            {visible:!isLogin,label:"确认密码",name:"repassword",required:false,input:<Input/>,rules:[
                {validator:this.compareWithPassword}
            ]},
            {visible:!isLogin,label:"邮箱",name:"email",required:true,input:<Input/>,rules:[{type:'email',message:'必须输入一个合法的邮箱!'}]},
            {visible:!isLogin,label:"性别",name:"gender",required:true,input:genderField,extra:{initialValue:1}},
            {visible:!isLogin,label:"住址",name:"address",required:true,input:<Cascader options={addresses}/>},
            {visible:!isLogin,label:"手机号",name:"phone",required:true,rules:[{pattern:/^1\d{10}$/,message:'请输入合法手机号'}],input:<Input addonBefore={countrySelector} style={{width:'100%'}}/>},
            {visible:!isLogin,label:"网址",name:"website",input:websiteField},
            {visible:!isLogin,name:"agreement",layout:formTailItemLayout,input:<Checkbox>我已经同意本协议</Checkbox>,extra:{valuePropName:'checked'}}
        ]);
        return (
            <FormWrapper>
                <Form style={{width:'500px'}} onSubmit={handleSubmit}>
                    <h3>欢迎{isLogin?"登录":"注册"}</h3>
                     {filedItems}
                     <FormItem>
                       <Button type="primary" htmlType="submit" style={{width:'100%'}}>{isLogin?"登录":"注册"}</Button>
                        已有账号?<a href="#" onClick={this.props.changeLoginStatus}>立刻{isLogin?"注册":"登录"}</a>
                    </FormItem>
                </Form>
            </FormWrapper>
        )
    }
}
LoginForm  = Form.create()(LoginForm);
const FormWrapper = styled.div`
  display:flex;
  justify-content:center;
  align-items:center;
  height:calc(100vh - 70px );
  h3{
      text-align:center;
  }
  form{
      border:1px solid #999;
      border-radius:5px;
      padding:20px;
  }
`
//一般来说在dva里,一个页面路由组件会对应一个子状态 login
export default connect(
    state=>state.login
)(Login);

3.1.3 getFieldItems.js #

src\utils\getFieldItems.js

import {Form} from 'antd';
const FormItem = Form.Item;
let formItemLayout = {
   labelCol:{span:4},wrapperCol:{span:20}
}
function getFieldItems(getFieldDecorator,fields){
    //{label,name,rules,input}
   return fields.filter(field=>field.visible).map((field,index)=>{
       let layout = field.layout?field.layout:formItemLayout;
       field.extra = field.extra||{};
       field.rules = field.rules||[];
       return (
           <FormItem key={index} label={field.label} {...layout}>
            {
                        getFieldDecorator(field.name,{
                            rules:[{required:field.required,message:`${field.label}必须输入`},...field.rules],
                            ...field.extra
                        })(field.input)
                      }
           </FormItem>
       )
   });
}
export default getFieldItems;

3.1.4 addresses.js #

src\utils\addresses.js

export default  [
            {
                value:'guangdong',
                label: '广东',
                children: [
                    {value:'guangzhou',label: '广州',},
                    {value:'dongguan',label: '东莞',}
                ]
            },
            {
                value:'shandong',
                label: '山东',
                children: [
                    {value:'jinan',label: '济南'},
                    {value:'shouguang',label: '寿光',}
                ]
            }
        ]

3.1.4 utils/request.js #

src/utils/request.js

import fetch from 'dva/fetch';
const BASE_URL = 'http://127.0.0.1:7001';
export default function (url, options={}) {
  let token = localStorage.getItem('token');
  options.headers = options.headers || {};
  if (token) {
    options.headers.authorization = token;
  }
  options.method = options.method || 'GET';
  options.headers["Content-Type"] = "application/json";
  options.headers["Accept"] = "application/json";
  options.credentials = 'include';
  return fetch(BASE_URL + url,options).then(res => res.json());
}

3.3 models/login.js #

src/pages/login/models/login.js

import * as service from '../services/login';
import {decode} from 'jsonwebtoken';
import {routerRedux} from 'dva/router';
import {message} from 'antd';
export default {
  namespace:'login',
  state:{
      isLogin:true,//是否正在注册
      errorInfo:'',//放着报错信息
      userInfo:null//如果用户登录之后,会把用户信息放在这里
  },
  effects:{
      *signup({payload},{call,put}){
        let result = yield call(service.signup,payload);
        if(result.code === 0){
         yield put({type:'save',payload:{isLogin:true}});
        }else{
          message.error('注册失败!');
        }
      },
      *login({payload},{call,put}){
        const  result = yield call(service.login,payload);
        if(result.code === 0){
          const userInfo = decode(result.data);
          yield put({type:'save',payload:{userInfo}});
          localStorage.setItem('token',result.data);
          yield put(routerRedux.push('/admin'));
        }else{
          message.error('登录失败!');
        }
      },
      *loadUser({payload},{call,put}) {
            let token=localStorage.getItem('token');
            if (token) {
                const userInfo = decode(token);
                yield put({type:'save',payload:{userInfo}});
            } else {
                yield put(routerRedux.push('/login'));
            }
        }
  },
  reducers:{
    save(state,action){
      return {...state,...action.payload};
    }
  }
}

3.4 login/services/login.js #

src/pages/login/services/login.js

import request from '../../../utils/request';

//定义一个注册的方法,调用api接口
export function signup(payload){
  return request('/api/signup',{
      method:'POST',
      body:JSON.stringify(payload)
  });
}
//定义一个登陆的方法
export function login(payload) {
  return request(`/api/signin`,{
    method: 'POST',
    body:JSON.stringify(payload)
  });
}

4.验证码 #

4.1 login/index.js #

src/pages/login/index.js LoginForm

+ import getFieldItems,{formItemLayout} from '../../utils/getFieldItems';

+ const captchaUrl=`http://127.0.0.1:7001/api/captcha?ts=`;

+ refreshCaptcha = (event)=>{
+     event.target.src = captchaUrl+Date.now();
+ }

+ <FormItem label="验证码" {...formItemLayout} extra="证明你不是机器人">
+      <Row gutter={8}>
+           <Col span={12}>
+               {
+                   getFieldDecorator('captcha',{
+                       rules: [{required:true,message:'必须输入验证码'}]
+                   })(<Input />)
+               }
+           </Col>
+           <Col span={12}>
+                  <img src={captchaUrl} onClick={this.refreshCaptcha}/>
+           </Col>
+     </Row>
+ </FormItem>

5.后台管理界面布局模版 #

5.1 /admin/_layout.js #

src/pages/admin/_layout.js

import React,{Component,Fragment} from 'react';
import { Layout } from 'antd';
const { Header, Footer, Sider, Content } = Layout;
export default class Admin extends Component{
    render() {
        return (
            <Layout>
                <Header>Header</Header>
                <Layout>
                    <Sider>Sider</Sider>
                    <Content>{this.props.children}</Content>
                </Layout>
                <Footer>
                   前端培训 ©2018
                </Footer>
            </Layout>
       )
   }
}

6. 顶部导航条 #

adminheader

6.1 components\AdminHeader\index.js #

src\components\AdminHeader\index.js

import React,{Component} from 'react';
import {Layout} from 'antd';
import {connect} from 'dva';
import styles from './index.less';
const {Header}=Layout;
class AdminHeader extends Component{
  componentWillMount() {
      this.props.dispatch({type:'login/loadUser'});
  }
  render() {
    let {userInfo}=this.props;
      return (
        <Header className={styles.header}>
          <img className={styles.logo} src="http://upload-markdown-images.oss-cn-beijing.aliyuncs.com/zfpxlogo.png" alt="logo"/>
          <span className={styles.welcome}>欢迎登录 {userInfo&&userInfo.username}</span>
        </Header>
      )
  }
}
export default connect(
  state => state.login
)(AdminHeader);

6.2 components\AdminHeader\index.less #

src\components\AdminHeader\index.less

.logo{
    width:120px;
    height:32px;
    margin:16px;
    float:left;
}
.welcome{
    float:right;
    color:#FFF;
}

6.3 admin/_layout.js #

src/pages/admin/_layout.js

import React,{Component} from 'react';
import { Layout } from 'antd';
import AdminHeader from '../../components/AdminHeader';
const {Footer,Sider,Content}=Layout;
export default class Admin extends Component{
    render() {
        return (
            <Layout>
                <AdminHeader/>
                <Layout>
                    <Sider>Sider</Sider>
                    <Content>{this.props.children}</Content>
                </Layout>
                <Footer>
                   前端架构 ©2018
                </Footer>
            </Layout>
       )
   }
}

7.左侧菜单和用户页面 #

resources

leftmenus

7.1 components/MenuList.js #

src/components/MenuList.js

import React,{Component,Fragment} from 'react';
import {Menu,Icon} from 'antd';
import Link from 'umi/link';
import {connect} from 'dva';
const SubMenu=Menu.SubMenu;
class MenuList extends Component{
    renderMenus=(resources=[]) => {
        return resources.map(resource => {
            if (resource.children.length>0) {
                return (
                    <SubMenu key={resource.key} title={<span><Icon type={resource.icon} />{resource.name}</span>}>
                        {this.renderMenus(resource.children)}
                    </SubMenu>
                )    
            } else {
                return <Menu.Item key={resource.key}><Link to={resource.key}><Icon type={resource.icon} />{resource.name}</Link></Menu.Item>;
            }
        });
    }
    render() {
        let {userInfo}=this.props;
        if (!userInfo)
            return null;
        return (
            <Menu
                theme="dark"
                defaultSelectedKeys={['/admin']}
                defaultOpenKeys={['/admin']}
                mode="inline"
            >
                {
                    this.renderMenus(userInfo.resources)
                }
            </Menu>
        )
    }
}
export default connect(
    state => state.login
  )(MenuList);

app\controller\user.js

   let list = await app.mysql.query(`SELECT resource.* FROM role_user,role_resource,resource where role_user.role_id = role_resource.role_id AND role_resource.resource_id = resource.id AND role_user.user_id = ? ORDER BY resource.id ASC`,[user.id]);
      let resources = [];
      let map = {};
      list.forEach(item => {
          item.children = [];
          map[item.id] = item;
          if (item.parent_id == 0) {
            resources.push(item);
          } else {
            map[item.parent_id].children.push(item);
          }
      });
      user.resources=resources;

7.2 admin/_layout.js #

src/pages/admin/_layout.js

import React,{Component} from 'react';
import { Layout } from 'antd';
import AdminHeader from '../../components/AdminHeader';
import MenuList from '../../components/MenuList';
const {Footer,Sider,Content}=Layout;
export default class Admin extends Component{
    render() {
        return (
            <Layout>
                <AdminHeader/>
                <Layout>
                    <Sider>
                        <MenuList/>
                    </Sider>
                    <Content>
                        {this.props.children}
                    </Content>
                </Layout>
                <Footer>
                   前端培训 ©2018
                </Footer>
            </Layout>
       )
   }
}

8. 用户管理 #

8.1 用户列表 #

8.1.1 index.js #

src\pages\admin\user\index.js

import React,{Component,Fragment} from 'react';
import {connect} from 'dva';
import {Card,Table,Button,Modal,Form,Input,message,Popconfirm} from 'antd';
import {PAGE_SIZE} from './constants';
import { routerRedux } from 'dva/router';
const FormItem=Form.Item;

export default 
@connect(
    state => ({...state.user,loading:state.loading.models.user})
) 
class user extends Component{
     render(){
          const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '性别',
                dataIndex: 'gender',
                key: 'gender',
                render: (val,record)=>(
                    val===1?'男':'女'
                )
            }
        ]
        let {list,loading,pageNum,total,dispatch}=this.props;
        const pagination={
            current: pageNum,
            pageSize: PAGE_SIZE,
            showQuickJumper: true,
            showTotal: (total,range) => {
                return `共${total}条`;
            },
            total,
            onChange: (pageNum) => {
                dispatch(routerRedux.push(`/admin/user?pageNum=${pageNum}`));
            }
        }
         return (
             <Card>
                <Table
                        columns={columns}
                        dataSource={list}
                        loading={loading}
                        rowKey={record => record.id}
                        pagination={pagination}
                />
            </Card>
         )
     }
}

8.1.2 constants.js #

src\pages\admin\user\constants.js

export const PAGE_SIZE = 3;

8.1.3 models\user.js #

src\pages\admin\user\models\user.js

import * as userService from '../services/user';

export default {
    namespace: 'user',
    state: {
        list: [],
        pageNum:1,
        total:0
    },
    reducers: {
        save(state,{payload}) {
            return {...state,...payload};
        }
    },
    effects: {
        *fetch({payload: {pageNum=1}},{call,put}) {
            const result=yield call(userService.fetch,pageNum);
            debugger;
            if(result.code === 0){
               let { list,total} = result.data;
               yield put({type:'save',payload:{list,pageNum:parseInt(pageNum),total}});
            }
        }
    },
    subscriptions: {
        setup({dispatch,history}) {
            return history.listen(({pathname,query}) => {
                if (pathname==='/admin/user') {
                    dispatch({type:'fetch',payload:query});
                }
            });
        }
    }
}

8.1.4 services\user.js #

src\pages\admin\user\services\user.js

import request from '@/utils/request';
import {PAGE_SIZE} from '../constants';
export function fetch(pageNum) {
    return request(`/api/user?pageNum=${pageNum}&pageSize=${PAGE_SIZE}`);
}

8.2 新增和编辑用户 #

8.1.1 index.js #

src\pages\admin\user\index.js

import React,{Component,Fragment} from 'react';
import {connect} from 'dva';
import {Card,Table,Button,Modal,Form,Input,message,Popconfirm} from 'antd';
import {PAGE_SIZE} from './constants';
import { routerRedux } from 'dva/router';
const FormItem=Form.Item;

export default 
@connect(
    state => ({...state.user,loading:state.loading.models.user})
) 
class user extends Component{
+     save = (payload) => {
+        this.props.dispatch({
+            type: 'user/save',
+            payload
+        });
+     }
+     onAdd=() => {
+        this.save({editVisible: true,isCreate:true,record: {} });
+     }
+     onEditCancel=() => {
+        this.save({ editVisible : false });
+    }
+    onEdit=(record) => {
+        this.save({editVisible: true,isCreate:false,record});
+    }
+    onEditOk=() => {
+        this.editForm.props.form.validateFields((err,values) => {
+            if (err) {
+                return message.error('表单校验失败!');
+            } else {
+                this.props.dispatch({
+                    type: this.props.isCreate?'user/create':'user/update',
+                    payload:values
+                });
+            }
+        });
+    }
     render(){
          const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '性别',
                dataIndex: 'gender',
                key: 'gender',
                render: (val,record)=>(
                    val===1?'男':'女'
                )
            },
+            {
+                title: '操作',
+                key: 'operation',
+                render: (val,record) => (
+                    <Fragment>
+                        <Button type="warning" onClick={()=>this.onEdit(record)}>编辑</Button>
+                    </Fragment>
+                )
+            }
        ]
+        let {list,loading,pageNum,total,dispatch,isCreate,editVisible,record}=this.props;
        const pagination={
            current: pageNum,
            pageSize: PAGE_SIZE,
            showQuickJumper: true,
            showTotal: (total,range) => {
                return `共${total}条`;
            },
            total,
            onChange: (pageNum) => {
                dispatch(routerRedux.push(`/admin/user?pageNum=${pageNum}`));
            }
        }
         return (
             <Card>
                <Button type="warning" onClick={this.onAdd}>添加</Button>
                <Table
                        columns={columns}
                        dataSource={list}
                        loading={loading}
                        rowKey={record => record.id}
                        pagination={pagination}
                />
+                 <EditModal
+                    wrappedComponentRef={instance =>this.editForm=instance}
+                    isCreate={isCreate}
+                    visible={editVisible}
+                    onOk={this.onEditOk}
+                    onCancel={this.onEditCancel}
+                    record={record}
+                />
            </Card>
         )
     }
}
+@Form.create()
+class EditModal extends Component{
+    render() {
+     let {visible,onOk,isCreate,onCancel,record,form: {getFieldDecorator}=this.props;
+        let {username,email,id}=record;
+        return (
+            <Modal
+                title={isCreate?'创建用户':'修改用户'}
+                visible={visible}
+                onOk={onOk}
+                onCancel={onCancel}
+                destroyOnClose
+            >
+                <Form>
+                    <FormItem>
+                       {
+                            getFieldDecorator('id',{
+                                initialValue: id
+                            })(<Input type="hidden" />)
+                        }
+                    </FormItem>
+                    <FormItem
+                        label="用户名"
+                    >
+                        {
+                            getFieldDecorator('username',{
+                                rules: [{
+                                    required: true,
+                                    message:'用户名必须输入'
+                               }],
+                                initialValue: username
+                            })(<Input />)
+                        }
+                    </FormItem>
+                    <FormItem label="邮箱" >
+                        {
+                            getFieldDecorator('email',{
+                                initialValue: email,
+                                rules: [{required: true,message:'用户名必须输入'
+                            }]})(<Input/>)
+                        }
+                    </FormItem>
+                </Form>
+            </Modal>
+        )
+    }
+}

8.1.2 models/user.js #

src/pages/admin/user/models/user.js

import * as userService from '../services/user';

export default {
    namespace: 'user',
    state: {
        list: [],
        pageNum:1,
        total:0,
+        editVisible: false,
+        isCreate:true,
+        record: {}
    },
    reducers: {
        save(state,{payload}) {
            return {...state,...payload};
        }
    },
    effects: {
        *fetch({payload: {pageNum=1}},{call,put}) {
            const result=yield call(userService.fetch,pageNum);
            debugger;
            if(result.code === 0){
               let { list,total} = result.data;
               yield put({type:'save',payload:{list,pageNum:parseInt(pageNum),total}});
            }
        },
+        *create({payload},{call,put}) {
+            yield call(userService.create,payload);
+            yield put({type: 'fetch',payload: {pageNum:1}});
+            yield put({type:'save',payload:{editVisible:false}});
+        },
+        *update({payload},{call,put,select}) {
+            yield call(userService.update,payload);
+            let pageNum=yield select(state=>state.user.pageNum);
+            yield put({type: 'fetch',payload: {pageNum}});
+            yield put({type:'save',payload:{editVisible:false}});
+        },
    },
    subscriptions: {
        setup({dispatch,history}) {
            return history.listen(({pathname,query}) => {
                if (pathname==='/admin/user') {
                    dispatch({type:'fetch',payload:query});
                }
            });
        }
    }
}

8.1.3 services/user.js #

src/pages/admin/user/services/user.js

import request from '@/utils/request';
import {PAGE_SIZE} from '../constants';
export function fetch(pageNum) {
    return request(`/api/user?pageNum=${pageNum}&pageSize=${PAGE_SIZE}`);
}
+export function create(values) {
+    return request(`/api/user`,{
+        method: 'POST',
+        headers:{"Content-Type":"application/json"},
+        body:JSON.stringify(values)
+    });
+}
+export function update(values) {
+   return request(`/api/user/${values.id}`,{
+        method: 'PUT',
+        headers:{"Content-Type":"application/json"},
+        body:JSON.stringify(values)
+    });
+}

8.3 删除用户 #

8.3.1 index.js #

src\pages\admin\user\index.js

+    onDel=(id) => {
+        this.props.dispatch({
+            type: 'user/del',
+            payload:id
+        });
+   }

   render: (val,record) => (
       <Fragment>
           <Button type="warning" onClick={()=>this.onEdit(record)   </Button>
+           <Popconfirm
+               okText="确认"
+               cancelText="取消"
+               title="确认删除此用户吗?"
+               onConfirm={() => this.onDel(record.id)}>
+                 <Button type="danger">删除</Button>
+           </Popconfirm>
       </Fragment>

8.3.2 models\user.js #

src\pages\admin\user\models\user.js

+        *del({payload},{call,put}) {
+            yield call(userService.del,payload);
+            yield put({type: 'fetch',payload: {pageNum:1}});
+        }

8.3.3 services\user.js #

src\pages\admin\user\services\user.js

+ export function del(id) {
+     return request(`/api/user/${id}`,{
+        method: 'DELETE'
+ });

8.4 全部删除 #

8.4.1 src\pages\admin\user\index.js #

src\pages\admin\user\index.js

            type: 'user/del',
            payload:id
        });
   }
+   onAllDel=() => {
+    this.props.dispatch({
+        type: 'user/delAll',
+        payload:this.props.selectedRowKeys
+    });
+   }
     render(){
          const columns=[

                dispatch(routerRedux.push(`/admin/user?pageNum=${pageNum}`));
            }
        }
+        const rowSelection={
+            type: 'checkbox',
+            selectedRowKeys: this.props.selectedRowKeys,
+            onChange: (selectedRowKeys) => {
+                this.save({selectedRowKeys});
+            }
     }
         return (
             <Card>
                <Button type="warning" onClick={this.onAdd}>添加</Button>
+                <Button type="danger" onClick={this.onAllDel}>全部删除</Button>
                <Table
                        columns={columns}
                        dataSource={list}
                        loading={loading}
                        rowKey={record => record.id}
                        pagination={pagination}
+                       rowSelection={rowSelection}
                />
                 <EditModal
                    wrappedComponentRef={instance =>this.editForm=instance}         

8.4.2 user.js #

pages\admin\user\models\user.js

        record: {},
+        selectedRowKeys:[]
+        *delAll({payload},{call,put}) {
+           yield call(userService.delAll,payload);
+           yield put({type: 'fetch',payload: {pageNum:1}});
+        }

8.4.3 services\user.js #

src\pages\admin\user\services\user.js

+ export function delAll(ids) {
+    return request(`/api/user/${ids[0]}`,{
+        method: 'DELETE',
+        headers: {"Content-Type": "application/json"},
+        body: JSON.stringify(ids)
+    });
+}

8.5 搜索 #

8.5.1 src\pages\admin\user\index.js #

src\pages\admin\user\index.js

        type: 'user/delAll',
        payload:this.props.selectedRowKeys
    });
   }
+   onSearch=() => {
+        let values=this.searchForm.props.form.getFieldsValue();
+        let where=Object.keys(values).reduce((memo,key) => {
+            if (values[key]){
+                memo[key]=values[key];
+            }
+            return memo;
+        },{});
+        this.props.dispatch({
+            type: 'user/search',
+            payload:where
+        });
+   }


+ let {list,loading,pageNum,total,dispatch,isCreate,editVisible,record,where}=this.props;


+    <>
+            <Card>
+                <SearchForm
+                    where={where}
+                    onSearch={this.onSearch}
+                    wrappedComponentRef={inst=>this.searchForm=inst}/>
            </Card>
             <Card>
+    </>      


+@Form.create()
+class SearchForm extends Component{
+    render() {
+        let {form: {getFieldDecorator},onSearch,where={}}=this.props;
+        return (
+            <Form layout="inline">
+                    <FormItem
+                        label="用户名"
+                    >
+                        {
+                            getFieldDecorator('username',{initialValue:where.username})(<Input />)
+                        }
+                    </FormItem>
+                    <FormItem
+                        label="邮箱"
+                    >
+                        {
+                            getFieldDecorator('email',{initialValue:where.email})(<Input/>)
+                        }
+                </FormItem>
+                <FormItem>
+                    <Button onClick={onSearch} shape="circle" icon="search"></Button>
+                </FormItem>
+                </Form>
+        )
+    }
+}

8.5.2 user.js #

pages\admin\user\models\user.js

        selectedRowKeys:[],
+        where:{}

+ *fetch({payload: {pageNum=1,where}},{call,put,select}) {
+             if (!where) {
+                 where =yield select(state => state.user.where);
+            }
+            if (!pageNum) {
+                pageNum =yield select(state => state.user.pageNum);
+            }
+            const result=yield call(userService.fetch,pageNum,where);
+            if(result.code === 0){
+               let { list,total} = result.data;
+               yield put({type:'save',payload:{list,pageNum:parseInt(pageNum),total}});
+            }
+        },
+        *search({payload:where},{call,put}) {
+            yield put({type: 'fetch',payload: {pageNum:1,where}});
+        },

8.5.3 services\user.js #

src\pages\admin\user\services\user.js

+import querystring from 'querystring';
+export function fetch(pageNum,where) {
+    let whereString=querystring.stringify(where);
+    return request(`/api/user?pageNum=${pageNum}&pageSize=${PAGE_SIZE}&${whereString}`);
+}

8.6 点行选择行 #

8.6.1 src\pages\admin\user\index.js #

src\pages\admin\user\index.js

 <Table
                    columns={columns}
                    dataSource={list}
                    loading={loading}
                    rowKey={record => record.id}
                    pagination={pagination}
                    rowSelection={rowSelection}
+                    onRow = {
+                      (record) => {
+                        return {
+                          onClick: () => {
+                            let selectedRowKeys = this.props.selectedRowKeys;
+                            let index = selectedRowKeys.indexOf(record.id);
+                            if (index == -1) {
+                              selectedRowKeys = [...selectedRowKeys, record.id];
+                            } else {
+                              selectedRowKeys = selectedRowKeys.filter(key => key !=record.id);
+                            }
+                            this.save({
+                              selectedRowKeys
+                            });
+                          }
+                        }
+                      }
+                    }
                />

9 角色管理 #

9.1 role\index.js #

src\pages\admin\role\index.js

import React,{Component,Fragment} from 'react';
import {connect} from 'dva';
import {Card,Table,Button,Modal,Form,Input,message,Popconfirm} from 'antd';
import {PAGE_SIZE} from './constants';
import { routerRedux } from 'dva/router';
const FormItem=Form.Item;
const ENTITY='role';
export default 
@connect(
    state => ({...state[ENTITY],loading:state.loading.models[ENTITY]})
) 
class Role extends Component{
     save = (payload) => {
        this.props.dispatch({
            type: `${ENTITY}/save`,
            payload
        });
     }
     onAdd=() => {
        this.save({editVisible: true,isCreate:true,record: {} });
     }
     onEditCancel=() => {
        this.save({ editVisible : false });
    }
    onEditOk=() => {
        this.editForm.props.form.validateFields((err,values) => {
            if (err) {
                return message.error('表单校验失败!');
            } else {
                this.props.dispatch({
                    type: this.props.isCreate?`${ENTITY}/create`:`${ENTITY}/update`,
                    payload:values
                });
            }
        });
    }
    onEdit=(record) => {
        this.save({editVisible: true,isCreate:false,record});
    }
    onDel=(id) => {
        this.props.dispatch({
            type: `${ENTITY}/del`,
            payload:id
        });
   }
   onAllDel=() => {
    this.props.dispatch({
        type: `${ENTITY}/delAll`,
        payload:this.props.selectedRowKeys
    });
   }
   onSearch=() => {
        let values=this.searchForm.props.form.getFieldsValue();
        let where=Object.keys(values).reduce((memo,key) => {
            if (values[key]){
                memo[key]=values[key];
            }
            return memo;
        },{});
        this.props.dispatch({
            type: `${ENTITY}/search`,
            payload:where
        });
   }
     render(){
          const columns=[
            {
                title: '名称',
                dataIndex: 'name',
                key: 'name'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record) => (
                    <Fragment>
                        <Button type="warning" onClick={()=>this.onEdit(record)}>编辑</Button>
                        <Popconfirm
                            okText="确认"
                            cancelText="取消"
                            title="确认删除此用户吗?"
                            onConfirm={() => this.onDel(record.id)}>
                             <Button type="danger">删除</Button>
                        </Popconfirm>
                    </Fragment>
                )
            }
        ]
        let {list,loading,pageNum,total,dispatch,isCreate,editVisible,record,where}=this.props;
        const pagination={
            current: pageNum,
            pageSize: PAGE_SIZE,
            showQuickJumper: true,
            showTotal: (total,range) => {
                return `共${total}条`;
            },
            total,
            onChange: (pageNum) => {
                dispatch(routerRedux.push(`/admin/${ENTITY}?pageNum=${pageNum}`));
            }
        }
        const rowSelection={
            type: 'checkbox',
            selectedRowKeys: this.props.selectedRowKeys,
            onChange: (selectedRowKeys) => {
                this.save({selectedRowKeys});
            }
     }
         return (
            <>
            <Card>
                <SearchForm
                    where={where}
                    onSearch={this.onSearch}
                    wrappedComponentRef={inst=>this.searchForm=inst}/>
            </Card>
             <Card>
                <Button type="warning" onClick={this.onAdd}>添加</Button>
                <Button type="danger" onClick={this.onAllDel}>全部删除</Button>
                <Table
                        columns={columns}
                        dataSource={list}
                        loading={loading}
                        rowKey={record => record.id}
                        pagination={pagination}
                         rowSelection={rowSelection}
                        onRow = {
                        (record) => {
                          return {
                            onClick: () => {
                            let selectedRowKeys = this.props.selectedRowKeys;
                            let index = selectedRowKeys.indexOf(record.id);
                            if (index === -1) {
                              selectedRowKeys = [...selectedRowKeys, record.id];
                            } else {
                              selectedRowKeys = selectedRowKeys.filter(key => key !=record.id);
                            }
                            this.save({
                              selectedRowKeys
                            });
                          }
                        }
                      }
                    }
                />
                 <EditModal
                    wrappedComponentRef={instance =>this.editForm=instance}
                    isCreate={isCreate}
                    visible={editVisible}
                    onOk={this.onEditOk}
                    onCancel={this.onEditCancel}
                    record={record}
                />
            </Card>
            </>
         )
     }
}

@Form.create()
class SearchForm extends Component{
    render() {
        let {form: {getFieldDecorator},onSearch,where={}}=this.props;
        return (
            <Form layout="inline">
                    <FormItem
                        label="角色名称"
                    >
                        {
                            getFieldDecorator('name',{initialValue:where.name})(<Input />)
                        }
                    </FormItem>
                <FormItem>
                    <Button onClick={onSearch} shape="circle" icon="search"></Button>
                </FormItem>
                </Form>
        )
    }
}

@Form.create()
class EditModal extends Component{
    render() {
        let {visible,onOk,isCreate,onCancel,record,form: {getFieldDecorator}}=this.props;
        let {id,name}=record;
        return (
            <Modal
                title={isCreate?'创建角色':'修改角色'}
                visible={visible}
                onOk={onOk}
                onCancel={onCancel}
                destroyOnClose
            >
                <Form>
                    <FormItem>
                       {
                            getFieldDecorator('id',{
                                initialValue: id
                            })(<Input type="hidden" />)
                        }
                    </FormItem>
                    <FormItem
                        label="用户名"
                    >
                        {
                            getFieldDecorator('name',{
                                rules: [{
                                    required: true,
                                    message:'名称必须输入'
                                }],
                                initialValue: name
                            })(<Input />)
                        }
                    </FormItem>
                </Form>
            </Modal>
        )
    }
}

9.2 role\constants.js #

src\pages\admin\role\constants.js

export const PAGE_SIZE = 3;

9.3 models\role.js #

src\pages\admin\role\models\role.js

import * as service from '../services/role';
const ENTITY='role';
export default {
    namespace: ENTITY,
    state: {
        list: [],
        pageNum:1,
        total:0,
        editVisible: false,
        isCreate:true,
        record: {},
        selectedRowKeys:[],
        where:{}
    },
    reducers: {
        save(state,{payload}) {
            return {...state,...payload};
        }
    },
    effects: {
        *fetch({payload: {pageNum=1,where}},{call,put,select}) {
            if (!where) {
                where =yield select(state => state[ENTITY].where);
            }
            if (!pageNum) {
                pageNum =yield select(state => state[ENTITY].pageNum);
            }
            const result=yield call(service.fetch,pageNum,where);
            if(result.code === 0){
               let { list,total} = result.data;
               yield put({type:'save',payload:{list,pageNum:parseInt(pageNum),total}});
            }
        },
        *search({payload:where},{call,put}) {
            yield put({type: 'fetch',payload: {pageNum:1,where}});
        },
        *create({payload},{call,put}) {
            yield call(service.create,payload);
            yield put({type: 'fetch',payload: {pageNum:1}});
            yield put({type:'save',payload:{editVisible:false}});
        },
        *update({payload},{call,put,select}) {
            yield call(service.update,payload);
            let pageNum=yield select(state=>state[ENTITY].pageNum);
            yield put({type: 'fetch',payload: {pageNum}});
            yield put({type:'save',payload:{editVisible:false}});
        },
        *del({payload},{call,put}) {
            yield call(service.del,payload);
            yield put({type: 'fetch',payload: {pageNum:1}});
        },
        *delAll({payload},{call,put}) {
             yield call(service.delAll,payload);
             yield put({type: 'fetch',payload: {pageNum:1}});
        }
    },
    subscriptions: {
        setup({dispatch,history}) {
            return history.listen(({pathname,query}) => {
                if (pathname===`/admin/${ENTITY}`) {
                    dispatch({type:'fetch',payload:query});
                }
            });
        }
    }
}

9.4 services\role.js #

src\pages\admin\role\services\role.js

import request from '@/utils/request';
import {PAGE_SIZE} from '../constants';
import querystring from 'querystring';
const ENTITY='role';
export function fetch(pageNum,where) {
    let whereString=querystring.stringify(where);
    return request(`/api/${ENTITY}?pageNum=${pageNum}&pageSize=${PAGE_SIZE}&${whereString}`);
}
export function create(values) {
    return request(`/api/${ENTITY}`,{
        method: 'POST',
        headers:{"Content-Type":"application/json"},
        body:JSON.stringify(values)
    });
}
export function update(values) {
    return request(`/api/${ENTITY}/${values.id}`,{
        method: 'PUT',
        headers:{"Content-Type":"application/json"},
        body:JSON.stringify(values)
    });
}
export function del(id) {
    return request(`/api/${ENTITY}/${id}`,{
        method: 'DELETE'
    });
}
export function delAll(ids) {
    return request(`/api/${ENTITY}/${ids[0]}`,{
        method: 'DELETE',
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify(ids)
    });
}

10. 设置权限 #

10.1 service\role.js #

//list  resourceIds=代表这个角色所拥有的资源ID数组
for (let i = 0; i < list.length; i++) {
    let rows = await app.mysql.select('role_resource', {
        where: { role_id: list[i].id }
    });//[{role_id:1,resource_id:2},{role_id:1,resource_id:3}]
    list[i].resourceIds = rows.map(item => item.resource_id);//[2,3]
    rows = await app.mysql.select('role_user', {
         where: { role_id: list[i].id }
    });//[{role_id:1,resource_id:2},{role_id:1,resource_id:3}]
   list[i].userIds = rows.map(item => item.user_id);//[2,3]
}

10.2 role/index.js #

+ import {Tree,Card,Table,Button,Modal,Form,Input,message,Popconfirm} from 'antd';
+const TreeNode=Tree.TreeNode;

+   setRolePermission=() => {
+        if (this.props.selectedRows.length===1) {
+            let record=this.props.selectedRows[0];
+            this.save({setPermissionVisible:true,record,checkedKeys:record.resourceIds});
+        } else {
+            message.error('为角色设置权限时要选择并且只能选择一个角色!');
+        }
+    }
+    onCheckPermission=(checkedKeys) => {
+            this.save({checkedKeys});
+        }
+    setRolePermissionOk=() => {
+            this.props.dispatch({
+                type: `${ENTITY}/setRolePermission`
+            });
+    }

+ let {list,loading,pageNum,total,dispatch,isCreate,editVisible,record,where,setPermissionVisible,resources,checkedKeys}=this.props;

+const rowSelection={
+            type: 'checkbox',
+            selectedRowKeys: this.props.selectedRowKeys,
+            selectedRows: this.props.selectedRows,
+            onChange: (selectedRowKeys,selectedRows) => {
+                this.save({selectedRowKeys,selectedRows});
+           }
+}

+ <Button icon="solution" onClick={this.setRolePermission}>设置权限</Button>
<Table
+    rowSelection={rowSelection}

+                <PermissonModal
+                    visible={setPermissionVisible}
+                    record={record}
+                    resources={resources}
+                    checkedKeys={checkedKeys}
+                    onCheck={this.onCheckPermission}
+                    onOk={this.setRolePermissionOk}
+                    onCancel={()=>this.save({setPermissionVisible:false})}
+                />

+class PermissonModal extends Component{
+    renderResources=(children) => {
+        return children.map(child => {
+            if (child.children.length > 0) {
+                return (
+                    <TreeNode title={child.name} key={child.id}>
+                        {this.renderResources(child.children)}
+                    </TreeNode>
+                )
+            } else {
+                return <TreeNode title={child.name} key={child.id}/>
+            }
+        });
+    }
+    render() {
+        let {onCheck,visible,onOk,onCancel,checkedKeys,resources=[]}=this.props;
+        return (
+            <Modal
+                visible={visible}
+                title="为角色设置权限"
+                onOk={onOk}
+                onCancel={onCancel}
+                destroyOnClose
+            >
+                <Tree
+                    checkable
+                    defaultExpandAll
+                    onCheck={onCheck}
+                    checkedKeys={checkedKeys}
+                >
+                    <TreeNode title="所有权限" key={0} disabled>
+                        {this.renderResources(resources)}
+                    </TreeNode>
+                </Tree>
+            </Modal>
+        )
+    }
+}

10.3 models\role.js #

src\pages\admin\role\models\role.js

+        selectedRows: [],
+        checkedKeys: [],
+        resources:[],
+        setPermissionVisible:false

+        *getResource({payload},{call,put}) {
+            const resources=yield call(service.getResources);
+            yield put({type:'save',payload:{resources}});
+        },
+        *setRolePermission({payload},{call,put,select}) {
+            let {record,checkedKeys}=yield select(state => state[ENTITY]);
+            yield call(service.setRolePermission,{roleId:record.id,resourceIds:checkedKeys});
+            yield put({type: 'fetch',payload: {}});
+            yield put({type:'save',payload:{setPermissionVisible:false,selectedRowKeys:[],selectedRows:[]}});
+        }

                if (pathname===`/admin/${ENTITY}`) {
                    dispatch({type:'fetch',payload:query});
+                   dispatch({type:'getResource'});
                }

10.4 role.js #

src\pages\admin\role\services\role.js

+ export function getResources() {
+    return request(`/api/role/getResource`);
+}
+export function setRolePermission(values) {
+    return request(`/api/role/setResource`,{
+        method: 'POST',
+        headers:{"Content-Type":"application/json"},
+        body:JSON.stringify(values)
+    });
+}

11.给角色加用户 #

11.1 roles/index.js #

+ import { Transfer} from 'antd';
+ setUser = () => {
+        let selectedRows = this.props.selectedRows;
+        if (selectedRows.length == 1) {
+            let record = selectedRows[0];
+            this.save({ setUserVisible: true, record, targetKeys: record.userIds });
+       } else {
+            message.warn('请选中并且只能选中一个角色!');
+        }
+    }
+    setUserOk = () => {
+            this.props.dispatch({
+               type: `${ENTITY}/setUser`
+            });
+        }
+    onSetUserChange = (targetKeys) => {
+            this.save({ targetKeys });
+    }

+        let {list,loading,pageNum,total,dispatch,isCreate,editVisible,record,
        where,setPermissionVisible,resources,checkedKeys, setUserVisible, targetKeys, users}=this.props;
+                 <Button style={{ marginLeft: 8 }} type="primary" icon="plus-circle" onClick={this.setUser}>分配用户</Button>

+                             let selectedRowKeys=this.props.selectedRowKeys;
+                                        let selectedRows = this.props.selectedRows;
+                                        let index = selectedRowKeys.indexOf(record.id);
+                                        if (index===-1) {
+                                            selectedRowKeys=[...selectedRowKeys,record.id];
+                                            selectedRows=[...selectedRows,record];
+                                        } else {
+                                            selectedRowKeys=selectedRowKeys.filter(key => key!=record.id);
+                                            selectedRows=selectedRows.filter(row => row.id!=record.id);
+                                        }
+                                        this.save({selectedRowKeys,selectedRows});


+                <SetUserModal
+                        visible={setUserVisible}
+                        record={record}
+                        onOk={this.setUserOk}
+                        targetKeys={targetKeys}
+                        onChange={this.onSetUserChange}
+                        users={users}
+                        onCancel={() => this.save({ setUserVisible: false })}
+/>

+ class SetUserModal extends Component {
+    render() {
+        let { visible, onOk, onCancel, record, targetKeys, onChange, users } = this.props;
+        return (
+            <Modal
+                title={`为 ${record.name} 分配用户`}
+                visible={visible}
+                onOk={onOk}
+                okText={"确定"}
+                cancelText={"取消"}
+                onCancel={onCancel}
+                destroyOnClose
+            >
+                <Transfer
+                    dataSource={users}
+                    targetKeys={targetKeys}
+                    titles={["待选用户", "已选用户"]}
+                    onChange={onChange}
+                    render={row => row.username}
+                    rowKey={row => row.id}
+                />
+            </Modal>
+        )
+    }
+}

11.2 models/roles.js #

src/pages/admin/roles/models/roles.js

+        setPermissionVisible:false,
+        setUserVisible: false,//窗口是否显示
+        targetKeys: [],//选中的用户
+        users: [] //所有的用户

+        *getUser({ }, { call, put }) {
+            let users = yield call(service.getUser);
+            yield put({ type: 'save', payload: { users } });
+        },
+         *setUser({ }, { call, put, select }) {
+            let record = yield select(state => state.role.record);
+            let targetKeys = yield select(state => state.role.targetKeys);
+            yield call(service.setUser, {
+                roleId: record.id,
+                userIds: targetKeys
+            });
+            yield put({ type: 'save', payload: { setUserVisible: false, selectedRowKeys: [], selectedRows: [] } });
+            yield put({ type: 'fetch', payload: {} });
+        }

            dispatch({type:'getResource'});
+           dispatch({ type: 'getUser' });

11.3 services/roles.js #

src/pages/admin/roles/services/roles.js

+export function getUser() {
+    return request(`/api/role/getUser`);
+}
+export function setUser(values) {
+    return request(`/api/role/setUser`, {
+        method: 'POST',
+        headers: {
+            "Content-Type": "application/json"
+        },
+        body: JSON.stringify(values)
+    });

访问验证

请输入访问令牌

Token不正确,请重新输入