背景
element-ui
中的 el-table
組件只提供了數據展示,而分頁功能作爲一個單獨的組件 el-pagination
,並沒有像 Ant Design
一樣集成到 el-able
組件中,並且每一個數據列也都是一個單獨的組件 el-table-column
。
在開發過程中就出現了一個很明顯的問題:業務中的大部分數據表格功能都類似,數據列通常在3-6個不等,且都需要分頁,前端分頁與後端分頁的邏輯又不相同,這就導致了每次寫一個這樣數據表格功能,都需要至少複製粘貼很多 el-table-column
組件和一套實現分頁功能的代碼。寫代碼最惱火的就是要複製粘貼很多重複的代碼,一不小心就漏了些什麼,有木有……
結合我最早使用的 easyui
中的 datagrid
組件,在 element-ui
的基礎上封裝一個能滿足大部分業務功能的 el-datagrid
組件,能大大的提高開發效率。
EasyUI 的 datagrid
接觸的第一個 UI 框架是 jQuery
版本的 EasyUI
,雖然樣式是醜了一點,但個人覺得它的組件的封裝,對開發人員是很友好的,基本上每個組件都可以用一個 對象或數組 去動態配置,很符合 數據驅動 的思維。
功能
大部分的業務(增刪改查)中,數據表格通常有以下幾個功能:
- 數據展示
- 排序
- 對每一行的操作(查看、修改、刪除)
- 對整個數據表格的操作(新增、批量刪除)
- 分頁(前端 / 後端)
- 刷新數據
數據列配置(功能1、2、3 )
通常會從遠程加載數據,將數據列配置成 數組,數組的每一項匹配遠程數據中對象的屬性,替代寫多個 el-table-column
組件的麻煩。
遠程數據格式:
{
data: [
{
name: 'lixiao 1',
email: 'lixiao [email protected]'
},
……
{
name: 'lixiao 12',
email: 'lixiao [email protected]'
}
],
total: 12
}
數據列配置如下:
columns: [
{
label: '姓名',
prop: 'name',
sortable: true
}, {
label: '郵箱',
prop: 'email',
sortable: true
}, {
label: '操作',
actions: [
{
label: '修改',
prop: 'edit',
type: 'warning',
icon: 'el-icon-edit'
}, {
label: '刪除',
prop: 'delete',
type: 'danger',
icon: 'el-icon-delete'
}
]
}
]
label
表示列對應的表頭,prop
表示列對應的遠程數據中對象的屬性,sortable
表示該列是否支持排序,actions
通常在最後一列,渲染一組操作按鈕,採用 this.$emit('rowClick', { row, prop })
的方式,向父組件 App.vue
傳遞一個 rowClick
事件,參數是一個包含了 該行數據 和 點擊按鈕的 prop
值(例如 edit / delete)的對象,在父組件中監聽 rowClick
事件,判斷 prop
的值即可做出不同的響應。
工具欄配置(功能4)
數據列配置能解決數據 增刪改查 中的刪、改、查,通常會在表格上方提供一組功能性按鈕,例如新增、導出(部分 / 全量)、批量刪除等功能,比較特殊的是批量刪除這種功能需要獲取到已經選中的行的數據。
toolbar: [
{
label: '新增',
prop: 'add',
type: 'primary',
icon: 'el-icon-plus',
}, {
label: '刪除多行',
prop: 'delete',
type: 'danger',
icon: 'el-icon-delete',
usableAfterSelect: true
}
]
同樣使用 this.$emit('toolbarClick', { rows, prop })
的方式向父組件傳遞一個 toolbarClick
事件,不同的是,rows
是一個包含了多行數據的數組(當然也可能是一個空數組),父組件中監聽 toolbarClick
事件,判斷 prop
值和 rows
長度即可做出不同的響應。
對於需要 至少選擇一行 才能進行操作的功能,參考了 Ant Design
例子中的交互,設置 usableAfterSelect
屬性爲 true
之後,選中至少一行之前按鈕不可用。
分頁(功能5)
前端分頁
第一次獲取數據之後,切換頁碼、切換每頁數量只需要利用 slice
截取原始數據數組中對應數量的數據。
後端分頁
每次切換頁碼和切換每頁數量都需要重新獲取數據,將返回的全部數據都作爲要顯示的原始數據。
// ElDatagrid.vue
// this.index 表示顯示的數據的序號,表頭爲 #
<el-table :data="showData" />
computed: {
showData() {
if (this.clientPagination) {
// 若客戶端分頁,則根據頁碼和每頁數量 截取相應的數據
return this.data.slice().splice(this.index - 1, this.pageSize);
} else {
// 若服務端分頁,則直接顯示獲取到的數據
return this.data.slice();
}
}
},
刷新數據(功能6)
刷新數據指僅刷新數據表格的數據,而不是刷新整個頁面。而觸發刷新的操作往往是由父組件發起的,就變成了,如何讓父組件通知子組件重新發請求獲取數據。
實現這個功能我嘗試了好幾種方法:
- 最難看的
bus
中央事件總線。每個子組件實例都與父組件有一個不衝突的事件名做關聯,用起來不方便 - 在子組件中
watch
一個傳遞過來的prop
值的變化,那父組件需要更新數據的時候還是需要改變那個傳遞給子組件的值,嗯,new Date()
肯定是一直在變的……使用仍然不是很方便 - 父組件中通過
$refs
訪問子組件,調用子組件的方法。原來有這麼簡單又好用的方法
<el-datagrid ref="datagrid" />
methods: {
reloadDatagrid() {
this.$refs['datagrid'].reload();
}
}
完整的 ElDatagrid
組件和使用 demo
、API
文檔:https://github.com/lixiao564/...。