背景
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 1@163.com'
},
……
{
name: 'lixiao 12',
email: 'lixiao 12@163.com'
}
],
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/…。