日日加班至夜半,环视周围无人走; 明晚八点准时走,谁不打卡谁是狗。前端
使用过element-ui
的表格的同窗应该都有这样的体会,作一个简单的表格还比较容易,但若是这个表格包含了顶部的按钮,还有分页,甚至再包含了行编辑,那开发工做量就成倍的增长,特别是在开发管理系统的时候,表格一个接一个的去开发, 即浪费时间,还对我的没有什么提高。今天小编带来了本身封装的一个表格,让你用JSON
就能够简单的生成表格。vue
本文主要集中于封装思想与核心代码说明,完整代码请访问 github.com/snowzijun/v…,若是以为有用,麻烦给小编一个
star
,你的每个star
都是对小编的支持,当前功能比较简陋,本仓库将持续更新。同时您也能够微信搜索【前端有的玩】公众号,与小编进行沟通。git
通常管理系统对表格会有如下需求github
以下图为一个示例表格npm
若是咱们直接使用element-ui
提供的组件的话,那么开发一个这样的表格就须要使用到如下内容element-ui
不只仅开发表格比较麻烦,并且还要考虑团队协做,若是每一个人实现表格的方式存在差异,那么可能后期的维护成本也会变得很高。那怎么办呢?api
在使用element-ui
的项目中,能够经过如下命令进行安装数组
npm install vue-elementui-table -S
复制代码
在main.js
中添加如下代码微信
import ZjTable from 'vue-elementui-table' Vue.use(ZjTable) 复制代码
而后便可像下文中的使用方式进行使用markdown
为了知足团队快速开发的须要,小编对上面提出来的需求进行了封装,而后使用的时候,开发人员只须要配置一些JSON
即可以完成以上功能的开发。
一个基础的表格包含了数据和列信息,那么如何用封装的表格去配置呢?
<template>
<zj-table
:columns="columns"
:data="data"
:pagination="false"
/>
</template>
<script>
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
}
]
}
}
}
</script>
复制代码
经过上面的配置,就能够完成一个基础表格的开发,完整代码见 github.com/snowzijun/v…,效果以下图所示
表格默认会显示复选框,也能够经过配置selectable
属性来关闭掉
简单的表格用封装以后的或未封装的开发工做量区别并不大,咱们继续为表格添加上分页
<template>
<!--
current-page.sync 表示页码, 添加上 .sync 在页码发生变化时自动同步页码
page-size.sync 每页条数
total 总条数
height="auto" 配置height:auto, 表格高度会根据内容自动调整,若是不指定,表格将保持充满父容器,同时表头会固定,不跟随滚动条滚动
@page-change 不管pageSize currentPage 哪个变化,都会触发这个事件
-->
<zj-table
v-loading="loading"
:columns="columns"
:data="data"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
height="auto"
@page-change="$_handlePageChange"
/>
</template>
<script>
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()
}
}
}
</script>
复制代码
完整代码请参考 github.com/snowzijun/v…
经过封装,表格将自带分页功能,经过上面代码,实现效果以下所示,是否是变得简单了一些。接下来咱们继续给表格添加按钮
表格上面可能会有新增,删除等等按钮,怎么办呢,接下来咱们继续经过配置去添加按钮
<template>
<zj-table
:buttons="buttons"
/>
</template>
<script>
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() {}
}
}
</script>
复制代码
表格顶部能够添加普通的按钮,也能够添加下拉按钮,同时还能够经过before
来配置按钮是否显示,disabled
来配置按钮是否禁用,上面完整代码见 github.com/snowzijun/v…
经过上面的代码就能够配置出下面的表格,是否是很简单呢?
表格顶部能够有按钮,行尾也是能够添加按钮的,一块儿来看看
通常咱们会将一些单行操做的按钮放在行尾,好比编辑,下载等按钮,那如何给行尾配置按钮呢?
<template> <zj-table :columns="columns" /> </template> <script> 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}】的行上的按钮`) } } } </script> 复制代码
行操做按钮会被冻结到表格最右侧,不会跟随滚动条滚动而滚动,上面完整代码见, github.com/snowzijun/v…
经过上面的代码就能够完成如下效果
最后再来一块儿看看行编辑
好比上例,我但愿点击行尾的编辑按钮的时候,能够直接在行上面编辑用户的姓名与性别,如何配置呢?
<template> <zj-table ref="table" :columns="columns" :data="data" /> </template> <script> 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) } }) } } } </script> 复制代码
不须要使用插槽就能够完成行编辑,是否是很开心。上述完整代码见 github.com/snowzijun/v…
效果以下图所示:
除了上面的功能以外,表格还能够配置其余许多功能,好比
link
属性经过上面的代码示例,咱们已经知道了封装以后的表格能够完成哪些事情,接下来一块儿来看看表格是如何实现的。完整代码见 github.com/snowzijun/v…
整个表格是经过JSX
来封装的,由于JSX
使用起来更加灵活。对于咱们封装的表格,咱们从竖向能够分为三部分,分别是顶部按钮区,中间表格区,底部分页区,如何去实现三个区域的布局呢,核心代码以下
render(h) { // 按钮区域 const toolbar = this.$_renderToolbar(h) // 表格区域 const table = this.$_renderTable(h) // 分页区域 const page = this.$_renderPage(h) return ( <div class="zj-table" style={{ height: this.tableContainerHeight }}> {toolbar} {table} {page} </div> ) } 复制代码
经过三个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 ( <Component size="small" on={newEvents} nativeOn={newNativeEvents} v-model={this.editRowsData[rowId][field.prop]} {...{ attrs: field, props: field }} /> ) } 复制代码
这个表格包含了许多功能,文章长度优先,若是以为有用,能够经过访问 github.com/snowzijun/v… 查看完整代码,本仓库代码及文档小编将持续完善,欢迎star。缘始积累,关注公众号,小编拉你进前端交流群
阅读小编近期的热门
Vue
相关文章,我是子君,每周一,不见不散
实战技巧,Vue原来还能够这样写 获赞 2700+
绝对干货~!学会这些Vue小技巧,能够早点下班和女神约会了 获赞 1200+
前方高能,这是最新的一波Vue实战技巧,不用则已,一用惊人 获赞 1000+
我在项目中是这样配置Vue的 获赞 1000+
学会使用Vue JSX,一车老干妈都是你的 获赞700+
让Vue项目更丝滑的几个小技巧 获赞 300+
看到赚到!重读vue2.0风格指南,我整理了这些关键规则 获赞 150+
不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高
本文使用 mdnice 排版