1. 程式人生 > >數據量龐大的分頁穿梭框實現

數據量龐大的分頁穿梭框實現

itl exp out 問題 技術 add change tps tle

博客地址:https://ainyi.com/#/63

昨天偶然看到評論區一位老哥的需求,一時興起,就答應了當天寫好源碼寫個博客

回來的晚,第二天才寫好。。

技術分享圖片

寫個分頁的穿梭框,從而解決數據量龐大的問題

我之前寫過一篇博客:關於 Element 組件的穿梭框的重構 介紹並實現的方法

但是第二個分頁的 demo 沒有,在上一家公司匆匆解決後,沒有寫入自己的 GitHub,有點可惜...

當時可是在上班,而且太忙了,不過既然答應了這位老哥寫個 demo,就要做到,也是給自己一個挑戰

進入正題

看實現效果圖

技術分享圖片

既然之前博客談過,這裏就不仔細談了,主要放主要的源碼

問題

Element 官方組件目前(==18年==)明顯對於多選==三級聯動的穿梭框==沒有解決方案,也對==數據量龐大的穿梭框==沒有結局方案(各位看官可以試一下,放入幾千條數據到穿梭框,卡到爆...),遂只能自己重寫組件,完成業務需求

功能

  1. 實現分頁
  2. 搜索,做成在所有數據裏搜索,不是在當前分頁的數據裏搜索,這樣就不用在每個分頁都搜索一次了。搜索後的結果也會自動分頁。(全部數據和僅作展示的數據存都是存放在不同變量)
  3. 全選只在當前頁裏的全選
  4. 穿梭框左右兩個框的聯動

關鍵點

  1. 每個框作為一個子組件(組件化思想)
  2. 分頁關鍵判斷臨界點
  3. 搜索,監聽 keyword 的變化,傳遞到父組件搜索,從全局數據搜索
  4. 把備選的數據當做已選的過濾數組,把已選的數據當做備選的過濾數組,在全局 data 進行過濾,最後再進行一次搜索(備選、已選)(考慮到是在搜索過後點擊的)
  5. 中間的左右箭頭(加入已選和移除已選)放在父組件控制數據流動
  6. 數據流動:子備選框 -> 父組件 -> 子已選框 (移除已選相反)

源碼

  1. Districts.vue(包裹兩個穿梭框的父組件)
export default {
  props: {
    data: {
      type: Array,
    },
  },
  data () {
    return {
      dataList: [], // 未選中(已過濾出已選)的數據
      selectList: [], // 已選中的數據,傳遞到子組件的數據

      dataListNoCheck: [], // 未選中的(或已搜索)傳遞到子組件的數據
      selectListCheck: [], // 已選中的(或已搜索)傳遞到子組件的數據

      checkData: [], // 已勾選的數據(待添加或刪除數據)

      noSelectkeyword: ‘‘,
      haSelectkeyword: ‘‘,

      disablePre: true,
      disableNex: true,
    };
  },
  created () {
    this.getDistrict();
  },
  methods: {
    // 分頁數據
    getDistrict () {
      this.dataList = this.data;
      this.dataListNoCheck = this.dataList;
    },
    searchWord (keyword, titleId) {
      // 過濾掉數據,保留搜索的數據
      if (titleId === 0) {
        this.noSelectkeyword = keyword;
        this.dataListNoCheck = this.dataList.filter(val => val.name.includes(keyword));
      } else {
        this.haSelectkeyword = keyword;
        this.selectListCheck = this.selectList.filter(val => val.name.includes(keyword));
      }
      let refsName = titleId === 0 ? ‘noSelect‘ : ‘hasSelect‘;
      // 延遲執行
      setTimeout(() => {
        this.$refs[refsName].getDistrict();
      }, 0);
    },
    // 檢查左右按鈕可用性
    checkDisable (data, id) {
      if (id === 0) {
        data.length > 0 ? (this.disableNex = false) : (this.disableNex = true);
      } else {
        data.length > 0 ? (this.disablePre = false) : (this.disablePre = true);
      }
    },
    // 選擇
    checkSelect (val) {
      this.checkData = val;
    },
    // 關鍵:把未選擇的數據當做已選擇的過濾數組,把已選擇的數據當做未選擇的過濾數組,在全局data進行過濾,最後進行一次搜索
    // 添加至已選
    addData () {
      let dataFilter = [
        ...this.selectList,
        ...this.checkData,
      ];
      this.dataList = this.data.filter(item1 => {
        return dataFilter.every(item2 => item2 !== item1);
      });
      this.selectList = this.data.filter(item1 => {
        return this.dataList.every(item2 => item2 !== item1);
      });
      // 搜索一次
      this.searchWord(this.noSelectkeyword, 0);
      this.searchWord(this.haSelectkeyword, 1);
    },
    // 從已選中刪除
    deleteData () {
      let dataFilter = [
        ...this.dataList,
        ...this.checkData,
      ];
      this.selectList = this.data.filter(item1 => {
        return dataFilter.every(item2 => item2 !== item1);
      });
      this.dataList = this.data.filter(item1 => {
        return this.selectList.every(item2 => item2 !== item1);
      });
      // 搜索一次
      this.searchWord(this.noSelectkeyword, 0);
      this.searchWord(this.haSelectkeyword, 1);
    },
  },
  components: {
    Transfer,
  },
};
</script>
  1. Transfer.vue(穿梭框子組件)
export default {
  props: {
    titleId: {
      type: Number,
    },
    districtList: { // 父組件傳遞的數據
      type: Array,
    },
  },
  data () {
    return {
      title: [‘渠道‘, ‘已選中‘],
      districtListMock: [], // 展示的數據 (搜索和分頁會自動修改這個數組)
      checkedCities: [], // 已選擇,數據格式:[id,id,id...]
      isIndeterminate: false,
      checkAll: false,
      searchWord: ‘‘,
      len: 0,
      total: 0,
      pageIndex: 0,
      disabledPre: true,
      disabledNex: false,
    };
  },
  created () {
    this.getDistrict();
  },
  watch: {
    // 搜索框的監聽器
    searchWord (newWord) {
      this.$emit(‘search-word‘, newWord, this.titleId);
    },
    // districtListMock 和 checkAll 的監聽器
    districtListMock () {
      // 當方框中無已選擇的數據時,不能勾選checkBox
      if (this.checkedCities.length === 0) {
        this.checkAll = false;
        this.isIndeterminate = false;
      }
    },
    checkedCities (newWord) {
      this.$emit(‘check-disable‘, newWord, this.titleId);
    },
    // 當列表中無數據時,不能勾選checkBox
    checkAll () {
      this.checkAll = this.districtListMock.length === 0 ? false : this.checkAll;
    },
  },
  methods: {
    // 分頁數據
    getDistrict () {
      this.len = this.districtList.length;
      this.total = Math.ceil(this.len / 200);
      this.pageIndex = 0;
      this.pageData();
    },
    pageData () {
      this.checkedCities = [];
      if (this.total > 1 && this.pageIndex < (this.total - 1)) {
        this.pageIndex === 0 ? this.disabledPre = true : this.disabledPre = false;
        this.disabledNex = false;
        this.districtListMock = this.districtList.slice(this.pageIndex \\* 200, this.pageIndex \\* 200 + 200);
      } else {
        this.total > 1 ? this.disabledPre = false : this.disabledPre = true;
        this.disabledNex = true;
        this.districtListMock = this.districtList.slice(this.pageIndex \\* 200, this.len);
      }
    },
    // 上一頁
    prev () {
      this.pageIndex > 0 && --this.pageIndex;
      this.pageData();
    },
    // 下一頁
    next () {
      this.pageIndex <= (this.total - 1) && ++this.pageIndex;
      this.pageData();
    },
    // 單選
    handleCheckedChange (value) {
      let checkedCount = value.length;
      this.checkAll = checkedCount === this.districtListMock.length;
      this.isIndeterminate = checkedCount > 0 && checkedCount < this.districtListMock.length;
      // 子傳父
      this.$emit(‘check-district‘, value);
    },
    // 全選
    handleCheckAllChange (val) {
      this.checkedCities = val ? this.districtListMock.map(val => val) : [];
      this.isIndeterminate = false;
      // 子傳父
      this.$emit(‘check-district‘, this.checkedCities);
    },
  },
};
</script>

具體源碼可前往 Github:https://github.com/Krryxa/my-transfer

歡迎 start

呼呼,雙休好好休息了~~

博客地址:https://ainyi.com/#/63

數據量龐大的分頁穿梭框實現