前言:上一篇梳理了上手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"
/>
參考資料
注:轉載請註明出處