日日加班至夜半,环视周围无人走;
明晚八点准时走,谁不打卡谁是狗。
使用过element-ui的表格的同学应该都有这样的体会,做一个简单的表格还比较容易,但如果这个表格包含了顶部的按钮,还有分页,甚至再包含了行编辑,那开发工作量就成倍的增加,特别是在开发管理系统的时候,表格一个接一个的去开发, 即浪费时间,还对个人没有什么提升。今天小编带来了自己封装的一个表格,让你用JSON就可以简单的生成表格。本文主要集中于使用说明与核心代码说明,完整代码请访问 https://github.com/snowzijun/vue-element-table,如果觉得有用,麻烦给小编一个star,你的每一个star都是对小编的支持,当前功能比较简陋,本仓库将持续更新。同时您也可以微信搜索【前端有的玩】公众号,小编拉你进前端技术交流群。
表格需求
一般管理系统对表格会有以下需求可以分页(需要有分页条)
可以多选(表格带复选框)
顶部需要加一些操作按钮(新增,删除等等)
表格每行行尾有操作按钮
表格行可以编辑
如下图为一个示例表格
如果我们直接使用element-ui提供的组件的话,那么开发一个这样的表格就需要使用到以下内容需要使用表格的插槽功能,开发每一行的按钮
需要通过样式调整顶部按钮,表格,分页条的布局样式
需要监听分页的事件然后去刷新表格数据
顶部按钮或操作按钮如果需要获取表格数据,需要调用表格提供的api
对于有行编辑的需求,还需要通过插槽去渲染行编辑的内容,同时要控制行编辑的开关
不仅仅开发表格比较麻烦,而且还要考虑团队协作,如果每个人实现表格的方式存在差别,那么可能后期的维护成本也会变得很高。那怎么办呢?
项目安装
安装插件
在使用element-ui的项目中,可以通过以下命令进行安装npm install vue-elementui-table -S
在项目中使用
在main.js中添加以下代码import ZjTable from 'vue-element-table'
Vue.use(ZjTable)
然后即可像下文中的使用方式进行使用
表格配置
为了满足团队快速开发的需要,小编对上面提出来的需求进行了封装,然后使用的时候,开发人员只需要配置一些JSON便可以完成以上功能的开发。
基础配置
一个基础的表格包含了数据和列信息,那么如何用封装的表格去配置呢?
:columns="columns"
:data="data"
:pagination="false"
/>
export default {
data() {
return {
// 表格的列信息, 数组每一项代表一个字段,可以使用element 列属性的所有属性,以下仅为示例
columns: Object.freeze([
{
// 表头显示的文字
label: '姓名',
// 对应数据里面的字段
prop: 'name'
},
{
label: '性别',
prop: 'sex',
// 格式化表格,与element-ui 的表格属性相同
formatter(row, column, cellValue) {
return cellValue === 1 ? '男' : '女'
}
},
{
label: '年龄',
prop: 'age'
}
]),
data: [
{
name: '子君',
sex: 1,
age: 18
}
]
}
}
}
表格默认会显示复选框,也可以通过配置selectable属性来关闭掉
添加分页
简单的表格用封装之后的或未封装的开发工作量区别并不大,我们继续为表格添加上分页
v-loading="loading"
:columns="columns"
:data="data"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
height="auto"
@page-change="$_handlePageChange"
/>
export default {
data() {
return {
columns: Object.freeze([
// 列字段与上例一样,此处省略
]),
data: [],
// 当前页码
currentPage: 1,
// 每页条数
pageSize: 10,
// 总条数
total: 0,
// 是否显示loading
loading: false
}
},
created() {
this.loadData()
},
methods: {
// 加载表格数据
loadData() {
this.loading = true
setTimeout(() => {
// 假设总条数是40条
this.total = 40
const { currentPage, pageSize } = this
// 模拟数据请求获取数据
this.data = new Array(pageSize).fill({}).map((item, index) => {
return {
name: `子君${currentPage + (index + 1) * 10}`,
sex: Math.random() > 0.5 ? 1 : 0,
age: Math.floor(Math.random() * 100)
}
})
this.loading = false
}, 1000)
},
$_handlePageChange() {
// 因为上面设置属性指定了.sync,所以这两个属性会自动变化
console.log(this.pageSize, this.currentPage)
// 分页发生变化,重新请求数据
this.loadData()
}
}
}
通过封装,表格将自带分页功能,通过上面代码,实现效果如下所示,是不是变得简单了一些。接下来我们继续给表格添加按钮
添加顶部按钮
表格上面可能会有新增,删除等等按钮,怎么办呢,接下来我们继续通过配置去添加按钮
:buttons="buttons"
/>
export default {
data() {
return {
buttons: Object.freeze([
{
// id 必须有而且是在当前按钮数组里面是唯一的
id: 'add',
text: '新增',
type: 'primary',
icon: 'el-icon-circle-plus',
click: this.$_handleAdd
},
{
id: 'delete',
text: '删除',
// rows 是表格选中的行,如果没有选中行,则禁用删除按钮, disabled可以是一个boolean值或者函数
disabled: rows => !rows.length,
click: this.$_handleRemove
},
{
id: 'auth',
text: '这个按钮根据权限显示',
// 可以通过返回 true/false来控制按钮是否显示
before: (/** rows */) => {
return true
}
},
// 可以配置下拉按钮哦
{
id: 'dropdown',
text: '下拉按钮',
children: [
{
id: 'moveUp',
text: '上移',
icon: 'el-icon-arrow-up',
click: () => {
console.log('上移')
}
},
{
id: 'moveDown',
text: '下移',
icon: 'el-icon-arrow-down',
disabled: rows => !rows.length,
click: () => {
console.log('下移')
}
}
]
}
])
}
},
created() {},
methods: {
// 新增
$_handleAdd() {
this.$alert('点击了新增按钮')
},
// 顶部按钮会自动将表格所选的行传出来
$_handleRemove(rows) {
const ids = rows.map(({ id }) => id)
this.$alert(`要删除的行id为${ids.join(',')}`)
},
// 关注作者公众号
$_handleFollowAuthor() {}
}
}
通过上面的代码就可以配置出下面的表格,是不是很简单呢?
表格顶部可以有按钮,行尾也是可以添加按钮的,一起来看看
行操作按钮
一般我们会将一些单行操作的按钮放在行尾,比如编辑,下载等按钮,那如何给行尾配置按钮呢?
:columns="columns"
/>
export default {
data() {
return {
columns: Object.freeze([
{
// 可以指定列的宽度,与element-ui原生用法一致
width: 220,
label: '姓名',
prop: 'name'
},
// 行编辑按钮,在表格末尾出现,自动锁定右侧
{
width: 180,
label: '操作',
// 通过 actions 指定行尾按钮
actions: [
{
id: 'follow',
text: '关注作者',
click: this.$_handleFollowAuthor
},
{
id: 'edit',
text: '编辑',
// 可以通过before控制按钮是否显示,比如下面年龄四十岁的才会显示编辑按钮
before(row) {
return row.age < 40
},
click: this.$_handleEdit
},
{
id: 'delete',
text: '删除',
icon: 'el-icon-delete',
disabled(row) {
return row.sex === 0
},
// 为了拿到this,这里需要用箭头函数
click: () => {
this.$alert('女生被禁止删除了')
}
}
]
}
])
}
},
methods: {
// 关注作者公众号
$_handleFollowAuthor() {
console.log('微信搜索【前端有的玩】,这是对小编最大的支持')
},
/**
* row 这一行的数据
*/
$_handleEdit(row, column) {
this.$alert(`点击了姓名为【${row.name}】的行上的按钮`)
}
}
}
通过上面的代码就可以完成以下效果
最后再来一起看看行编辑
行编辑
比如上例,我希望点击行尾的编辑按钮的时候,可以直接在行上面编辑用户的姓名与性别,如何配置呢?
ref="table"
:columns="columns"
:data="data"
/>
export default {
data() {
return {
columns: Object.freeze([
{
label: '姓名',
prop: 'name',
editable: true,
field: {
componentType: 'input',
rules: [
{
required: true,
message: '请输入姓名'
}
]
}
},
{
label: '性别',
prop: 'sex',
// 格式化表格,与element-ui 的表格属性相同
formatter(row, column, cellValue) {
return cellValue === '1' ? '男' : '女'
},
editable: true,
field: {
componentType: 'select',
options: [
{
label: '男',
value: '1'
},
{
label: '女',
value: '0'
}
]
}
},
{
label: '年龄',
prop: 'age',
editable: true,
field: {
componentType: 'number'
}
},
{
label: '操作',
actions: [
{
id: 'edit',
text: '编辑',
// 如果当前行启用了编辑,则不显示编辑按钮
before: row => {
return !this.editIds.includes(row.id)
},
click: this.$_handleEdit
},
{
id: 'save',
text: '保存',
// 如果当前行启用了编辑,则显示保存按钮
before: row => {
return this.editIds.includes(row.id)
},
click: this.$_handleSave
}
]
}
]),
data: [
{
// 行编辑必须指定rowKey字段,默认是id,如果修改为其他字段,需要给表格指定row-key="字段名"
id: '0',
name: '子君',
sex: '1',
age: 18
},
{
// 行编辑必须指定rowKey字段,默认是id,如果修改为其他字段,需要给表格指定row-key="字段名"
id: '1',
name: '子君1',
sex: '0',
age: 18
}
],
editIds: []
}
},
methods: {
$_handleEdit(row) {
// 通过调用 startEditRow 可以开启行编辑
this.$refs.table.startEditRow(row.id)
// 记录开启了行编辑的id
this.editIds.push(row.id)
},
$_handleSave(row) {
// 点击保存的时候,通过endEditRow 结束行编辑
this.$refs.table.endEditRow(row.id, (valid, result, oldRow) => {
// 如果有表单验证,则valid会返回是否验证成功
if (valid) {
console.log('修改之后的数据', result)
console.log('原始数据', oldRow)
const index = this.editIds.findIndex(item => item === row.id)
this.editIds.splice(index, 1)
} else {
// 如果校验失败,则返回校验的第一个输入框的异常信息
console.log(result)
this.$message.error(result.message)
}
})
}
}
}
效果如下图所示:
其他功能
除了上面的功能之外,表格还可以配置其他许多功能,比如可以指定字段为链接列,需要给列配置link属性
可以通过插槽自定义顶部按钮,行操作按钮,行字段等
可以在按钮区域右侧通过插槽配置其他内容
其他等等
表格开发说明
表格布局
整个表格是通过JSX来封装的,因为JSX使用起来更加灵活。对于我们封装的表格,我们从竖向可以分为三部分,分别是顶部按钮区,中间表格区,底部分页区,如何去实现三个区域的布局呢,核心代码如下render(h) {
// 按钮区域
const toolbar = this.$_renderToolbar(h)
// 表格区域
const table = this.$_renderTable(h)
// 分页区域
const page = this.$_renderPage(h)
return (
{toolbar}
{table}
{page}
)
}
通过三个render函数分别渲染对应区域,然后将三个区域组合在一起。
渲染表格列
通过前文的讲解,我们可以将表格的列分为以下几种常规列
行编辑列
操作按钮列
插槽列
链接列(文档后续完善)
嵌套列(文档后续完善)$_renderColumns(h, columns) {
// 整体是否排序
let sortable = this.sortable ? 'custom' : false
return columns
.filter(column => {
const { hidden } = column
if (hidden !== undefined) {
if (typeof hidden === 'function') {
return hidden({
columns,
column
})
}
return hidden
}
return true
})
.map(column => {
const {
useSlot = false,
// 如果存在操作按钮,则actions为非空数组
actions = [],
// 是否可编辑列, 对于可编辑列需要动态启用编辑
editable = false,
// 是否有嵌套列
nests,
// 是否可点击
link = false
} = column
let newSortable = sortable
if (column.sortable !== undefined) {
newSortable = column.sortable ? 'custom' : false
}
column = {
...column,
sortable: newSortable
}
if (nests && nests.length) {
// 使用嵌套列
return this.$_renderNestColumn(h, column)
} else if (editable) {
// 使用编辑列
return this.$_renderEditColumn(h, column)
} else if (useSlot) {
// 使用插槽列
return this.$_renderSlotColumn(h, column)
} else if (actions && actions.length > 0) {
// 使用操作列
column.sortable = false
return this.$_renderActionColumn(h, column)
} else if (link) {
// 使用链接列
return this.$_renderLinkColumn(h, column)
} else {
// 使用默认列
return this.$_renderDefaultColumn(h, column)
}
})
},
行编辑列
当前表格行编辑支持input,select,datepicker,TimeSelect,InputNumber等组件,具体渲染代码如下所示// 编辑单元格
$_renderEditCell(h, field) {
const components = {
input: Input,
select: ZjSelect,
date: DatePicker,
time: TimeSelect,
number: InputNumber
}
const componentType = field.componentType
const component = components[componentType]
if (component) {
return this.$_renderField(h, field, component)
} else if (componentType === 'custom') {
// 如果自定义,可以通过component指定组件
return this.$_renderField(h, field, field.component)
}
return this.$_renderField(h, field, Input)
},
$_renderField(h, field, Component) {
// 编辑行的id字段
const { rowId, events = {}, nativeEvents = {} } = field
const getEvents = events => {
const newEvents = {}
Object.keys(events).forEach(key => {
const event = events[key]
newEvents[key] = (...rest) => {
const args = [
...rest,
{
rowId,
row: this.editRowsData[rowId],
value: this.editRowsData[rowId][field.prop]
}
]
return event(...args)
}
})
return newEvents
}
// 事件改写
const newEvents = getEvents(events)
const newNativeEvents = getEvents(nativeEvents)
return (
size="small"
on={newEvents}
nativeOn={newNativeEvents}
v-model={this.editRowsData[rowId][field.prop]}
{...{
attrs: field,
props: field
}}
/>
)
}
总结
这个表格包含了许多功能,文章长度优先,如果觉得有用,可以通过访问 https://github.com/snowzijun/vue-element-table 查看完整代码,本仓库代码及文档小编将持续完善,欢迎star。
结语不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高