0 前言
在前端開發過程中時常會遇到表格相關的顯示與處理。組件庫通常都會提供表格組件,對於展示、簡單操作這些常用功能通常也夠用;但如果需要更多的定制或進行比較復雜的操作,組件庫自帶的組件可能會捉襟見肘。vxe-table是一個基於Vue.js的功能較為完善的表格組件庫,常用功能易於上手,也有很多高級功能。
最近做項目時使用了vxe-table庫,期間遇到一個需求(感覺也算是比較常見的場景)
從多頁表格中選擇一些項,把選中的項返回給后端
然而在實現的時候發現一個問題:由於每次翻頁時是重新從后端請求並更新表格展示的數據,所以一旦翻頁,這一頁已經選中的復選框就丟失了。
1 解決思路
最直接的辦法當然是不翻頁,或者在每次翻頁前必須先提交;但顯然只有在特定的業務場景中才能這樣使用。對於通用情況,不難想到如下思路:
- 翻頁之前將復選框狀態保存下來
- 翻頁之后還原新頁面的復選框狀態
- 提交時將所有選中的項目提交
最終的實現方案是使用一個 Set
保存所有選中的項目id,下面詳細介紹代碼實現。(之前還想了一個有點奇怪的方案,沒有采用,寫在最后了。)
2 代碼實現
vxe-table 配置
<vxe-table
ref="paperTable"
row-id="id"
@checkbox-change="togglePaperSelect"
@checkbox-all="toggleAllPaperSelect"
>
<vxe-table-column type="checkbox" width="60"></vxe-table-column>
<vxe-table-column field="title" title="標題"></vxe-table-column>
<!-- other columns -->
</vxe-table>
<vxe-pager
@page-change="handlePageChange"
>
</vxe-pager>
<Button @click="submitAddPaper">確認</Button>
以上是要用到的一些主要配置(有部分省略)
-
Vue 提供的
ref
屬性便於在script中訪問表格,省去 getElement 等操作 -
row-id
給每行設置一個自定義的id,對應行數據中的一個屬性,並且需要保證唯一。最好設置這個選項,之后保存和設置的時候更方便。如果不設置他會自動生成一個id,不過自動id每次會改變,無法作為選擇和保存的依據。 -
checkbox-change
checkbox-all
兩個事件綁定,顧名思義就是點擊每一行中的復選框/表頭的全選復選框時觸發的事件
保存復選框狀態
由於要進行高頻的添加、刪除、查詢操作,所以比起順序存儲的 Array
,使用哈希表的 Set
明顯更加適合。Set 的三個基本操作:
let s = new Set();
s.add('a'); // 添加
s.has('a'); // 查詢
s.delete('a'); // 刪除
只需要在每次復選框狀態發生變動時將對應的項目加入/移出 Set 即可。注意表頭的全選框和每行中的復選框對應兩個不同事件,不要忘記單獨設置。
事件返回的 payload 中可以說是應有盡有,這里只取了兩個需要用到的:行 id 和復選框的新狀態(true/false)
-
自定義設置了
row-id
配置后rowid
屬性才有意義,因為自動生成的id會改變。如果實在沒有適合當id的屬性,或者需要取整個對象,可以使用 payload 里的row
字段 -
【21.03.11 勘誤】全選框這里有個坑。雖然全選中和全取消選中都是
checkbox-all
事件,但返回的 payload.records 不同:-
選中時返回的 records 是一個
Array<row>
,每個 row 對象就是表格中的一行數據 -
取消選中時 records 為空列表,從事件中獲取不到都取消了哪些項目
-
// 在data中定義:selectedPapers: new Set()
togglePaperSelect({ rowid, checked }) {
if (checked) {
this.selectedPapers.add(rowid);
} else {
this.selectedPapers.delete(rowid);
}
},
toggleAllPaperSelect({records, checked}) {
if (checked) {
records.forEach(item => this.selectedPapers.add(item.id))
} else {
// 注意取消全選時需要遍歷當前表格數據來刪除,records不管用
this.paperResult.forEach(item => this.selectedPapers.delete(item.id));
}
},
設置新的復選框狀態
雖然 vxe-table 提供了 checkRowKeys
配置設置默認選中的行,但是只在表格第一次渲染的時候生效,因此只能手動設置選中的復選框。
不知道為什么 vxe-table 沒有提供用 id 設置復選框的 api,只能傳入整個 row 作為參數。所以只能先通過 id 獲取到 row,再設置這一行的復選框狀態為 true。
- 翻頁時先正常請求到新頁中的數據,加載完新數據之后遍歷檢查,如果是之前保存過的項,就設置復選框為選中
setCheckboxRow()
getRowById()
都是 table 提供的 api,所以需要先拿到表格的引用
handlePageChange({ currentPage, pageSize }) {
// request new page
this.queryPaper().then(() => {
let table = this.$refs.paperTable;
this.paperResult.forEach((paper) => {
if (this.selectedPapers.has(paper.id)) {
table.setCheckboxRow(table.getRowById(paper.id), true);
}
});
});
},
完成上述設置后就可以實現翻頁時保存復選框狀態的需求了。
獲取選中項
因為監聽了所有復選框改變的事件,所以 Set 中的內容就是最終所有被選中的內容,因此只需要在提交的時候將 Set 轉換為 Array 即可。
let ids = Array.from(this.selectedPapers);
到此為止就實現了需求的所有功能。
3 一個不太合理的嘗試
其實最初的思路是十分簡單粗暴地保存一個列表的列表,即把每一頁中選取的 id 分別保存,提交時再合並。雖然最終沒有采用,但在寫這一版代碼的過程中也學習了一些新方法,將代碼貼上來記錄一下。同時希望分析的過程也能提供一些參考。
這樣的思路存在一些優點,比如:
- 只有在翻頁時才保存更改,翻頁前反復修改復選框的操作不會帶來消耗,也不需要單獨考慮全選框的問題
- 每頁設置復選框的時候不需要一個一個查詢,直接主動設置已經選中的,如果一頁內容很多同時選中的內容很少可能存在一點點性能優勢
但最終沒有使用它是因為它有更多問題:
- 邏輯問題:如果一頁的大小發生改變,或查詢過程中表格中的數據增加了導致頁的位置發生改變,會直接失效
- 代碼質量問題:二維數組和多個狀態變量都增加了代碼的理解難度,也明顯沒有上面的方法簡潔優雅
- 由於哈希表的CRD效率都很高,對於實際中點擊復選框的操作頻率來說性能開銷的差異很小
// 每次翻頁時保存當前頁中選中的項目,之后加載新頁中選中的項目
handlePageChange({ currentPage, pageSize }) {
// save selected ids in current page
let table = this.$refs.paperTable;
let ids = table.getCheckboxRecords().map((item) => item.id);
this.selectedPapers[this.paperPageParam.pageNum] = ids;
// request new page
this.queryPaper().then(() => {
// load saved ids in new page
let currents = this.selectedPapers[currentPage];
if (currents) {
currents.forEach((rowid) => {
table.setCheckboxRow(table.getRowById(rowid), true);
});
}
this.selectedPapers[currentPage] = [];
});
},
// 提交時合並為一個列表
getAllSelectedPapers() {
let table = this.$refs.paperTable;
let ids = table.getCheckboxRecords().map((item) => item.id);
this.selectedPapers[this.paperPageParam.pageNum] = ids;
let definedLists = this.selectedPapers.filter((item) => item);
return [].concat(...definedLists);
},
結語&參考資料
以上就是我個人在項目過程中遇到的問題以及解決辦法,由於個人能力所限,用到的方法可能不是最好的,也可能存在各種問題,歡迎大家討論與指正。
參考資料:vxe-table 官方文檔