2018-09-29

生成项目


npm i dva-cli@next -g
dva new cms
cd cms
npm start

2. 配置代理 [#](#t12. 配置代理)

.webpackrc

{
        "proxy": {
            "/api": {
              "target": "http://localhost:7001",
              "changeOrigin": true,
              "pathRewrite": { "^/api" : "" }
            }
        }
}

http://localhost:8000

3.后台首页布局模版 #

3.1 _layout.js [#](#t33.1 _layout.js)

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>Content</Content>
                </Layout>
                <Footer>
                   张熙沐枫 ©2018
                </Footer>
            </Layout>
       )
   }
}

4.顶部导航条 #

4.1 admin/_layout.js [#](#t54.1 admin/_layout.js)

src/pages/admin/_layout.js

import React,{Component,Fragment} from 'react';
import {Layout,Menu,Icon} from 'antd';
import logo from '../../assets/logo.png';
const {Header,Footer,Sider,Content}=Layout;
export default class Admin extends Component{
    render() {
        return (
            <Layout>
                <Header>
                    <img className="logo" src={logo} alt="logo"/>
                    <span className="welcome">欢迎登录 张三</span>
                </Header>
                <Layout>
                    <Sider>Sider</Sider>
                    <Content>Content</Content>
                </Layout>
                <Footer style={{ textAlign: 'center' }}> 张熙沐枫 ©2018</Footer>
            </Layout>
       )
   }
}

4.2 src/global.less [#](#t64.2 src/global.less)

src/global.less

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

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

5.1 _layout.js [#](#t85.1 _layout.js)

import React,{Component,Fragment} from 'react';
import {Layout,Menu,Icon} from 'antd';
import logo from '../../assets/logo.png';
import NavLeft from '../../components/NavLeft';
const {Header,Footer,Sider,Content}=Layout;
export default class Admin extends Component{
    render() {
        return (
            <Layout>
                <Header>
                    <img className="logo" src={logo} alt="logo"/>
                    <span className="welcome">欢迎登录 张三</span>
                </Header>
                <Layout>
                    <Sider>
                        <NavLeft/>
                    </Sider>
                    <Content>
                        {this.props.children}
                    </Content>
                </Layout>
                <Footer style={{ textAlign: 'center' }}> 张熙沐枫 ©2018</Footer>
            </Layout>
       )
   }
}

5.2 components/NavLeft.js [#](#t95.2 components/NavLeft.js)

import React,{Component,Fragment} from 'react';
import {Menu,Icon} from 'antd';
import Link from 'umi/link';
const SubMenu=Menu.SubMenu;
export default class NavLeft extends Component{
    render() {
        return (
            <Menu
                theme="dark"
                defaultSelectedKeys={['/admin/user']}
                defaultOpenKeys={['/admin/permission']}
                mode="inline"
            >
                <SubMenu key="/admin/permission" title={<span><Icon type="bars"/>权限管理</span>}>
                    <Menu.Item key="/admin/user"><Link to="/admin/user"><Icon type="user" />用户管理</Link></Menu.Item>
                    <Menu.Item key="/admin/role"><Link to="/admin/role"><Icon type="idcard" />角色管理</Link></Menu.Item>
                    <Menu.Item key="/admin/resource"><Link to="/admin/resource"><Icon type="wallet" />资源管理</Link></Menu.Item>
                </SubMenu>
            </Menu>
        )
    }
}

5.3 user.js [#](#t105.3 user.js)

import React,{Component,Fragment} from 'react';
import {Menu,Icon,Card,Table,Button,Form,Input,Select, Popconfirm,Modal} from 'antd';
const FormItem=Form.Item;
export default class User extends Component{
    state={
        userVisible: false,
        isUserCreate: true,
        record:{}
    }
    handleUserModalOk=() => {

    }
    createUser=() => {
        this.setState({
            record: {},
            userVisible: true,
            isUserCreate:true
        });
    }
    editUser=(record) => {
        this.setState({
            record,
            userVisible: true,
            isUserCreate:false
        });
    }
    render() {
        const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '手机号',
                dataIndex: 'phone',
                key: 'phone'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.editUser(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.remove(record._id)}>
                                <Button style={{marginLeft:10}} icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const dataSource=[
            {username: 'zxmf1',email: 'zxmf1@qq.com',phone: '1'},
            {username:'zxmf2',email:'zxmf2@qq.com',phone:'2'}
        ]
        return (
            <Fragment>
                <Card>
                    <Form layout="inline">
                        <FormItem>
                            <Input placeholder="请输入用户名"/>
                        </FormItem>
                        <FormItem>
                            <Input type="password" placeholder="请输入密码"/>
                        </FormItem>
                        <FormItem>
                            <Button type="primary">登 录</Button>
                        </FormItem>
                    </Form>
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                        <Button icon="plus" onClick={this.createUser}>添加</Button>
                      <Button icon="delete" style={{marginLeft:5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                    />
                    <UserModal
                            title={this.state.isCreate?'新增用户':'编辑用户'}
                            visible={this.state.userVisible}
                            destroyOnClose
                            record={this.state.record}
                            onCancel={() => this.setState({userVisible: false})}
                            onOk={this.handleUserModalOk}
                    />
                </Card>
            </Fragment>
        )
    }
}

class UserModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="用户名" {...formItemLayout}>
                        {
                            getFieldDecorator('username',{
                                initialValue:record.username
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="邮箱" {...formItemLayout}>
                        {
                            getFieldDecorator('email',{
                                initialValue:record.email
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="电话" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                initialValue:record.phone
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
UserModal=Form.create()(UserModal);

6.加载用户数据 #

6.1 ../../components/NavLeft.js [#](#t126.1 ../../components/NavLeft.js)

../../components/NavLeft.js

import React,{Component,Fragment} from 'react';
import {Menu,Icon} from 'antd';
import Link from 'umi/link';
const SubMenu=Menu.SubMenu;
export default class NavLeft extends Component{
    render() {
        return (
            <Menu
                theme="dark"
                defaultSelectedKeys={['/admin/users']}
                defaultOpenKeys={['/admin/permission']}
                mode="inline"
            >
                <SubMenu key="/admin/permission" title={<span><Icon type="bars"/>权限管理</span>}>
                    <Menu.Item key="/admin/users"><Link to="/admin/user"><Icon type="user" />用户管理</Link></Menu.Item>
                    <Menu.Item key="/admin/role"><Link to="/admin/role"><Icon type="idcard" />角色管理</Link></Menu.Item>
                    <Menu.Item key="/admin/resource"><Link to="/admin/resource"><Icon type="wallet" />资源管理</Link></Menu.Item>
                </SubMenu>
            </Menu>
        )
    }
}

6.2 users/page.js [#](#t136.2 users/page.js)

pages/admin/users/page.js

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal} from 'antd';
import { connect } from 'dva';
const FormItem=Form.Item;
class User extends Component{
    state={
        userVisible: false,
        isUserCreate: true,
        record:{}
    }
    handleUserModalOk=() => {

    }
    createUser=() => {
        this.setState({
            record: {},
            userVisible: true,
            isUserCreate:true
        });
    }
    editUser=(record) => {
        this.setState({
            record,
            userVisible: true,
            isUserCreate:false
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: '/admin/users',
            query:{page}
        }));
    }
    render() {
        const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '手机号',
                dataIndex: 'phone',
                key: 'phone'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.editUser(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.remove(record._id)}>
                                <Button style={{marginLeft:10}} icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current}=this.props;
        return (
            <Fragment>
                <Card>
                    <Form layout="inline">
                        <FormItem>
                            <Input placeholder="请输入用户名"/>
                        </FormItem>
                        <FormItem>
                            <Input type="email" placeholder="请输入邮箱"/>
                        </FormItem>
                        <FormItem>
                            <Input type="phone" placeholder="请输入手机号"/>
                        </FormItem>
                        <FormItem>
                            <Button shape="circle" icon="search"></Button>
                        </FormItem>
                    </Form>
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                        <Button icon="plus" onClick={this.createUser}>添加</Button>
                      <Button icon="delete" style={{marginLeft:5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                    />
                    <UserModal
                            title={this.state.isCreate?'新增用户':'编辑用户'}
                            visible={this.state.userVisible}
                            destroyOnClose
                            record={this.state.record}
                            onCancel={() => this.setState({userVisible: false})}
                            onOk={this.handleUserModalOk}
                    />
                </Card>
            </Fragment>
        )
    }
}

class UserModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="用户名" {...formItemLayout}>
                        {
                            getFieldDecorator('username',{
                                initialValue:record.username
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="邮箱" {...formItemLayout}>
                        {
                            getFieldDecorator('email',{
                                initialValue:record.email
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="电话" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                initialValue:record.phone
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
UserModal=Form.create()(UserModal);

export default connect(
    state => ({
        ...state.users,
        loading:state.loading.models.users
    })
)(User);

6.3 users/constants.js [#](#t146.3 users/constants.js)

pages/admin/users/constants.js

export const PAGE_SIZE=3;

6.4 services/users.js [#](#t156.4 services/users.js)

pages/admin/users/services/users.js

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

export function list(page,limit) {
  return request(`/api/users?page=${page}&limit=${limit}`);
}

6.5 models/users.js [#](#t166.5 models/users.js)

pages/admin/users/models/users.js

import {PAGE_SIZE} from '../constants';
import * as usersService from '../services/users';
export default {
    namespace:'users',
    state: {
        list:[],
        total:0,
        page:1
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == '/admin/users') {
                    dispatch({type:'list',payload:query});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(usersService.list,page,PAGE_SIZE);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
      },
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
      },
    },

  };

7. 添加用户 [#](#t177. 添加用户)

7.1 users/models/users.js [#](#t187.1 users/models/users.js)

users/models/users.js

import {PAGE_SIZE} from '../constants';
import * as usersService from '../services/users';
export default {
    namespace:'users',
    state: {
        list:[],
        total:0,
        page:1
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == '/admin/users') {
                    dispatch({type:'list',payload:query});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(usersService.list,page,PAGE_SIZE);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *create({payload},{call,put,select}) {
            yield call(usersService.create,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(usersService.update,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(usersService.del,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
      },
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
      },
    },

  };

7.2 users/page.js [#](#t197.2 users/page.js)

users/page.js

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message} from 'antd';
import { connect } from 'dva';
const FormItem=Form.Item;
class User extends Component{
    state={
        userVisible: false,
        isUserCreate: true,
        record:{}
    }
    handleUserModalOk=() => {
        this.userForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isUserCreate,record}=this.state;
                if (isUserCreate) {
                    this.props.dispatch({
                        type: 'users/create',
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: 'users/update',
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    userVisible: false,
                    isUserCreate: true,
                    record:{}
                });
            }
        });
    }
    delUser=(id) => {
        this.props.dispatch({
            type: 'users/del',
            payload: id
        });
    }
    createUser=() => {
        this.setState({
            record: {},
            userVisible: true,
            isUserCreate:true
        });
    }
    editUser=(record) => {
        this.setState({
            record,
            userVisible: true,
            isUserCreate:false
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: '/admin/users',
            query:{page}
        }));
    }

    render() {
        const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '手机号',
                dataIndex: 'phone',
                key: 'phone'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.editUser(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.delUser(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading}=this.props;
        dataSource.forEach(row=>row.key = row.id);
        return (
            <Fragment>
                <Card>
                    <Form layout="inline">
                        <FormItem>
                            <Input placeholder="请输入用户名"/>
                        </FormItem>
                        <FormItem>
                            <Input type="email" placeholder="请输入邮箱"/>
                        </FormItem>
                        <FormItem>
                            <Input type="phone" placeholder="请输入手机号"/>
                        </FormItem>
                        <FormItem>
                            <Button shape="circle" icon="search"></Button>
                        </FormItem>
                    </Form>
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.createUser}>添加</Button>
                      <Button icon="delete" style={{marginLeft:5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                    />
                    <UserModal
                            title={this.state.isCreate?'新增用户':'编辑用户'}
                            visible={this.state.userVisible}
                            record={this.state.record}
                            onCancel={() => this.setState({userVisible: false})}
                            onOk={this.handleUserModalOk}
                            wrappedComponentRef={instance => this.userForm = instance}
                    />
                </Card>
            </Fragment>
        )
    }
}

class UserModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="用户名" {...formItemLayout}>
                        {
                            getFieldDecorator('username',{
                                initialValue: record.username,
                                rules:[
                                    {
                                        required:true,
                                        message:'用户名不能为空'
                                    },
                                    {
                                        min:1,max:12,
                                        message:'长度不在范围内'
                                    },
                                    {
                                        pattern:/^\w+$/,
                                        message:'用户名必须为字母或者数字'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="邮箱" {...formItemLayout}>
                        {
                            getFieldDecorator('email',{
                                initialValue: record.email,
                                rules:[
                                    {
                                        required:true,
                                        message:'邮箱不能为空'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="电话" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                initialValue:record.phone
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
UserModal=Form.create()(UserModal);

export default connect(
    state => ({
        ...state.users,
        loading:state.loading.models.users
    })
)(User);

7.3 services/users.js [#](#t207.3 services/users.js)

users/services/users.js

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

export function list(page,limit) {
  return request(`/api/users?page=${page}&limit=${limit}`);
}

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

export function update(values) {
  return request(`/api/users/${values.id}`,{
    method: 'PUT',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(values)
  });
}

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

8. 多选删除 [#](#t218. 多选删除)

8.1 users/page.js [#](#t228.1 users/page.js)

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message} from 'antd';
import { connect } from 'dva';
const FormItem=Form.Item;
class User extends Component{
    state={
        userVisible: false,
        isUserCreate: true,
        record: {},
        selectedRowKeys:[]
    }
    handleUserModalOk=() => {
        this.userForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isUserCreate,record}=this.state;
                if (isUserCreate) {
                    this.props.dispatch({
                        type: 'users/create',
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: 'users/update',
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    userVisible: false,
                    isUserCreate: true,
                    record:{}
                });
            }
        });
    }
    delUser=(id) => {
        this.props.dispatch({
            type: 'users/del',
            payload: id
        });
    }
    createUser=() => {
        this.setState({
            record: {},
            userVisible: true,
            isUserCreate:true
        });
    }
    editUser=(record) => {
        this.setState({
            record,
            userVisible: true,
            isUserCreate:false
        });
    }
    deleteSelectedUsers=() => {
        this.props.dispatch({
            type: 'users/delMulti',
            payload: this.state.selectedRowKeys
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: '/admin/users',
            query:{page}
        }));
    }
    onRowClick = (record, key) => {

    }
    render() {
        const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '手机号',
                dataIndex: 'phone',
                key: 'phone'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.editUser(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.delUser(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading}=this.props;
        dataSource.forEach(row => row.key=row.id);
        const rowSelection={
            type:'checkbox',
            selectedRowKeys: this.state.selectedRowKeys,
            onChange: (selectedRowKeys,selectedRows) => {
                this.setState({selectedRowKeys});
            }
        }
        return (
            <Fragment>
                <Card>
                    <Form layout="inline">
                        <FormItem>
                            <Input placeholder="请输入用户名"/>
                        </FormItem>
                        <FormItem>
                            <Input type="email" placeholder="请输入邮箱"/>
                        </FormItem>
                        <FormItem>
                            <Input type="phone" placeholder="请输入手机号"/>
                        </FormItem>
                        <FormItem>
                            <Button shape="circle" icon="search"></Button>
                        </FormItem>
                    </Form>
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.createUser}>添加</Button>
                        <Button
                            onClick={this.deleteSelectedUsers}
                            icon="delete" style={{marginLeft: 5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                        rowSelection={rowSelection}
                    />
                    <UserModal
                            title={this.state.isCreate?'新增用户':'编辑用户'}
                            visible={this.state.userVisible}
                            record={this.state.record}
                            onCancel={() => this.setState({userVisible: false})}
                            onOk={this.handleUserModalOk}
                            wrappedComponentRef={instance => this.userForm = instance}
                    />
                </Card>
            </Fragment>
        )
    }
}

class UserModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="用户名" {...formItemLayout}>
                        {
                            getFieldDecorator('username',{
                                initialValue: record.username,
                                rules:[
                                    {
                                        required:true,
                                        message:'用户名不能为空'
                                    },
                                    {
                                        min:1,max:12,
                                        message:'长度不在范围内'
                                    },
                                    {
                                        pattern:/^\w+$/,
                                        message:'用户名必须为字母或者数字'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="邮箱" {...formItemLayout}>
                        {
                            getFieldDecorator('email',{
                                initialValue: record.email,
                                rules:[
                                    {
                                        required:true,
                                        message:'邮箱不能为空'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="电话" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                initialValue:record.phone
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
UserModal=Form.create()(UserModal);

export default connect(
    state => ({
        ...state.users,
        loading:state.loading.models.users
    })
)(User);

8.2 users/models/users.js [#](#t238.2 users/models/users.js)

import {PAGE_SIZE} from '../constants';
import * as usersService from '../services/users';
export default {
    namespace:'users',
    state: {
        list:[],
        total:0,
        page:1
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == '/admin/users') {
                    dispatch({type:'list',payload:query});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(usersService.list,page,PAGE_SIZE);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *create({payload},{call,put,select}) {
            yield call(usersService.create,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(usersService.update,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(usersService.del,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *delMulti({payload},{call,put,select}) {
            yield call(usersService.delMulti,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
       }
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
      },
    },

  };

8.3 users/services/users.js [#](#t248.3 users/services/users.js)

users/services/users.js

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

export function list(page,limit) {
  return request(`/api/users?page=${page}&limit=${limit}`);
}

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

export function update(values) {
  return request(`/api/users/${values.id}`,{
    method: 'PUT',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(values)
  });
}

export function del(id) {
  return request(`/api/users/${id}`,{
    method: 'DELETE'
  });
}
export function delMulti(ids) {
  return request(`/api/users`,{
    method: 'DELETE',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(ids)
  });
}

9. 搜索 [#](#t259. 搜索)

9.1 users/page.js [#](#t269.1 users/page.js)

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message} from 'antd';
import { connect } from 'dva';
const FormItem=Form.Item;
class User extends Component{
    state={
        userVisible: false,
        isUserCreate: true,
        record: {},
        selectedRowKeys:[]
    }
    handleUserModalOk=() => {
        this.userForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isUserCreate,record}=this.state;
                if (isUserCreate) {
                    this.props.dispatch({
                        type: 'users/create',
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: 'users/update',
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    userVisible: false,
                    isUserCreate: true,
                    record:{}
                });
            }
        });
    }
    delUser=(id) => {
        this.props.dispatch({
            type: 'users/del',
            payload: id
        });
    }
    createUser=() => {
        this.setState({
            record: {},
            userVisible: true,
            isUserCreate:true
        });
    }
    editUser=(record) => {
        this.setState({
            record,
            userVisible: true,
            isUserCreate:false
        });
    }
    deleteSelectedUsers=() => {
        this.props.dispatch({
            type: 'users/delMulti',
            payload: this.state.selectedRowKeys
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: '/admin/users',
            query:{page}
        }));
    }
    onRowClick = (record, key) => {

    }
    handleSearch=() => {
        let filter=this.searchForm.props.form.getFieldsValue();
        let keys=Object.keys(filter);
        keys.forEach(key => {
            if (filter[key]==''||typeof filter[key] =='undefined')
                delete filter[key];
        });
        console.log(filter);
        this.props.dispatch({
            type: 'users/search',
            payload:filter
        });
    }
    render() {
        const columns=[
            {
                title: '用户名',
                dataIndex: 'username',
                key: 'username'
            },
            {
                title: '邮箱',
                dataIndex: 'email',
                key: 'email'
            },
            {
                title: '手机号',
                dataIndex: 'phone',
                key: 'phone'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.editUser(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.delUser(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading}=this.props;
        dataSource.forEach(row => row.key=row.id);
        const rowSelection={
            type:'checkbox',
            selectedRowKeys: this.state.selectedRowKeys,
            onChange: (selectedRowKeys,selectedRows) => {
                this.setState({selectedRowKeys});
            }
        }
        return (
            <Fragment>
                <Card>
                    <SearchForm
                        onSearch={this.handleSearch}
                        wrappedComponentRef={inst=>this.searchForm=inst}
                    />
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.createUser}>添加</Button>
                        <Button
                            onClick={this.deleteSelectedUsers}
                            icon="delete" style={{marginLeft: 5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                        rowSelection={rowSelection}
                    />
                    <UserModal
                            title={this.state.isCreate?'新增用户':'编辑用户'}
                            visible={this.state.userVisible}
                            record={this.state.record}
                            onCancel={() => this.setState({userVisible: false})}
                            onOk={this.handleUserModalOk}
                            wrappedComponentRef={instance => this.userForm = instance}
                    />
                </Card>
            </Fragment>
        )
    }
}

class SearchForm extends Component{
    render() {
        let {form: {getFieldDecorator},onSearch}=this.props;
        return (
            <Form layout="inline">
                        <FormItem>
                        {
                                getFieldDecorator('username')(
                                    <Input placeholder="请输入用户名"/>
                                )
                        }
                        </FormItem>
                        <FormItem>
                        {
                                getFieldDecorator('email')(
                                    <Input placeholder="请输入邮箱"/>
                                )
                        }
                        </FormItem>
                        <FormItem>
                        {
                                getFieldDecorator('phone')(
                                    <Input placeholder="请输入手机号"/>
                                )
                        }
                        </FormItem>
                        <FormItem>
                    <Button
                        onClick={onSearch}
                        shape="circle"
                                icon="search"></Button>
                        </FormItem>
            </Form>
        )
    }
}
SearchForm=Form.create()(SearchForm);

class UserModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="用户名" {...formItemLayout}>
                        {
                            getFieldDecorator('username',{
                                initialValue: record.username,
                                rules:[
                                    {
                                        required:true,
                                        message:'用户名不能为空'
                                    },
                                    {
                                        min:1,max:12,
                                        message:'长度不在范围内'
                                    },
                                    {
                                        pattern:/^\w+$/,
                                        message:'用户名必须为字母或者数字'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="邮箱" {...formItemLayout}>
                        {
                            getFieldDecorator('email',{
                                initialValue: record.email,
                                rules:[
                                    {
                                        required:true,
                                        message:'邮箱不能为空'
                                    }
                                ]
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
                    <Form.Item label="电话" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                initialValue:record.phone
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
UserModal=Form.create()(UserModal);

export default connect(
    state => ({
        ...state.users,
        loading:state.loading.models.users
    })
)(User);

9.2 models/users.js [#](#t279.2 models/users.js)

users/models/users.js

import {PAGE_SIZE} from '../constants';
import * as usersService from '../services/users';
export default {
    namespace:'users',
    state: {
        list:[],
        total:0,
        page: 1,
        filter: {}
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == '/admin/users') {
                    dispatch({type:'list',payload:query});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1,filter={}}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(usersService.list,page,PAGE_SIZE,filter);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *search({payload},{call,put,select}) {
            yield put({type: 'searched',payload});
            const page=yield select(state => state.users.page);
            const filter=yield select(state=>state.users.filter);
            yield put({type:'list',payload:{page,filter}});
        },
        *create({payload},{call,put,select}) {
            yield call(usersService.create,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(usersService.update,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(usersService.del,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
        },
        *delMulti({payload},{call,put,select}) {
            yield call(usersService.delMulti,payload);
            const page=yield select(state=>state.users.page);
            yield put({type:'list',payload:{page}});
       }
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
        },
      searched(state, action) {
            return { ...state, filter:action.payload };
      },
    },
  };

9.3 services/users.js [#](#t289.3 services/users.js)

users/services/users.js

import request from '../../../../utils/request';
import querystring from 'querystring';
export function list(page,limit,filter) {
  return request(`/api/users?page=${page}&limit=${limit}&${querystring.stringify(filter)}`);
}

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

export function update(values) {
  return request(`/api/users/${values.id}`,{
    method: 'PUT',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(values)
  });
}

export function del(id) {
  return request(`/api/users/${id}`,{
    method: 'DELETE'
  });
}
export function delMulti(ids) {
  return request(`/api/users`,{
    method: 'DELETE',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(ids)
  });
}

9.4 ../../dva.js [#](#t299.4 ../../dva.js)

../../dva.js

import { message } from 'antd';

export function config() {
  return {
    onError(err) {
      err.preventDefault();
      message.error(err.message);
    },
    initialState: {

    },
  };
}

10. 角色管理 [#](#t3010. 角色管理)

10.1 /roles/page.js [#](#t3110.1 /roles/page.js)

/roles/page.js

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message} from 'antd';
import { connect } from 'dva';
const FormItem=Form.Item;
const entity='roles';
class Comp extends Component{
    state={
        visible: false,
        isCreate: true,
        record: {},
        selectedRowKeys:[]
    }
    handleModalOk=() => {
        this.editForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isCreate,record}=this.state;
                if (isCreate) {
                    this.props.dispatch({
                        type: `${entity}/create`,
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: `${entity}/update`,
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    visible: false,
                    isCreate: true,
                    record:{}
                });
            }
        });
    }
    del=(id) => {
        this.props.dispatch({
            type: `${entity}/del`,
            payload: id
        });
    }
    create=() => {
        this.setState({
            record: {},
            visible: true,
            isCreate:true
        });
    }
    edit=(record) => {
        this.setState({
            record,
            visible: true,
            isCreate:false
        });
    }
    deleteSelected=() => {
        this.props.dispatch({
            type: `${entity}/delMulti`,
            payload: this.state.selectedRowKeys
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: `/admin/${entity}`,
            query:{page}
        }));
    }
    onRowClick = (record, key) => {

    }
    handleSearch=() => {
        let filter=this.searchForm.props.form.getFieldsValue();
        let keys=Object.keys(filter);
        keys.forEach(key => {
            if (filter[key]==''||typeof filter[key] =='undefined')
                delete filter[key];
        });
        console.log(filter);
        this.props.dispatch({
            type: `${entity}/search`,
            payload:filter
        });
    }
    render() {
        const columns=[
            {
                title: '角色名',
                dataIndex: 'name',
                key: 'name'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.edit(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.del(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading}=this.props;
        dataSource.forEach(row => row.key=row.id);
        const rowSelection={
            type:'checkbox',
            selectedRowKeys: this.state.selectedRowKeys,
            onChange: (selectedRowKeys,selectedRows) => {
                this.setState({selectedRowKeys});
            }
        }
        return (
            <Fragment>
                <Card>
                    <SearchForm
                        onSearch={this.handleSearch}
                        wrappedComponentRef={inst=>this.searchForm=inst}
                    />
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.create}>添加</Button>
                        <Button
                            onClick={this.deleteSelected}
                            icon="delete" style={{marginLeft: 5}}>全部删除</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                        rowSelection={rowSelection}
                    />
                    <EditModal
                            title={this.state.isCreate?'新增数据':'编辑数据'}
                            visible={this.state.visible}
                            record={this.state.record}
                            onCancel={() => this.setState({visible: false})}
                            onOk={this.handleModalOk}
                            wrappedComponentRef={instance => this.editForm = instance}
                    />
                </Card>
            </Fragment>
        )
    }
}

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

class EditModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="角色名" {...formItemLayout}>
                        {
                            getFieldDecorator('name',{
                                initialValue: record.name,
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
EditModal=Form.create()(EditModal);

export default connect(
    state => ({
        ...state[entity],
        loading:state.loading.models[entity]
    })
)(Comp);

10.2 models/users.js [#](#t3210.2 models/users.js)

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

import {PAGE_SIZE} from '../constants';
import * as service from '../services/users';
const entity='roles';
export default {
    namespace:entity,
    state: {
        list:[],
        total:0,
        page: 1,
        filter: {}
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == `/admin/${entity}`) {
                    dispatch({type:'list',payload:query});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1,filter={}}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(service.list,page,PAGE_SIZE,filter);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *search({payload},{call,put,select}) {
            yield put({type: 'searched',payload});
            const page=yield select(state => state[entity].page);
            const filter=yield select(state=>state[entity].filter);
            yield put({type:'list',payload:{page,filter}});
        },
        *create({payload},{call,put,select}) {
            yield call(service.create,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(service.update,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(service.del,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *delMulti({payload},{call,put,select}) {
            yield call(service.delMulti,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
       }
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
        },
      searched(state, action) {
            return { ...state, filter:action.payload };
      },
    },

  };

10.3 services/users.js [#](#t3310.3 services/users.js)

pages/admin/roles/services/users.js

import request from '../../../../utils/request';
import querystring from 'querystring';
const entity='roles';
export function list(page,limit,filter) {
  return request(`/api/${entity}?page=${page}&limit=${limit}&${querystring.stringify(filter)}`);
}

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 delMulti(ids) {
  return request(`/api/${entity}`,{
    method: 'DELETE',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(ids)
  });
}

11. 资源管理 [#](#t3411. 资源管理)

12. 点击行多选 [#](#t3512. 点击行多选)

onRow={(record) => {
       return {
       onClick: () => {
       let index=this.state.selectedRowKeys.indexOf(record.key);
       if (index==-1) {
       this.state.selectedRowKeys.push(record.key);
      } else {
        this.state.selectedRowKeys.splice(index,1);
      }
this.setState({
  selectedRowKeys: [...this.state.selectedRowKeys]
});
}
};
}}

13.给角色设置权限 #

13.1 models/roles.js [#](#t3713.1 models/roles.js)

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

import {PAGE_SIZE} from '../constants';
import * as service from '../services/roles';
const entity='roles';
export default {
    namespace:entity,
    state: {
        list:[],
        total:0,
        page: 1,
        filter: {},
        resources: []
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == `/admin/${entity}`) {
                    dispatch({type: 'list',payload: query});
                    dispatch({type:'getResources'});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1,filter={}}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(service.list,page,PAGE_SIZE,filter);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *search({payload},{call,put,select}) {
            yield put({type: 'searched',payload});
            const page=yield select(state => state[entity].page);
            const filter=yield select(state=>state[entity].filter);
            yield put({type:'list',payload:{page,filter}});
        },
        *create({payload},{call,put,select}) {
            yield call(service.create,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(service.update,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(service.del,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *delMulti({payload},{call,put,select}) {
            yield call(service.delMulti,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *setPermissions({payload},{call,put,select}) {
            yield call(service.setPermissions,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
         *getResources({},{call,put,select}) {
             const {data: resources}=yield call(service.getResources);
             console.log(resources);
             yield put({type: 'gotResources',payload: resources.data});
         }
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
        },
      searched(state, action) {
            return { ...state, filter:action.payload };
      },
      gotResources(state, action) {
            return { ...state, resources:action.payload };
      }
    },

  };

13.2 services/roles.js [#](#t3813.2 services/roles.js)

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

import request from '../../../../utils/request';
import querystring from 'querystring';
const entity='roles';
export function list(page,limit,filter) {
  return request(`/api/${entity}?page=${page}&limit=${limit}&${querystring.stringify(filter)}`);
}

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 delMulti(ids) {
  return request(`/api/${entity}`,{
    method: 'DELETE',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(ids)
  });
}
export function setPermissions(payload) {
  return request(`/api/${entity}/setPermissions`,{
    method: 'POST',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(payload)
  });
}
export function getResources() {
  return request(`/api/${entity}/getResources`);
}

13.3 roles/page.js [#](#t3913.3 roles/page.js)

src/pages/admin/roles/page.js

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message,Tree} from 'antd';
import { connect } from 'dva';
import {runInThisContext} from 'vm';
const FormItem=Form.Item;
const TreeNode=Tree.TreeNode;
const entity='roles';
class Comp extends Component{
    state={
        visible: false,
        isCreate: true,
        record: {},
        selectedRowKeys: [],
        setPermissionVisible: false,
        checkedKeys: []
    }
    handleModalOk=() => {
        this.editForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isCreate,record}=this.state;
                if (isCreate) {
                    this.props.dispatch({
                        type: `${entity}/create`,
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: `${entity}/update`,
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    visible: false,
                    isCreate: true,
                    record:{}
                });
            }
        });
    }
    del=(id) => {
        this.props.dispatch({
            type: `${entity}/del`,
            payload: id
        });
    }
    create=() => {
        this.setState({
            record: {},
            visible: true,
            isCreate:true
        });
    }
    edit=(record) => {
        this.setState({
            record,
            visible: true,
            isCreate:false
        });
    }
    deleteSelected=() => {
        this.props.dispatch({
            type: `${entity}/delMulti`,
            payload: this.state.selectedRowKeys
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: `/admin/${entity}`,
            query:{page}
        }));
    }

    handleSearch=() => {
        let filter=this.searchForm.props.form.getFieldsValue();
        let keys=Object.keys(filter);
        keys.forEach(key => {
            if (filter[key]==''||typeof filter[key] =='undefined')
                delete filter[key];
        });
        this.props.dispatch({
            type: `${entity}/search`,
            payload:filter
        });
    }
    setRolePermission=() => {
        if (this.state.record.id) {
            this.setState({setPermissionVisible:true,checkedKeys:this.state.record.resourceIds});
        } else {
            message.warn('请先选择一个角色!');
        }
    }
    setPermissionModalOk=() => {
        let {record,checkedKeys}=this.state;
        this.props.dispatch({
            type: 'roles/setPermissions',
            payload:{roleId:record.id,resourceIds:checkedKeys}
        });
        this.setState({setPermissionVisible:false,selectedRowKeys: [],checkedKeys:[],record:{}});
    }
    onCheck=(checkedKeys) => {
        this.setState({checkedKeys});
    }
    render() {
        const columns=[
            {
                title: '角色名',
                dataIndex: 'name',
                key: 'name'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.edit(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.del(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading,resources}=this.props;
        console.log('resources',resources);
        dataSource.forEach(row => row.key=row.id);
        const rowSelection={
            type:'radio',
            selectedRowKeys: this.state.selectedRowKeys,
            onChange: (selectedRowKeys,selectedRows) => {
                this.setState({selectedRowKeys,record:selectedRows[0]});
            }
        }
        return (
            <Fragment>
                <Card>
                    <SearchForm
                        onSearch={this.handleSearch}
                        wrappedComponentRef={inst=>this.searchForm=inst}
                    />
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.create}>添加</Button>
                        <Button
                            onClick={this.deleteSelected}
                            icon="delete" style={{marginLeft: 5}}>全部删除</Button>
                        <Button icon="solution" onClick={this.setRolePermission}>设置权限</Button>
                        <Button icon="team" onClick={this.setUserRole}>用户授权</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                        rowSelection={rowSelection}
                    />
                    <EditModal
                            title={this.state.isCreate?'新增数据':'编辑数据'}
                            visible={this.state.visible}
                            record={this.state.record}
                            onCancel={() => this.setState({visible: false})}
                            onOk={this.handleModalOk}
                            wrappedComponentRef={instance => this.editForm = instance}
                    />
                    <PermissionModal
                            visible={this.state.setPermissionVisible}
                            record={this.state.record}
                            resources={resources}
                            checkedKeys={this.state.checkedKeys}
                            onCheck={this.onCheck}
                            onCancel={() => this.setState({setPermissionVisible: false})}
                            onOk={this.setPermissionModalOk}
                    />
                </Card>
            </Fragment>
        )
    }
}

class PermissionModal extends Component{

    renderTreeNode=(children) => {
        return children.map(child => {
            if (child.children.length>0) {
                return (<TreeNode title={child.name} key={child.id}>
                    {this.renderTreeNode(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}
                destroyOnClose
                onCancel={onCancel}
            >
            <Tree
                    checkable
                    defaultExpandAll
                    onCheck={onCheck}
                    checkedKeys={checkedKeys}
            >
                    <TreeNode title="平台权限" key={0} disabled>
                        {this.renderTreeNode(resources)}
                    </TreeNode>
                </Tree>
            </Modal>
        )
    }
}
PermissionModal=Form.create()(PermissionModal);

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

class EditModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="角色名" {...formItemLayout}>
                        {
                            getFieldDecorator('name',{
                                initialValue: record.name,
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
EditModal=Form.create()(EditModal);

export default connect(
    state => ({
        ...state[entity],
        loading:state.loading.models[entity]
    })
)(Comp);

14.给角色加用户 #

14.1 roles/page.js [#](#t4114.1 roles/page.js)

src/pages/admin/roles/page.js

import React,{Component,Fragment} from 'react';
import {routerRedux} from 'dva/router';
import {PAGE_SIZE} from './constants';
import {Menu,Icon,Card,Table,Button,Form,Input,Select,Popconfirm,Modal,message,Tree,Transfer} from 'antd';
import { connect } from 'dva';
import {runInThisContext} from 'vm';
const FormItem=Form.Item;
const TreeNode=Tree.TreeNode;
const entity='roles';
class Comp extends Component{
    state={
        visible: false,
        isCreate: true,
        record: {},
        selectedRowKeys: [],
        setPermissionVisible: false,
        checkedKeys: [],
        setAssignUserVisible: false,
        targetKeys:[],
    }
    handleModalOk=() => {
        this.editForm.props.form.validateFields((err,values) => {
            if (err) {
                message.info('表单填写不合法!');
            } else {
                let {isCreate,record}=this.state;
                if (isCreate) {
                    this.props.dispatch({
                        type: `${entity}/create`,
                        payload:values
                    });
                } else {
                    this.props.dispatch({
                        type: `${entity}/update`,
                        payload: {...values,id:record.id}
                    });
                }

                this.setState({
                    visible: false,
                    isCreate: true,
                    record:{}
                });
            }
        });
    }
    del=(id) => {
        this.props.dispatch({
            type: `${entity}/del`,
            payload: id
        });
    }
    create=() => {
        this.setState({
            record: {},
            visible: true,
            isCreate:true
        });
    }
    edit=(record) => {
        this.setState({
            record,
            visible: true,
            isCreate:false
        });
    }
    deleteSelected=() => {
        this.props.dispatch({
            type: `${entity}/delMulti`,
            payload: this.state.selectedRowKeys
        });
    }
    pageChangeHandler=(page) => {
        this.props.dispatch(routerRedux.push({
            pathname: `/admin/${entity}`,
            query:{page}
        }));
    }

    handleSearch=() => {
        let filter=this.searchForm.props.form.getFieldsValue();
        let keys=Object.keys(filter);
        keys.forEach(key => {
            if (filter[key]==''||typeof filter[key] =='undefined')
                delete filter[key];
        });
        this.props.dispatch({
            type: `${entity}/search`,
            payload:filter
        });
    }
    setRolePermission=() => {
        if (this.state.record.id) {
            this.setState({setPermissionVisible:true,checkedKeys:this.state.record.resourceIds});
        } else {
            message.warn('请先选择一个角色!');
        }
    }

       setUserRole=() => {
            if (this.state.record.id) {
                this.setState({setAssignUserVisible:true,targetKeys:this.state.record.users});
            } else {
                message.warn('请先选择一个角色!');
            }
        }

    setPermissionModalOk=() => {
        let {record,checkedKeys}=this.state;
        this.props.dispatch({
            type: 'roles/setPermissions',
            payload:{roleId:record.id,resourceIds:checkedKeys}
        });
        this.setState({setPermissionVisible:false,selectedRowKeys: [],checkedKeys:[],record:{}});
    }
    setAssignUserModalOk=() => {
        let {record,targetKeys}=this.state;
        this.props.dispatch({
            type: 'roles/assignUsers',
            payload:{roleId:record.id,userIds:targetKeys}
        });
        this.setState({setAssignUserVisible:false,selectedRowKeys: [],targetKeys:[],record:{}});
    }
    onCheck=(checkedKeys) => {
        this.setState({checkedKeys});
    }
    assignUsers=(targetKeys) => {
        this.setState({targetKeys});
    }
    render() {
        const columns=[
            {
                title: '角色名',
                dataIndex: 'name',
                key: 'name'
            },
            {
                title: '操作',
                key: 'operation',
                render: (val,record,index) => {
                    return (
                        <Button.Group>
                            <Button
                                onClick={()=>this.edit(record)}
                                style={{marginLeft: 10}} icon="edit">编辑</Button>
                            <Popconfirm onConfirm={()=>this.del(record.id)}>
                                <Button
                                    style={{marginLeft: 10}}
                                    icon="delete">删除</Button>
                            </Popconfirm>
                        </Button.Group>
                    )
                }
            }
        ]
        const {total,list: dataSource,page: current,loading,resources,allUsers}=this.props;
        dataSource.forEach(row => row.key=row.id);
        allUsers.forEach(row => row.key=row.id);
        const rowSelection={
            type:'radio',
            selectedRowKeys: this.state.selectedRowKeys,
            onChange: (selectedRowKeys,selectedRows) => {
                this.setState({selectedRowKeys,record:selectedRows[0]});
            }
        }
        return (
            <Fragment>
                <Card>
                    <SearchForm
                        onSearch={this.handleSearch}
                        wrappedComponentRef={inst=>this.searchForm=inst}
                    />
                </Card>
                <Card>
                    <Button.Group style={{marginBottom:10}}>
                      <Button icon="plus" onClick={this.create}>添加</Button>
                        <Button
                            onClick={this.deleteSelected}
                            icon="delete" style={{marginLeft: 5}}>全部删除</Button>
                        <Button icon="solution" onClick={this.setRolePermission}>设置权限</Button>
                        <Button icon="team" onClick={this.setUserRole}>用户授权</Button>
                   </Button.Group>
                    <Table
                        columns={columns}
                        dataSource={dataSource}
                        loading={loading}
                        pagination={{
                            total,
                            current,
                            pageSize:PAGE_SIZE,
                            onChange: this.pageChangeHandler,
                            showQuickJumper:true,
                            showTotal: (total) => {
                                return `共${total}条`
                            }
                        }}
                        rowSelection={rowSelection}
                    />
                    <EditModal
                            title={this.state.isCreate?'新增数据':'编辑数据'}
                            visible={this.state.visible}
                            record={this.state.record}
                            onCancel={() => this.setState({visible: false})}
                            onOk={this.handleModalOk}
                            wrappedComponentRef={instance => this.editForm = instance}
                    />
                    <PermissionModal
                            visible={this.state.setPermissionVisible}
                            record={this.state.record}
                            resources={resources}
                            checkedKeys={this.state.checkedKeys}
                            onCheck={this.onCheck}
                            onCancel={() => this.setState({setPermissionVisible: false})}
                            onOk={this.setPermissionModalOk}
                    />
                    <AssignModal
                            visible={this.state.setAssignUserVisible}
                            record={this.state.record}
                            allUsers={allUsers}
                            targetKeys={this.state.targetKeys}
                            assignUsers={this.assignUsers}
                            onCancel={() => this.setState({setAssignUserVisible: false})}
                            onOk={this.setAssignUserModalOk}
                    />
                </Card>
            </Fragment>
        )
    }
}

class AssignModal extends Component{
    filterOption = (inputValue, option) => {
        return option.username.indexOf(inputValue) > -1;
    };
    handleChange = (targetKeys) => {
        this.props.assignUsers(targetKeys);
    };
    render() {
        let {onCheck,visible,onOk,onCancel,checkedKeys,resources}=this.props;
        return (
            <Modal
                visible={visible}
                title={'为角色设置权限'}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
              <Transfer
                        listStyle={{width: 200,height: 400}}
                        dataSource={this.props.allUsers}
                        showSearch
                        titles={['待选用户', '已选用户']}
                        searchPlaceholder='输入用户名'
                        filterOption={this.filterOption}
                        targetKeys={this.props.targetKeys}
                        onChange={this.handleChange}
                        render={item => item.username}
                    />
            </Modal>
        )
    }
}
AssignModal=Form.create()(AssignModal);

class PermissionModal extends Component{

    renderTreeNode=(children) => {
        return children.map(child => {
            if (child.children.length>0) {
                return (<TreeNode title={child.name} key={child.id}>
                    {this.renderTreeNode(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}
                destroyOnClose
                onCancel={onCancel}
            >
            <Tree
                    checkable
                    defaultExpandAll
                    onCheck={onCheck}
                    checkedKeys={checkedKeys}
            >
                    <TreeNode title="平台权限" key={0} disabled>
                        {this.renderTreeNode(resources)}
                    </TreeNode>
                </Tree>
            </Modal>
        )
    }
}
PermissionModal=Form.create()(PermissionModal);

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

class EditModal extends Component{
    render() {
        const {form: {getFieldDecorator},visible,title,onOk,onCancel,record}=this.props;
        const formItemLayout={
            labelCol: {span: 4},
            wrapperCol:{span:20}
        }
        return (
            <Modal
                visible={visible}
                title={title}
                onOk={onOk}
                destroyOnClose
                onCancel={onCancel}
            >
                <Form layout="horizontal">
                    <Form.Item label="角色名" {...formItemLayout}>
                        {
                            getFieldDecorator('name',{
                                initialValue: record.name,
                            })(
                                <Input/>
                            )
                        }
                    </Form.Item>
               </Form>
            </Modal>
        )
    }
}
EditModal=Form.create()(EditModal);

export default connect(
    state => ({
        ...state[entity],
        loading:state.loading.models[entity]
    })
)(Comp);

14.2 models/roles.js [#](#t4214.2 models/roles.js)

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

import {PAGE_SIZE} from '../constants';
import * as service from '../services/roles';
const entity='roles';
export default {
    namespace:entity,
    state: {
        list:[],
        total:0,
        page: 1,
        filter: {},
        resources: [],
        allUsers:[]
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == `/admin/${entity}`) {
                    dispatch({type: 'list',payload: query});
                    dispatch({type: 'getResources'});
                    dispatch({type:'getUsers'});
                }
            });
      },
    },

    effects: {
        *list({payload: {page=1,filter={}}},{call,put}) {
            const {data: {data: {list,total}}}=yield call(service.list,page,PAGE_SIZE,filter);
            yield put({type: 'listed',payload: {list,total:parseInt(total,10),page:parseInt(page)}});
        },
        *search({payload},{call,put,select}) {
            yield put({type: 'searched',payload});
            const page=yield select(state => state[entity].page);
            const filter=yield select(state=>state[entity].filter);
            yield put({type:'list',payload:{page,filter}});
        },
        *create({payload},{call,put,select}) {
            yield call(service.create,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *update({payload},{call,put,select}) {
            yield call(service.update,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *del({payload},{call,put,select}) {
            yield call(service.del,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *delMulti({payload},{call,put,select}) {
            yield call(service.delMulti,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *setPermissions({payload},{call,put,select}) {
            yield call(service.setPermissions,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
        *assignUsers({payload},{call,put,select}) {
            yield call(service.assignUsers,payload);
            const page=yield select(state=>state[entity].page);
            yield put({type:'list',payload:{page}});
        },
         *getResources({},{call,put,select}) {
             const {data: {data}}=yield call(service.getResources);
             console.log('getResources',data);
             yield put({type: 'gotResources',payload: data});
        },
        *getUsers({},{call,put,select}) {
            const {data: {data}}=yield call(service.getUsers);
            console.log('getUsers',data);
            yield put({type: 'gotUsers',payload: data});
        }
    },

    reducers: {
      listed(state, action) {
           return { ...state, ...action.payload };
        },
      searched(state, action) {
            return { ...state, filter:action.payload };
      },
      gotResources(state, action) {
            return { ...state, resources:action.payload };
      },
      gotUsers(state, action) {
            return { ...state, allUsers:action.payload };
      }
    },

  };

14.3 services/roles.js [#](#t4314.3 services/roles.js)

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

import request from '../../../../utils/request';
import querystring from 'querystring';
const entity='roles';
export function list(page,limit,filter) {
  return request(`/api/${entity}?page=${page}&limit=${limit}&${querystring.stringify(filter)}`);
}

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 delMulti(ids) {
  return request(`/api/${entity}`,{
    method: 'DELETE',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(ids)
  });
}
export function setPermissions(payload) {
  return request(`/api/${entity}/setPermissions`,{
    method: 'POST',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(payload)
  });
}
export function assignUsers(payload) {
  return request(`/api/${entity}/assignUsers`,{
    method: 'POST',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(payload)
  });
}

export function getResources() {
  return request(`/api/${entity}/getResources`);
}
export function getUsers() {
  return request(`/api/${entity}/getUsers`);
}

15. 登录表单 [#](#t4415. 登录表单)

import React,{Component,Fragment} from 'react';
import {Layout,Form,Icon,Input,Button,Checkbox,Cascader,Select,Row,Col,AutoComplete} from 'antd';
import styled from 'styled-components';
const {Header,Footer,Sider,Content}=Layout;
const Option=Select.Option;
const AutoCompleteOption = AutoComplete.Option;
const FormItem=Form.Item;
export default class Login extends Component{
    render() {
        return (
            <Layout>
                <Content>
                  <WrappedLoginForm/>
                </Content>
                <Footer style={{ textAlign: 'center' }}> 张熙沐枫 ©2018</Footer>
            </Layout>
       )
   }
}
class LoginForm extends Component{
    state={isLogin: true,autoCompleteResult:[],confirmDirty:false}
    handleWebsiteChange=(value) => {
        let autoCompleteResult;
        if (value) {
            autoCompleteResult=['.com','.cn','.org'].map(domain=>`${value}${domain}`);
        } else {
            autoCompleteResult=[];
        }
        this.setState({autoCompleteResult});
    }
    handleConfirmBlur = (e) => {
        const value = e.target.value;
        this.setState({ confirmDirty: this.state.confirmDirty || !!value });
      }

    compareToFirstPassword=(rule,value,callback) => {
        const form=this.props.form;
        if (value && value !== form.getFieldValue('password')) {
            callback('密码不匹配');
        } else {
            callback();
        }
    }
    validateToNextPassword=(rule,value,callback) => {
        const form=this.props.form;
        if (value&&this.state.confirmDirty) {
            form.validateFields(['confirm'],{force:true});
        }
        callback();
    }
    render() {
        const {getFieldDecorator}=this.props.form;
        const loginForm=(
            <Form onSubmit={this.handleSubmit}>
                    <h3>欢迎登录</h3>
                    <FormItem>
                            {
                                getFieldDecorator('username',{
                                    rules:[{required:true,message:'请输入你的用户名'}]
                                })(<Input prefix={<Icon type="user" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="用户名"/>)
                            }
                    </FormItem>
                    <FormItem>
                            {
                                getFieldDecorator('password',{
                                    rules:[{required:true,message:'请输入你的密码'}]
                                })(<Input prefix={<Icon type="lock" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="密码"/>)
                            }
                    </FormItem>
                    <FormItem>
                        {
                            getFieldDecorator('remember',{
                                valuePropName: 'checked',
                                initialValue:true
                            })(
                                <Checkbox>记住密码</Checkbox>
                            )
                        }
                        <a href="" style={{float:'right'}}>忘记密码</a>
                        <Button type="primary" htmlType="submit" style={{width:'100%'}}>登录</Button>
                        没有账号? <a href="#" onClick={()=>this.setState({isLogin:false})}>立刻注册!</a>
                    </FormItem>
                </Form>
        )
        const formItemLayout={
            labelCol:{
                span:4
            },
            wrapperCol: {
                span:20
            }

        }
        const tailFormItemLayout={
            wrapperCol: {
                span: 20,
                offset:4
            }
        }
        const addresses=[
            {
                value:'guangdong',
                label: '广东',
                children: [
                    {
                        value:'guangzhou',
                        label: '广州',
                    },
                    {
                        value:'dongguan',
                        label: '东莞',
                    }
                ]
            },
            {
                value:'shandong',
                label: '山东',
                children: [
                    {
                        value:'jinan',
                        label: '济南',
                    },
                    {
                        value:'shouguang',
                        label: '寿光',
                    }
                ]
            }
        ]
        const countrySelector=getFieldDecorator('prefix',{initialValue: '86'})(
            <Select style={{width: 70}}>
                <Option value="86">+86</Option>
                <Option value="87">+87</Option>
            </Select>
        );
        const websiteOptions=this.state.autoCompleteResult.map(website => (
            <AutoCompleteOption key={website}>{website}</AutoCompleteOption>
        ));
        const signupForm=(
            <Form onSubmit={this.handleSubmit} style={{width:this.state.isLogin?'300px':'500px'}}>
                <h3>欢迎注册</h3>
                <FormItem label="用户名" {...formItemLayout}>
                            {
                                getFieldDecorator('username',{
                                    rules:[{required:true,message:'请输入你的用户名'}]
                                })(<Input prefix={<Icon type="user" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="用户名"/>)
                            }
                </FormItem>
                <FormItem label="密码" {...formItemLayout}>
                            {
                                getFieldDecorator('password',{
                            rules: [{required: true,message: '请输入你的密码'},
                                {validator:this.validateToNextPassword}
                                ]
                                })(<Input prefix={<Icon type="lock" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="密码"/>)
                            }
                </FormItem>
                <FormItem label="确认密码" {...formItemLayout}>
                            {
                                getFieldDecorator('confirm',{
                            rules: [{required: true,message: '请输入你的确认密码'},
                                {validator:this.compareToFirstPassword}
                                ]
                                })(<Input prefix={<Icon type="lock" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="确认密码"/>)
                            }
                </FormItem>
                <FormItem label="邮箱" {...formItemLayout}>
                            {
                                getFieldDecorator('email',{
                            rules: [{type:'email',message:'必须输入合法的邮箱地址'},{required:true,message:'请输入邮箱'}]
                                })(<Input prefix={<Icon type="mail" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="密码"/>)
                            }
                </FormItem>
                <FormItem label="住址" {...formItemLayout}>
                            {
                               getFieldDecorator('address',{
                                 initialValue:["guangdong","guangzhou"],
                                 rules: [{type:'array',required:true,message:'必须输入合法的地址'}]
                                })(<Cascader options={addresses}/>)
                            }
                </FormItem>
                <FormItem label="手机号" {...formItemLayout}>
                            {
                               getFieldDecorator('phone',{
                                 rules: [{required:true,message:'必须输入合法的手机号'}]
                        })(<Input addonBefore={countrySelector} style={{width:'100%'}}/>)
                            }
                </FormItem>
                <FormItem label="个人主页" {...formItemLayout}>
                            {
                               getFieldDecorator('website',{
                                 rules: [{required:true,message:'必须输入合法的网址'}]
                        })(
                            <AutoComplete
                            dataSource={websiteOptions}
                            onChange={this.handleWebsiteChange}
                            placeholder="网址"
                            >
                                <Input/>
                            </AutoComplete>
                        )
                            }
                </FormItem>
                <FormItem label="验证码" {...formItemLayout} extra="证明你不是机器人">
                    <Row gutter={8}>
                        <Col span={12}>
                        {
                               getFieldDecorator('captcha',{
                                 rules: [{required:true,message:'必须输入验证码'}]
                           })(<Input />)
                        }
                        </Col>
                        <Col span={12}>
                            <Button>获取验证码</Button>
                        </Col>
                    </Row>

                </FormItem>
                <FormItem {...tailFormItemLayout}>
                    {getFieldDecorator('agreement', {
                        valuePropName: 'checked',
                    })(
                        <Checkbox>我已经阅读并同意 <a href="">协议</a></Checkbox>
                    )}
                </FormItem>
                <FormItem>
                        <Button type="primary" htmlType="submit" style={{width:'100%'}}>注册</Button>
                        已有账号? <a href="#" onClick={()=>this.setState({isLogin:true})}>立即登录!</a>
                    </FormItem>
                </Form>
        )
        return (
            <FormWrapper>
                {this.state.isLogin?loginForm:signupForm}
            </FormWrapper>

        )
  }
}
const WrappedLoginForm = 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;
   }
`

16. 验证码 [#](#t4516. 验证码)

const captchaUrl=`http://127.0.0.1:7001/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+Date.now()} onClick={this.refreshCaptcha}/>
        </Col>
    </Row>
</FormItem>

17. 注册登录 [#](#t4617. 注册登录)

17.1 src/components/NavHeader.js [#](#t4717.1 src/components/NavHeader.js)

src/components/NavHeader.js

import React,{Component,Fragment} from 'react';
import {connect} from 'dva';
import {Layout,Menu,Icon} from 'antd';
import logo from '../assets/logo.png';
const {Header}=Layout;
class NavHeader extends Component{
    componentWillMount() {
        this.props.dispatch({
            type:'login/loadUser'
        });
    }
    render() {
        return (
            <Header>
                   <img className="logo" src={logo} alt="logo"/>
                   <span className="welcome">欢迎 {this.props.userInfo&&this.props.userInfo.username}</span>
            </Header>
        )
    }
}
export default connect(
    state => ({userInfo:state.login.userInfo})
)(NavHeader);

17.2 src/pages/admin/_layout.js

src/pages/admin/_layout.js

 import React,{Component,Fragment} from 'react';
 import {Layout,Menu,Icon} from 'antd';
 import NavHeader from '../../components/NavHeader';
 import NavLeft from '../../components/NavLeft';
 const {Header,Footer,Sider,Content}=Layout;
 export default class Admin extends Component{
     render() {
         return (
             <Layout>
                <NavHeader/>
                 <Layout>
                     <Sider>
                         <NavLeft/>

17.3 src/pages/admin/home/page.js [#](#t4917.3 src/pages/admin/home/page.js)

src/pages/admin/home/page.js

import React,{Component,Fragment} from 'react';
export default  class User extends Component{
    render(){
        return <h1 style={{textAlign: 'center',marginTop:100}}>欢迎光临后台系统</h1>
    }
}

17.4 src/pages/admin/resources/services/users.js [#](#t5017.4 src/pages/admin/resources/services/users.js)

src/pages/admin/resources/services/users.js

 import querystring from 'querystring';
 const entity='resources';
 export function list(page,limit,filter) {
   return request(`/${entity}?page=${page}&limit=${limit}&${querystring.stringify(filter)}`);
 }

 export function create(values) {
  return request(`/${entity}`,{
     method: 'POST',
     headers:{"Content-Type":"application/json"},
     body:JSON.stringify(values)
    }

 export function update(values) {

  return request(`/${entity}/${values.id}`,{
     method: 'PUT',
     headers:{"Content-Type":"application/json"},
     body:JSON.stringify(values)
    }

 export function del(id) {

  return request(`/${entity}/${id}`,{
     method: 'DELETE'
   });
 }
 export function delMulti(ids) {

  return request(`/${entity}`,{
     method: 'DELETE',
     headers:{"Content-Type":"application/json"},
     body:JSON.stringify(ids)

17.5 src/pages/login/index.js [#](#t5117.5 src/pages/login/index.js)

src/pages/login/index.js

 import React,{Component,Fragment} from 'react';
 import {Layout,Form,Icon,Input,Button,Checkbox,Cascader,Select,Row,Col,Alert,AutoComplete,message} from 'antd';
 import styled from 'styled-components';
 import { connect } from 'dva';
 const {Header,Footer,Sider,Content}=Layout;
 const Option=Select.Option;
 const AutoCompleteOption = AutoComplete.Option;
 const FormItem=Form.Item;

const captchaUrl=`http://127.0.0.1:7001/captcha`;
const entity='login';
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/changeLoginStatus'
         });
     }
     render() {
             <Layout>
                 <Content>
                     <WrappedLoginForm
                        isLogin={this.props.isLogin}
                        changeLoginStatus={this.changeLoginStatus}
                         handleSubmit={this.handleSubmit}

                        wrappedComponentRef={inst => this.userForm=inst}
                        errorInfo={this.props.errorInfo}
                     />
                 </Content>
                 <Footer style={{ textAlign: 'center' }}> 张熙沐枫 ©2018</Footer>

    }
 }
 class LoginForm extends Component{
    constructor() {
        super();
    }
    state={autoCompleteResult:[],confirmDirty:false}
     handleWebsiteChange=(value) => {
         let autoCompleteResult;
         if (value) {

         callback();
     }
     refreshCaptcha=(event) => {

        let target=event.target;
        fetch(captchaUrl,{credentials:"include"}).then(res => {
            return res.blob();
        }).then(imageBlob => {
            target.src = URL.createObjectURL(imageBlob);
        })
     }
     render() {
         const {getFieldDecorator}=this.props.form;

         const formItemLayout={
             labelCol:{
                 span:4
               const websiteOptions=this.state.autoCompleteResult.map(website => (
             <AutoCompleteOption key={website}>{website}</AutoCompleteOption>
         ));


        return (
            <FormWrapper>
                <Form onSubmit={this.props.handleSubmit} style={{width:this.state.isLogin?'300px':'500px'}}>
                 <h3>欢迎注册</h3>
                 <FormItem label="用户名" {...formItemLayout}>
                             {
                                 })(<Input prefix={<Icon type="lock" style={{color:'rbga(0,0,0,.25)'}}/>} placeholder="密码"/>)
                             }
                 </FormItem>





                    {!this.props.isLogin&&<FormItem label="确认密码" {...formItemLayout}>
                        {
                            getFieldDecorator('confirm',{
                                rules: [{required: true,message: '请输入你的确认密码'},
                                {validator: this.compareToFirstPassword}
                                 ]

                            })(<Input prefix={<Icon type="lock" style={{color: 'rbga(0,0,0,.25)'}} />} placeholder="确认密码" />)
                        }
                    </FormItem>}
                    {!this.props.isLogin&&<FormItem label="邮箱" {...formItemLayout}>
                         {

[{required:true,message:'必须输入验证码'}]

                            getFieldDecorator('email',{
                                rules: [{type: 'email',message: '必须输入合法的邮箱地址'},{required: true,message: '请输入邮箱'}]
                            })(<Input prefix={<Icon type="mail" style={{color: 'rbga(0,0,0,.25)'}} />} placeholder="密码" />)
                         }

                    </FormItem>}
                    {!this.props.isLogin&&<FormItem label="住址" {...formItemLayout}>
                        {
                            getFieldDecorator('address',{
                                initialValue: ["guangdong","guangzhou"],
                                rules: [{type: 'array',required: true,message: '必须输入合法的地址'}]
                            })(<Cascader options={addresses} />)
                        }
                    </FormItem>}
                    {!this.props.isLogin&&<FormItem label="手机号" {...formItemLayout}>
                        {
                            getFieldDecorator('phone',{
                                rules: [{required: true,message: '必须输入合法的手机号'}]
                            })(<Input addonBefore={countrySelector} style={{width: '100%'}} />)
                        }
                    </FormItem>}
                    {!this.props.isLogin&&<FormItem label="个人主页" {...formItemLayout}>
                        {
                            getFieldDecorator('website',{
                                rules: [{required: true,message: '必须输入合法的网址'}]
                            })(
                                <AutoComplete
                                    dataSource={websiteOptions}
                                    onChange={this.handleWebsiteChange}
                                    placeholder="网址"
                                >
                                    <Input />
                                </AutoComplete>
                            )
                        }
                    </FormItem>}
                    {!this.props.isLogin&&<FormItem label="验证码" {...formItemLayout} extra="证明你不是机器人">
                        <Row gutter={8}>
                            <Col span={12}>
                                {
                                    getFieldDecorator('captcha',{
                                        rules: [{required: true,message: '必须输入验证码'}]
                                    })(<Input />)
                                }
                            </Col>
                            <Col span={12}>
                                <img style={{width:100,height:40}} onClick={this.refreshCaptcha} />
                            </Col>
                        </Row>
                    </FormItem>}
                    {this.props.isLogin?<FormItem>
                        <Button type="primary" htmlType="submit" style={{width:'100%'}}>登录</Button>
                        没有账号? <a href="#" onClick={this.props.changeLoginStatus}>立刻注册!</a>
                    </FormItem>:<FormItem>
                        <Button type="primary" htmlType="submit" style={{width: '100%'}}>注册</Button>
                        已有账号? <a href="#" onClick={this.props.changeLoginStatus}>立即登录!</a>
                        </FormItem>}
                    {this.props.errorInfo&&<Alert style={{textAlign:'center'}} message={this.props.errorInfo} type="error" />}

                 </Form>

         )
       box-shadow:1px 1px 2px 1px #aaaaaa,-1px -1px 2px 1px #aaaaaa;
   }
`

export default connect(
    state => ({
        ...state[entity],
        loading:state.loading.models[entity]
    })
)(Login);

17.6 src/pages/login/models/login.js [#](#t5217.6 src/pages/login/models/login.js)

src/pages/login/models/login.js

import * as service from '../services/login';
import {Router,Route,routerRedux} from 'dva/router';
import {decode} from 'jsonwebtoken';
const entity='login';
export default {
    namespace:entity,
    state: {
        isLogin: true,
        errorInfo: '',
        userInfo:null
    },

    subscriptions: {
        setup({dispatch,history}) {
            history.listen(({pathname,query}) => {
                if (pathname == '/login') {

                } else if (pathname == '/signup') {

                }
            });
      },
    },
    effects: {
        *signup({payload},{call,put}) {
            const ret=yield call(service.signup,payload);
            debugger;
            yield put({type:'changeLoginStatus'});
        },
        *login({payload},{call,put}) {
            const {data}=yield call(service.login,payload);
            if (data.code==0) {
                let token=data.data;
                const user = decode(token);
                yield put({type:'setUserInfo',payload:user});
                localStorage.setItem('token',token);
                yield put(routerRedux.push('/admin/home'));
            } else {
                yield put({type:'setErrorInfo',payload:data.error});
            }
        },
        *loadUser({payload},{call,put}) {
            let token=localStorage.getItem('token');
            if (token) {
                const user = decode(token);
                yield put({type:'setUserInfo',payload:user});
            } else {
                yield put(routerRedux.push('/login'));
            }
        }
    },

    reducers: {
        //修改是登录还是注册的状态
        changeLoginStatus(state, action) {
           return { ...state,  isLogin:!state.isLogin};
        },
        setErrorInfo(state,{payload}) {
            return { ...state,  errorInfo:payload};
        },
        setUserInfo(state,{payload}) {
            return { ...state,  userInfo:payload};
        }
    },

  };

17.7 src/pages/login/services/login.js [#](#t5317.7 src/pages/login/services/login.js)

src/pages/login/services/login.js

import request from '../../../utils/request';
export function signup(values) {
  return request(`/signup`,{
    method: 'POST',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(values)
  });
}
export function login(values) {
  return request(`/login`,{
    method: 'POST',
    headers:{"Content-Type":"application/json"},
    body:JSON.stringify(values)
  });
}

17.8 src/utils/request.js [#](#t5417.8 src/utils/request.js)

src/pages/login/services/login.js

  /* @param  {object} [options] The options we want to pass to "fetch"
  * @return {object}           An object containing either "data" or "err"
  */

const BaseURL='http://127.0.0.1:7001';
export default function request(url,options={}) {
  let token=localStorage.getItem('token');
  if (token) {
    options.headers=options.headers||{};
    options.headers.authorization=token;
  }
  options.credentials="include";
  return fetch(`${BaseURL}${url}`, options)
     .then(checkStatus)
     .then(parseJSON)
     .then(data => ({ data }))

参考 #

Gitalking ...

Markdown is supported

Be the first guy leaving a comment!