前言:上一篇梳理了上手Ant Design Pro须要了解的一些基础知识,这一篇记录一些在开发【后台管理系统】登陆注册、数据获取、列表展现等功能时须要注意的地方。javascript
1、与服务器交互的通常步骤html
proxy: {
'/login': {
target: 'http://192.168.1.106:9099',
changeOrigin: true,
pathRewrite: { '^/login': '' },
},
}
import request from '@/utils/request'; //ant design pro封装的reques请求文件
export async function fakeAccountLogin(params) {
return request('/login', {
method: 'POST',
body: params
});
}
在modal里面写effect调取接口方法java
import { fakeAccountLogin, getFakeCaptcha } from '@/services/api'; //请求的接口方法
namespace: 'login', //要惟一
state: {
list: [] //后台返回的数据存储在该list中,名字想怎么起怎么起
},
effects: {
*login({ payload }, { call, put }) { //login是界面要调取的接口名称
const response = yield call(fakeAccountLogin, payload); //yield call()真正调用接口,传递数据,返回响应的方法
yield put({ //一个yield put只能触发一个action
type: 'queryList', //经过调用这个reducer把返回数据传给list
payload: response,
});
reducers: {
queryList(state, action) {
return {
...state,
list: action.payload, //这就拿到数据啦
};
},
}
}
}
组件中建立链接react
在 pages/User/Login.js 组件中经过 dva提供的connect高阶组件链接组件和dva,传入namespace(惟一)得到其中的state和effects(dispatch方法)json
import { connect } from 'dva'
@connect(({ login, loading }) => ({ //login是namespace loading是对应使用的方法
login, //login是namespace
submitting: loading.effects['login/login'], //login 命名空间的login请求接口(入口)
}))
通常在componentDidMount生命钩子中发送请求获取数据redux
componentDidMount() {
//注意务必先使用dva中的connect创建链接,不然是没法调用props中的dispatch法的
this.props.dispatch({
//调用model中的方法发起请求,(model的命名空间+方法名)
type: 'mbit/firstRequest',
//设置参数
payload:{
args1:"参数1",
args2:"参数2",
},
});
}
登陆提交等操做方法中发送请求获取数据后端
handleSubmit = (err, values) => {
const { type } = this.state;
if (!err) {
const { dispatch } = this.props;
dispatch({
type: 'login/login',
payload: {
login_type: "usernameAndPassword",
credentials: {
username: values.userName,
password: values.password
},
...values
},
});
}
};
获取数据后的其它操做api
显示后台返回的数据跨域
const {Login: { list },loading} = this.props; //这个就在对应namespace下面list数组,以前存放后台返回数据的list数组
<Table columns={columns} dataSource={list?list.content:[]} rowKey="id"/> //dataSource里面是经过list获取到的数据
跳转路由页面数组
import { routerRedux } from 'dva/router';
yield put(routerRedux.replace('/'));
将获取到的数据存入localStorage
localStorage.setItem('login_token', response.data.token);
每次请求中带着token 获取localStorage中的token封装进请求头中(修改 request.js 请求文件)
let login_token;
newOptions.headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
...newOptions.headers,
};
if (localStorage.getItem('login_token') != null){
login_token = localStorage.getItem('login_token');
newOptions.headers['AuthorizationToken'] = localStorage.getItem('login_token');
}
二、关于@connect装饰器
dva
所封装的 react-redux
的 @connect
装饰器list
这个 model 对应的 redux store。app.state.list
之外还实际接收 app.state.loading
做为参数,这个 loading
的来源是 src/index.js
中调用的 dva-loading
这个插件。
/*
* src/index.js
*/
import createLoading from 'dva-loading';
app.use(createLoading());
它返回的信息包含了 global、model 和 effect 的异步加载完成状况。数据map一
{
"global": true,
"models": {
"list": false,
"user": true,
"rule": false
},
"effects": {
"list/fetch": false,
"user/fetchCurrent": true,
"rule/fetch": false
}
}
在这里带上 {count: 5}
这个 payload (参数)向 store 进行了一个类型为 list/fetch
的 dispatch,在 src/models/list.js
中就能够找到具体的对应操做。
import { queryFakeList } from '../services/api';
export default {
namespace: 'list',
state: {
list: [],
},
effects: {
*fetch({ payload }, { call, put }) {
const response = yield call(queryFakeList, payload);
yield put({
type: 'queryList',
payload: Array.isArray(response) ? response : [],
});
},
/* ... */
},
reducers: {
queryList(state, action) {
return {
...state,
list: action.payload,
};
},
/* ... */
},
};
View中使用@connect
@connect(({ list, loading }) => ({
list,//①
loading: loading.models.list,//②
}))
connect 有两个参数,mapStateToProps以及mapDispatchToProps,一个将state状态绑定到组件的props,一个将dispatch方法绑定到组件的props
代码①:将实体list中的state数据绑定到props,注意绑定的是实体list总体,使用时须要list.[state中的具体变量]
代码②:经过loading将上文“数据map一”中的models的list的key对应的value读取出来。赋值给loading,以方便使用,如表格是否有加载图标(也能够经过key value编写:loading.effects["list/fetch"],以下↓可取多个)
//pages/Dashboard/WorkPlace.js
@connect(({ user, project, activities, chart, loading }) => ({
currentUser: user.currentUser,
project,
activities,
chart,
currentUserLoading: loading.effects['user/fetchCurrent'],
projectLoading: loading.effects['project/fetchNotice'],
activitiesLoading: loading.effects['activities/fetchList'],
}))
变量获取
因,在src/models/list.js
export default {
namespace: 'list',
state: {
list: [],
},
故,在view中
render() {
const { list: { list }, loading } = this.props;
定义使用时:list: { list } ,含义:实体list下的state类型的list变量
3、项目实践注意点
{
title: '资料审核',
dataIndex: 'detailInfo',
render: (text, record) => <a onClick={() => this.previewItem(record.id)}>资料详情>></a>
},
//components/StandardTable/index.js
handleRowSelectChange = (selectedRowKeys, selectedRows) => {
let { noPayList, payedList, needTotalList } = this.state;
noPayList = initNoPayList(selectedRows);
payedList = initPayedList(selectedRows);
needTotalList = needTotalList.map(item => ({
...item,
total: selectedRows.reduce((sum, val) => sum + parseFloat(val[item.dataIndex], 10), 0),
}));
const { onSelectRow } = this.props;
if (onSelectRow) {
onSelectRow(selectedRows);
}
this.setState({ selectedRowKeys, selectedRows, noPayList, payedList, needTotalList });
};
const okHandle = (record) => {
form.validateFields((err, fieldsValue) => {
if (err) return;
form.resetFields();
handleRemark(record, fieldsValue);
});
};
const CreateForm = Form.create()(props => {
const { modalVisible, record, form, handleRemark, handleModalVisible } = props;
const rowObject = {
minRows: 2,
maxRows: 6
}
const okHandle = (record) => {
form.validateFields((err, fieldsValue) => {
if (err) return;
form.resetFields();
handleRemark(record, fieldsValue);
});
};
return (
<Modal
destroyOnClose
title="添加备注"
visible={modalVisible}
okText="肯定"
cancelText="取消"
onOk={() => okHandle(record)}
onCancel={() => handleModalVisible()}
>
<FormItem labelCol={{ span: 5 }} wrapperCol={{ span: 15 }}>
{form.getFieldDecorator('remark', {
rules: [{ required: true, message: '请输入至少五个字符的审批备注!', min: 5 }],
})(
<TextArea
autosize={rowObject}
/>
)}
</FormItem>
</Modal>
);
});
trimStr = str => {
return str.replace(/(^\s*)|(\s*$)/g,"");
}
//查询
handleSearch = e => {
e.preventDefault();
const { dispatch, form } = this.props;
form.validateFields((err, fieldsValue) => {
if (err) return;
let values = {
...fieldsValue,
updatedAt: fieldsValue.updatedAt && fieldsValue.updatedAt.valueOf(),
};
Object.keys(values).forEach(key => {
if(values[key]){
values[key] = this.trimStr(values[key])
}
});
this.setState({
formValues: values,
});
dispatch({
type: 'agent/fetch',
payload: {
currentPage: 1,
pd: {},
showCount: 10
},
});
});
};
<Row gutter={24}>
{ infoPics.map((infoPic, index) =>
(<Col key={index} xl={6} lg={12} md={24} sm={24} xs={24}>
<div className={styles.infoTitle}>{`代理资料${index+1}`}</div>
<div
className={styles.infoPic}
style={{ backgroundImage: `url(${infoPic})` }}
>
{ infoPic == '' ? <div className={styles.empty}><Empty /></div> : '' }
</div>
</Col>)
)
}
</Row>
资源文件须要加统一前缀时,在配置文件中定义方法,应用时直接在数据前调用方法便可
//util/util.js
export function setFileHost(){
return 'http://baidu.com/';
}
//RightContent.js
import { setFileHost } from '@/utils/utils'
<Avatar
size="small"
className={styles.avatar}
src={`${setFileHost()+currentUser.headIcon}`}
alt="avatar"
/>
参考资料
注:转载请注明出处