資料量龐大的分頁穿梭框實現
阿新 • • 發佈:2018-12-16
部落格地址:https://ainyi.com/#/63
昨天偶然看到評論區一位老哥的需求,一時興起,就答應了當天寫好原始碼寫個部落格
回來的晚,第二天才寫好。。
寫個分頁的穿梭框,從而解決資料量龐大的問題
我之前寫過一篇部落格:關於 Element 元件的穿梭框的重構 介紹並實現的方法
但是第二個分頁的 demo 沒有,在上一家公司匆匆解決後,沒有寫入自己的 GitHub,有點可惜...
當時可是在上班,而且太忙了,不過既然答應了這位老哥寫個 demo,就要做到,也是給自己一個挑戰
進入正題
看實現效果圖
既然之前部落格談過,這裡就不仔細談了,主要放主要的原始碼
問題
Element 官方元件目前(==18年==)明顯對於多選==三級聯動的穿梭框==沒有解決方案,也對==資料量龐大的穿梭框==沒有結局方案(各位看官可以試一下,放入幾千條資料到穿梭框,卡到爆...),遂只能自己重寫元件,完成業務需求
功能
- 實現分頁
- 搜尋,做成在所有資料裡搜尋,不是在當前分頁的資料裡搜尋,這樣就不用在每個分頁都搜尋一次了。搜尋後的結果也會自動分頁。(全部資料和僅作展示的資料存都是存放在不同變數)
- 全選只在當前頁裡的全選
- 穿梭框左右兩個框的聯動
關鍵點
- 每個框作為一個子元件(元件化思想)
- 分頁關鍵判斷臨界點
- 搜尋,監聽 keyword 的變化,傳遞到父元件搜尋,從全域性資料搜尋
- 把備選的資料當做已選的過濾陣列,把已選的資料當做備選的過濾陣列,在全域性 data 進行過濾,最後再進行一次搜尋(備選、已選)(考慮到是在搜尋過後點選的)
- 中間的左右箭頭(加入已選和移除已選)放在父元件控制資料流動
- 資料流動:子備選框 -> 父元件 -> 子已選框 (移除已選相反)
原始碼
- 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>
- 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