安装dva-cli并创建应用
$ npm i dva-cli@next -g
$ dva -v
1.0.0-beta.4
2. 配置代理,能通过restful 的方式访问 [#](#t12. 配置代理,能通过restful 的方式访问)
.webpackrc
{
"proxy": {
"/api": {
"target": "http://localhost:7001",
"changeOrigin": true,
"pathRewrite": { "^/api" : "" }
}
}
}
#
3.新建用户路由src/pages/users/page.js
export default () => {
return (
<div>
用户管理
</div>
)
}
4. model 和 service [#](#t34. model 和 service)
4.1 src/utils/request.js [#](#t44.1 src/utils/request.js)
export default async function request(url,options) {
const response=fetch(url,options);
checkStatus(response);
const data=await response.json();
const ret={
data,
headers:{}
}
if (response.headers.get('x-total-count')) {
ret.headers['x-total-count']=response.headers.get('x-total-count');
}
return ret;
}
4.2 models/users.js [#](#t54.2 models/users.js)
src/pages/users/models/users.js
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total:null
},
reducers: {
save(state,{payload:{data:list,total}}) {
return {...state,list,total};
}
},
effects: {
*fetch({payload: {page}},{call,put}) {
const {data,headers}=yield call(usersService.fetch,{page});
yield put({type:'save',payload:{data,total:headers['x-total-count']}});
}
},
subscriptions: {
setup({dispatch,history}) {
return history.listen((pathname,query) => {
if (pathname=='/users') {
dispatch({type:'fetch',payload:query});
}
});
}
}
}
4.3 services/users.js [#](#t64.3 services/users.js)
pages/users/services/users.js
import request from '../../../utils/request';
export function fetch(page=1) {
return request(`/api/users?_page=${page}&_limit=5`);
}
5. 显示用户列表 [#](#t75. 显示用户列表)
5.1 src/pages/users/models/users.js [#](#t85.1 src/pages/users/models/users.js)
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
page:1
},
reducers: {
save(state,{payload:{data:list,total,page}}) {
return {...state,list,total,page};
}
},
effects: {
*fetch({payload: {page=1}},{call,put}) {
const {data,headers}=yield call(usersService.fetch,{page});
yield put({
type: 'save',payload: {
data,
total: parseInt(headers['x-total-count'],10),
page: parseInt(page,10)
}
});
}
},
subscriptions: {
setup({dispatch,history}) {
return history.listen(({pathname,query}) => {
if (pathname=='/users') {
dispatch({type:'fetch',payload:query});
}
});
}
}
}
5.2 src/pages/users/page.js [#](#t95.2 src/pages/users/page.js)
import Users from './components/Users';
export default () => {
return (
<Users />
)
}
5.3 src/pages/users/services/users.js [#](#t105.3 src/pages/users/services/users.js)
import request from '../../../utils/request';
import {PAGE_SIZE} from '../contants';
export function fetch(page=1) {
return request(`/api/users?_page=${page}&_limit=${PAGE_SIZE}`);
}
5.4 src/utils/request.js [#](#t115.4 src/utils/request.js)
import fetch from 'dva/fetch';
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function request(url,options) {
const response=await fetch(url,options);
checkStatus(response);
const data=await response.json();
const ret={
data,
headers:{}
}
if (response.headers.get('x-total-count')) {
ret.headers['x-total-count']=response.headers.get('x-total-count');
}
return ret;
}
5.5 src/pages/users/contants.js [#](#t125.5 src/pages/users/contants.js)
export const PAGE_SIZE = 3;
5.6. src/pages/users/components/Users.js [#](#t135.6. src/pages/users/components/Users.js)
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm} from 'antd';
import {PAGE_SIZE} from '../contants';
function Users({list: dataSource,total,page: current,dispatch}) {
function del(id) {
console.log('delete ',id);
dispatch({type:'users/del',payload:id});
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,{id}) => (
<span>
<a href="">编辑</a>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(id)}>
<a>删除</a>
</Popconfirm>
</span>
)
}]
return (
<Card>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
/>
</Card>
)
}
export default connect(
state => state.users
)(Users);
6. 添加 layout [#](#t146. 添加 layout)
6.1 Header.js [#](#t156.1 Header.js)
import { Menu, Icon } from 'antd';
import Link from 'umi/link';
function Header({ location }) {
return (
<Menu
selectedKeys={[location.pathname]}
mode="horizontal"
theme="dark"
>
<Menu.Item key="/">
<Link to="/"><Icon type="home" />Home</Link>
</Menu.Item>
<Menu.Item key="/users">
<Link to="/users"><Icon type="bars" />Users</Link>
</Menu.Item>
</Menu>
);
}
export default Header;
6.2 layouts/index.js [#](#t166.2 layouts/index.js)
layouts/index.js
import React from 'react';
import Header from './Header';
import withRouter from 'umi/withRouter';
function Layout({children,location}) {
return (
<div>
<Header location={location}/>
{children}
</div>
)
}
export default withRouter(Layout);
7. 处理loading状态 [#](#t177. 处理loading状态)
7.1 pages/users/components/Users.js [#](#t187.1 pages/users/components/Users.js)
src/pages/users/components/Users.js
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm} from 'antd';
import {PAGE_SIZE} from '../contants';
function Users({list: dataSource,loading,total,page: current,dispatch}) {
function del(id) {
console.log('delete ',id);
dispatch({type:'users/del',payload:id});
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,{id}) => (
<span>
<a href="">编辑</a>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(id)}>
<a>删除</a>
</Popconfirm>
</span>
)
}]
return (
<Card>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
loading={loading}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
/>
</Card>
)
}
export default connect(
state => ({...state.users,loading:state.loading.models.users})
)(Users);
8. 处理分页 [#](#t198. 处理分页)
8.1 src/pages/users/components/Users.js [#](#t208.1 src/pages/users/components/Users.js)
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm} from 'antd';
import {PAGE_SIZE} from '../contants';
import {routerRedux} from 'dva/router';
function Users({list: dataSource,loading,total,page: current,dispatch}) {
function del(id) {
console.log('delete ',id);
dispatch({type:'users/del',payload:id});
}
function handlePageChange(page) {
dispatch(routerRedux.push({
pathname: '/users',
query:{page}
}));
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,{id}) => (
<span>
<a href="">编辑</a>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(id)}>
<a>删除</a>
</Popconfirm>
</span>
)
}]
return (
<Card>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
loading={loading}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
onChange={handlePageChange}
/>
</Card>
)
}
export default connect(
state => ({...state.users,loading:state.loading.models.users})
)(Users);
9. 处理用户删除 [#](#t219. 处理用户删除)
9.1 src/pages/users/components/Users.js [#](#t229.1 src/pages/users/components/Users.js)
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm} from 'antd';
import {PAGE_SIZE} from '../contants';
import {routerRedux} from 'dva/router';
function Users({list: dataSource,loading,total,page: current,dispatch}) {
function del(id) {
dispatch({type:'users/del',payload:id});
}
function handlePageChange(page) {
dispatch(routerRedux.push({
pathname: '/users',
query:{page}
}));
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,{id}) => (
<span>
<a href="">编辑</a>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(id)}>
<a>删除</a>
</Popconfirm>
</span>
)
}]
return (
<Card>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
loading={loading}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
onChange={handlePageChange}
/>
</Card>
)
}
export default connect(
state => ({...state.users,loading:state.loading.models.users})
)(Users);
9.2 src/pages/users/models/users.js [#](#t239.2 src/pages/users/models/users.js)
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
page:1
},
reducers: {
save(state,{payload:{data:list,total,page}}) {
return {...state,list,total,page};
}
},
effects: {
*fetch({payload: {page=1}},{call,put}) {
const {data,headers}=yield call(usersService.fetch,{page});
yield put({
type: 'save',payload: {
data,
total: parseInt(headers['x-total-count'],10),
page: parseInt(page,10)
}
});
},
*del({payload:id},{call,put,select}) {
yield call(usersService.del,id);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
}
},
subscriptions: {
setup({dispatch,history}) {
return history.listen(({pathname,query}) => {
if (pathname=='/users') {
dispatch({type:'fetch',payload:query});
}
});
}
}
}
9.3 src/pages/users/services/users.js [#](#t249.3 src/pages/users/services/users.js)
import request from '../../../utils/request';
import {PAGE_SIZE} from '../contants';
export function fetch(page=1) {
return request(`/api/users?_page=${page}&_limit=${PAGE_SIZE}`);
}
export function del(id) {
return request(`/api/users/${id}`,{
method:'DELETE'
});
}
10. 用户编辑 [#](#t2510. 用户编辑)
10.1 src/pages/users/components/Users.js [#](#t2610.1 src/pages/users/components/Users.js)
src/pages/users/components/Users.js
import React,{Fragment} from 'React';
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm,Button} from 'antd';
import {PAGE_SIZE} from '../contants';
import {routerRedux} from 'dva/router';
import UserModal from './UserModal';
function Users({list: dataSource,loading,total,page: current,dispatch}) {
function del(id) {
dispatch({type:'users/del',payload:id});
}
function handlePageChange(page) {
dispatch(routerRedux.push({
pathname: '/users',
query:{page}
}));
}
function handleUpdate(id,values) {
values.id=id;
dispatch({
type: 'users/update',
payload:{id,values}
});
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,record) => (
<Fragment>
<UserModal
onOk={handleUpdate.bind(this,record.id)}
record={record}>
<Button type="warning" style={{marginRight:10}}>编辑</Button>
</UserModal>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(record.id)}>
<Button type="danger">删除</Button>
</Popconfirm>
</Fragment>
)
}]
return (
<Card>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
loading={loading}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
onChange={handlePageChange}
/>
</Card>
)
}
export default connect(
state => ({...state.users,loading:state.loading.models.users})
)(Users);
10.2 src/pages/users/models/users.js [#](#t2710.2 src/pages/users/models/users.js)
src/pages/users/models/users.js
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
page:1
},
reducers: {
save(state,{payload:{data:list,total,page}}) {
return {...state,list,total,page};
}
},
effects: {
*fetch({payload: {page=1}},{call,put}) {
const {data,headers}=yield call(usersService.fetch,{page});
yield put({
type: 'save',payload: {
data,
total: parseInt(headers['x-total-count'],10),
page: parseInt(page,10)
}
});
},
*del({payload:id},{call,put,select}) {
yield call(usersService.del,id);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
},
*update({payload: {id,values}},{call,put,select}) {
yield call(usersService.update,id,values);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
}
},
subscriptions: {
setup({dispatch,history}) {
return history.listen(({pathname,query}) => {
if (pathname=='/users') {
dispatch({type:'fetch',payload:query});
}
});
}
}
}
10.3 src/pages/users/services/users.js [#](#t2810.3 src/pages/users/services/users.js)
src/pages/users/services/users.js
import request from '../../../utils/request';
import {PAGE_SIZE} from '../contants';
export function fetch(page=1) {
return request(`/api/users?_page=${page}&_limit=${PAGE_SIZE}`);
}
export function del(id) {
return request(`/api/users/${id}`,{
method:'DELETE'
});
}
export function update(id,values) {
return request(`/api/users/${id}`,{
method: 'PUT',
headers: {
"Content-Type":"application/json"
},
body:JSON.stringify(values)
});
}
10.4 src/pages/users/components/UserModal.js [#](#t2910.4 src/pages/users/components/UserModal.js)
src/pages/users/components/UserModal.js
import {Component,Fragment} from 'react';
import {Modal,Form,Input,Button} from 'antd';
class UserEditModal extends Component{
constructor() {
super();
this.state={
visible:false
}
}
handleOk=() => {
const {onOk}=this.props;
this.props.form.validateFields((err,values) => {
if (!err) {
onOk(values);
this.setState({visible:false});
}
});
}
render() {
const {children}=this.props;
const {getFieldDecorator}=this.props.form;
const {username,email,phone}=this.props.record;
const formItemLayout={
labelCol: {span: 6},
wrapperCol:{span:14}
}
return (
<Fragment>
<span onClick={()=>this.setState({visible:true})}>{children}</span>
<Modal
title="编辑用户"
visible={this.state.visible}
onOk={this.handleOk}
onCancel={()=>this.setState({visible:false})}
>
<Form horizontal onSubmit={this.okHandler}>
<Form.Item {...formItemLayout} label="用户名">
{
getFieldDecorator('username', {
initialValue: username,
})(<Input />)
}
</Form.Item>
<Form.Item {...formItemLayout} label="邮箱"
>
{
getFieldDecorator('email', {
initialValue: email,
})(<Input />)
}
</Form.Item>
<Form.Item {...formItemLayout} label="电话">
{
getFieldDecorator('phone', {
initialValue: phone,
})(<Input />)
}
</Form.Item>
</Form>
</Modal>
</Fragment>
)
}
}
export default Form.create()(UserEditModal);
11. 添加用户 [#](#t3011. 添加用户)
11.1 src/pages/users/components/Users.js [#](#t3111.1 src/pages/users/components/Users.js)
src/pages/users/components/Users.js
import React,{Fragment} from 'React';
import {connect} from 'dva';
import {Table,Card,Pagination,Popconfirm,Button} from 'antd';
import {PAGE_SIZE} from '../contants';
import {routerRedux} from 'dva/router';
import UserModal from './UserModal';
function Users({list: dataSource,loading,total,page: current,dispatch}) {
function del(id) {
dispatch({type:'users/del',payload:id});
}
function handlePageChange(page) {
dispatch(routerRedux.push({
pathname: '/users',
query:{page}
}));
}
function handleUpdate(id,values) {
values.id=id;
dispatch({
type: 'users/update',
payload:{id,values}
});
}
function handleCreate(values) {
dispatch({
type: 'users/create',
payload:values
});
}
const columns=[
{
title: '用户名',
dataIndex: 'username',
key: 'username'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email'
}
,
{
title: '电话',
dataIndex: 'phone',
key: 'phone'
},
{
title: ' 操作',
key: 'operation',
render: (text,record) => (
<Fragment>
<UserModal
onOk={handleUpdate.bind(this,record.id)}
record={record}>
<Button type="warning" style={{marginRight:10}}>编辑</Button>
</UserModal>
<Popconfirm title="请问你确定要删除吗?" onConfirm={()=>del(record.id)}>
<Button type="danger">删除</Button>
</Popconfirm>
</Fragment>
)
}]
return (
<Card>
<UserModal
onOk={handleCreate}
record={{}}>
<Button type="warning" style={{marginRight:10}}>添加</Button>
</UserModal>
<Table
columns={columns}
dataSource={dataSource}
rowKey={record => record.id}
pagination={false}
loading={loading}
/>
<Pagination
className="ant-table-pagination"
total={total}
current={current}
pageSize={PAGE_SIZE}
onChange={handlePageChange}
/>
</Card>
)
}
export default connect(
state => ({...state.users,loading:state.loading.models.users})
)(Users);
11.2 src/pages/users/models/users.js [#](#t3211.2 src/pages/users/models/users.js)
src/pages/users/models/users.js
import * as usersService from '../services/users';
export default {
namespace: 'users',
state: {
list: [],
total: null,
page:1
},
reducers: {
save(state,{payload:{data:list,total,page}}) {
return {...state,list,total,page};
}
},
effects: {
*fetch({payload: {page=1}},{call,put}) {
const {data,headers}=yield call(usersService.fetch,{page});
yield put({
type: 'save',payload: {
data,
total: parseInt(headers['x-total-count'],10),
page: parseInt(page,10)
}
});
},
*del({payload:id},{call,put,select}) {
yield call(usersService.del,id);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
},
*update({payload: {id,values}},{call,put,select}) {
yield call(usersService.update,id,values);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
},
*create({payload: values},{call,put,select}) {
yield call(usersService.create,values);
const page=yield select(state => state.users.page);
yield put({type:'fetch',payload:{page}});
}
},
subscriptions: {
setup({dispatch,history}) {
return history.listen(({pathname,query}) => {
if (pathname=='/users') {
dispatch({type:'fetch',payload:query});
}
});
}
}
}
11.3 src/pages/users/services/users.js [#](#t3311.3 src/pages/users/services/users.js)
src/pages/users/services/users.js
export function create(values) {
return request(`/api/users`,{
method: 'POST',
headers: {
"Content-Type":"application/json"
},
body:JSON.stringify(values)
});
}
Gitalking ...
Be the first guy leaving a comment!