生成项目
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" : "" }
}
}
}
#
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 ...
Be the first guy leaving a comment!