Ant Design Pro中Transfer穿梭框的实际用法(与后端交互)
该控件的属性以及属性的作用在ADP的官方文档中都有介绍,但没有讲如何与后端交互,本文旨在讲解该控件与后端的交互。 Ant Design官方文档
先来看需求是怎样的,以及实现的效果

需求:管理某个场馆中的活动,可以对这个场馆进行加入活动和移出活动。 如图,初始进入到这个页面后,韵苑体育馆中有两个活动:活动1、活动5,其余活动在右边的框中(不属于该场馆)。选中右边框中的活动,可以进行移入操作。选中左边的活动,可以进行移出活动。
第一步:从后端取数据
(1)首先,我在该model(namesapce为themeList)的state中定义了两个数据
1 activitys: { 2 list: [], 3 }, 4 emptyActivitys: { 5 list: [], 6 }
activitys是上图中属于左边框的活动,即场馆中的活动。emptyActivitys是上图中右边框的活动,即不属于该场馆中的活动。这两个集合没有交集。
(2)接下来就是该页面在加载的时候从后端取数据并放到这两个属性中,这里需要熟悉Ant Design Pro框架的用法以及React交互原理,就不再这里进行赘述了。
第二步:引进该控件
方便起见,我在使用这个控件时没有做代码规范,直接在该页面的代码上添加了该控件代码。
1@connect(({ themeList, loading }) => ({ 2 themeList, 3 submitting: loading.effects['themeList/'], 4})) 5class App extends React.Component { 6 handleChange = targetKeys => { 7 const { 8 themeList: { emptyActivitys }, 9 } = this.props; //从model中取出emptyActivitys 10 const { dispatch, themeList } = this.props; 11 12 var addList = []; // 需要添加到当前场馆的活动 13 var removeList = []; // 需要移出当前场馆的活动 14 15 //每次点击按钮,都会判断是否有移出的活动或者移入的活动 16 17 // 移出 18 for (var i = 0; i < targetKeys.length; i++) { 19 var key = targetKeys[i]; 20 var has = false; 21 for (var j = 0; j < emptyActivitys.list.length; j++) { 22 if (emptyActivitys.list[j].key == key) { 23 has = true; 24 break; 25 } 26 } 27 if (has) continue; 28 removeList.push(key); 29 } 30 //targetKeys是右框中活动的Key,不是选中的Key,是在点击“移入”后(即用户已经看到了活动移过去)才调用handleChange 31 32 // 移入 33 for (var i = 0; i < emptyActivitys.list.length; i++) { 34 var key = emptyActivitys.list[i].key; 35 var has = false; 36 for (var j = 0; j < targetKeys.length; j++) { 37 if (targetKeys[j] == key) { 38 has = true; 39 break; 40 } 41 } 42 if (has) continue; 43 addList.push(key); 44 } 45 46 //再利用两个for循环,实现接口调用 47 for (var i = 0; i < addList.length; i++) { 48 dispatch({ 49 type: 'themeList/api_addActivityToTheme', 50 payload: { 51 themeId: themeList.detailId, 52 activityId: addList[i], 53 }, 54 }); 55 } 56 57 for (var i = 0; i < removeList.length; i++) { 58 dispatch({ 59 type: 'themeList/api_deleteActivityFromTheme', 60 payload: { 61 themeId: themeList.detailId, 62 activityId: removeList[i], 63 }, 64 }); 65 } 66 }; 67 68 render() { 69 const { 70 themeList, 71 themeList: { activitys, emptyActivitys }, 72 } = this.props; 73 var list = []; 74 var keys = []; 75 76 for (var i = 0; i < activitys.list.length; i++) { 77 // 把场馆中的活动的id赋值给key属性 78 activitys.list[i].key = activitys.list[i].activityId; 79 // 把场馆中的活动加入到集合list中 80 list.push(activitys.list[i]); 81 } 82 83 for (var i = 0; i < emptyActivitys.list.length; i++) { 84 emptyActivitys.list[i].key = emptyActivitys.list[i].activityId; 85 list.push(emptyActivitys.list[i]); 86 keys.push(emptyActivitys.list[i].key); 87 } //keys保留着不属于当前场馆的活动ID 88 89 return ( 90 <Transfer 91 dataSource={list} 92 showSearch 93 listStyle={{ 94 width: 250, 95 height: 300, 96 }} 97 operations={['移出', '移入']} 98 targetKeys={keys} //targetKeys是页面右边的框:即不属于当前场馆的活动 99 onChange={this.handleChange} 100 titles={[themeList.detailName, '所有活动']} 101 render={item => `${item.title}-${item.label}`} 102 /> 103 ); 104 } 105}
1、@connect不用介绍,用于将该控件与指定的medel绑定。 2、该控件中只含有一个方法:handleChange 3、在transer控件的属性中,targetKeys指的是右边框中的集合的Keys,所以我首先自定义了一个空的keys,然后把empyActivitys的key放到keys中。dataSource指的是所有的集合,即左边和右边的合集。所以在render中会有两个for循环,一个是将所有活动放在list中,另一个是将非场馆活动放在keys中。 4、选中活动,点击按钮,会触发handleChange函数(点击按钮才触发,不是选中活动)。在该函数中,我定义了addList和removeList,用于指定哪些活动被添加或者被移除。 5、用两个for循环,对addList和removeList进行填充。 6、再用两个for循环,对addList和removeList中的每个元素进行相应接口的调用。
第三步:修改当前的activitys和emptyActivitys
在完成上述的加入和移出活动后,此时的activitys和emptyActivitys应该被改变,但在实际的model中他们是不会变的,所以只能自己手动的进行更改。
先看看model中的effects的方法:
1 *api_deleteActivityFromTheme({ payload }, { call, put }) { 2 var activityId = payload.activityId; 3 const response = yield call(api_deleteActivityFromTheme, payload); 4 if (isResponseSuccess(response)) { 5 // success 6 yield put({ 7 type: 'save_deleteActivityFromStadium', 8 payload: activityId, 9 }); 10 } else { 11 } 12 }, 13 *api_addActivityToTheme({ payload }, { call, put }) { 14 var activityId = payload.activityId; 15 const response = yield call(api_addActivityToTheme, payload); 16 if (isResponseSuccess(response)) { 17 // success 18 yield put({ 19 type: 'save_api_addActivityToStadium', 20 payload: activityId, 21 }); 22 } else { 23 } 24 },
上述代码正是那最后两个for循环调用的接口
然后该改变是在model中的reducer中进行改变的:
1 save_api_addActivityToStadium(state, action) { 2 var l = null; 3 var list = []; 4 for (var i = 0; i < state.emptyActivitys.list.length; i++) { 5 if (state.emptyActivitys.list[i].activityId == action.payload) { 6 l = state.emptyActivitys.list[i]; 7 } else { 8 list.push(state.emptyActivitys.list[i]); 9 } 10 } 11 state.emptyActivitys.list = list; 12 state.activitys.list.push(l); 13 return { 14 ...state, 15 }; 16 }, 17 save_deleteActivityFromStadium(state, action) { 18 var l = null; 19 var list = []; 20 for (var i = 0; i < state.activitys.list.length; i++) { 21 if (state.activitys.list[i].activityId == action.payload) { 22 l = state.activitys.list[i]; 23 } else { 24 list.push(state.activitys.list[i]); 25 } 26 } 27 state.activitys.list = list; 28 state.emptyActivitys.list.push(l); 29 return { 30 ...state, 31 }; 32 },
简单来说,就是在加入和移出后,我要更新activitys和emptyActivitys这两个数据,否则会造成逻辑的错误。