Ant Design Pro中Transfer穿梭框的實際用法(與后端交互)


Ant Design Pro中Transfer穿梭框的實際用法(與后端交互)


該控件的屬性以及屬性的作用在ADP的官方文檔中都有介紹,但沒有講如何與后端交互,本文旨在講解該控件與后端的交互。
Ant Design官方文檔


先來看需求是怎樣的,以及實現的效果

此處輸入圖片的描述

需求:管理某個場館中的活動,可以對這個場館進行加入活動和移出活動。
如圖,初始進入到這個頁面后,韻苑體育館中有兩個活動:活動1、活動5,其余活動在右邊的框中(不屬於該場館)。選中右邊框中的活動,可以進行移入操作。選中左邊的活動,可以進行移出活動。

第一步:從后端取數據

(1)首先,我在該model(namesapce為themeList)的state中定義了兩個數據

    activitys: {
      list: [],
    },
    emptyActivitys: {
      list: [],
    }

activitys是上圖中屬於左邊框的活動,即場館中的活動。emptyActivitys是上圖中右邊框的活動,即不屬於該場館中的活動。這兩個集合沒有交集。

(2)接下來就是該頁面在加載的時候從后端取數據並放到這兩個屬性中,這里需要熟悉Ant Design Pro框架的用法以及React交互原理,就不再這里進行贅述了。

第二步:引進該控件

方便起見,我在使用這個控件時沒有做代碼規范,直接在該頁面的代碼上添加了該控件代碼。

@connect(({ themeList, loading }) => ({
  themeList,
  submitting: loading.effects['themeList/'],
}))
class App extends React.Component {
  handleChange = targetKeys => {
    const {
      themeList: { emptyActivitys },
    } = this.props;    //從model中取出emptyActivitys
    const { dispatch, themeList } = this.props;

    var addList = []; // 需要添加到當前場館的活動
    var removeList = []; // 需要移出當前場館的活動

    //每次點擊按鈕,都會判斷是否有移出的活動或者移入的活動
    
    // 移出
    for (var i = 0; i < targetKeys.length; i++) { 
      var key = targetKeys[i];
      var has = false;
      for (var j = 0; j < emptyActivitys.list.length; j++) {
        if (emptyActivitys.list[j].key == key) {
          has = true;
          break;
        }
      }
      if (has) continue;
      removeList.push(key);
    }
    //targetKeys是右框中活動的Key,不是選中的Key,是在點擊“移入”后(即用戶已經看到了活動移過去)才調用handleChange
    
    // 移入
    for (var i = 0; i < emptyActivitys.list.length; i++) {
      var key = emptyActivitys.list[i].key;
      var has = false; 
      for (var j = 0; j < targetKeys.length; j++) {
        if (targetKeys[j] == key) {
          has = true;
          break;
        }
      }
      if (has) continue;
      addList.push(key);
    }

    //再利用兩個for循環,實現接口調用
    for (var i = 0; i < addList.length; i++) {
      dispatch({
        type: 'themeList/api_addActivityToTheme',
        payload: {
          themeId: themeList.detailId,
          activityId: addList[i],
        },
      });
    }

    for (var i = 0; i < removeList.length; i++) {
      dispatch({
        type: 'themeList/api_deleteActivityFromTheme',
        payload: {
          themeId: themeList.detailId,
          activityId: removeList[i],
        },
      });
    }
  };

  render() {
    const {
      themeList,
      themeList: { activitys, emptyActivitys },
    } = this.props;
    var list = [];
    var keys = [];

    for (var i = 0; i < activitys.list.length; i++) {
      // 把場館中的活動的id賦值給key屬性
      activitys.list[i].key = activitys.list[i].activityId;
      // 把場館中的活動加入到集合list中
      list.push(activitys.list[i]);
    }

    for (var i = 0; i < emptyActivitys.list.length; i++) {
      emptyActivitys.list[i].key = emptyActivitys.list[i].activityId;
      list.push(emptyActivitys.list[i]);
      keys.push(emptyActivitys.list[i].key);
    } //keys保留着不屬於當前場館的活動ID

    return (
      <Transfer
        dataSource={list}
        showSearch
        listStyle={{
          width: 250,
          height: 300,
        }}
        operations={['移出', '移入']}
        targetKeys={keys} //targetKeys是頁面右邊的框:即不屬於當前場館的活動
        onChange={this.handleChange}
        titles={[themeList.detailName, '所有活動']}
        render={item => `${item.title}-${item.label}`}
      />
    );
  }
}

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的方法:

      *api_deleteActivityFromTheme({ payload }, { call, put }) {
      var activityId = payload.activityId;
      const response = yield call(api_deleteActivityFromTheme, payload);
      if (isResponseSuccess(response)) {
        // success
        yield put({
          type: 'save_deleteActivityFromStadium',
          payload: activityId,
        });
      } else {
      }
    },
    *api_addActivityToTheme({ payload }, { call, put }) {
      var activityId = payload.activityId;
      const response = yield call(api_addActivityToTheme, payload);
      if (isResponseSuccess(response)) {
        // success
        yield put({
          type: 'save_api_addActivityToStadium',
          payload: activityId,
        });
      } else {
      }
    },

上述代碼正是那最后兩個for循環調用的接口

然后該改變是在model中的reducer中進行改變的:

    save_api_addActivityToStadium(state, action) {
      var l = null;
      var list = [];
      for (var i = 0; i < state.emptyActivitys.list.length; i++) {
        if (state.emptyActivitys.list[i].activityId == action.payload) {
          l = state.emptyActivitys.list[i];
        } else {
          list.push(state.emptyActivitys.list[i]);
        }
      }
      state.emptyActivitys.list = list;
      state.activitys.list.push(l);
      return {
        ...state,
      };
    },
        save_deleteActivityFromStadium(state, action) {
      var l = null;
      var list = [];
      for (var i = 0; i < state.activitys.list.length; i++) {
        if (state.activitys.list[i].activityId == action.payload) {
          l = state.activitys.list[i];
        } else {
          list.push(state.activitys.list[i]);
        }
      }
      state.activitys.list = list;
      state.emptyActivitys.list.push(l);
      return {
        ...state,
      };
    },

簡單來說,就是在加入和移出后,我要更新activitys和emptyActivitys這兩個數據,否則會造成邏輯的錯誤。

注意:該控件的限制在於需要后端提供相應的接口,且算法不簡單,但由於產品經理的硬性要求,不得已這么寫了,有許多不規范的地方還請大佬們指教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM