前端 | vxe-table 翻頁保留復選框狀態


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 官方文檔


免責聲明!

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



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