封装element-ui表格

2020-7-30    seo达人

表格需求

一般管理系统对表格会有以下需求


可以分页(需要有分页条)

可以多选(表格带复选框)

顶部需要加一些操作按钮(新增,删除等等)

表格每行行尾有操作按钮

表格行可以编辑

如下图为一个示例表格




如果我们直接使用element-ui提供的组件的话,那么开发一个这样的表格就需要使用到以下内容


需要使用表格的插槽功能,开发每一行的按钮

需要通过样式调整顶部按钮,表格,分页条的布局样式

需要监听分页的事件然后去刷新表格数据

顶部按钮或操作按钮如果需要获取表格数据,需要调用表格提供的api

对于有行编辑的需求,还需要通过插槽去渲染行编辑的内容,同时要控制行编辑的开关

不仅仅开发表格比较麻烦,而且还要考虑团队协作,如果每个人实现表格的方式存在差别,那么可能后期的维护成本也会变得很高。那怎么办呢?


项目安装

安装插件

在使用element-ui的项目中,可以通过以下命令进行安装


npm install vue-elementui-table -S

在项目中使用

在main.js中添加以下代码


import ZjTable from 'vue-element-table'


Vue.use(ZjTable)

然后即可像下文中的使用方式进行使用


表格配置

为了满足团队快速开发的需要,小编对上面提出来的需求进行了封装,然后使用的时候,开发人员只需要配置一些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>

通过上面的配置,就可以完成一个基础表格的开发,完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/base.vue,效果如下图所示




表格默认会显示复选框,也可以通过配置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>

完整代码请参考 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/pagination.vue


通过封装,表格将自带分页功能,通过上面代码,实现效果如下所示,是不是变得简单了一些。接下来我们继续给表格添加按钮




添加顶部按钮

表格上面可能会有新增,删除等等按钮,怎么办呢,接下来我们继续通过配置去添加按钮


<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来配置按钮是否禁用,上面完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


通过上面的代码就可以配置出下面的表格,是不是很简单呢?




表格顶部可以有按钮,行尾也是可以添加按钮的,一起来看看


行操作按钮

一般我们会将一些单行操作的按钮放在行尾,比如编辑,下载等按钮,那如何给行尾配置按钮呢?


<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>

行操作按钮会被冻结到表格最右侧,不会跟随滚动条滚动而滚动,上面完整代码见, https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/button.vue


通过上面的代码就可以完成以下效果




最后再来一起看看行编辑


行编辑

比如上例,我希望点击行尾的编辑按钮的时候,可以直接在行上面编辑用户的姓名与性别,如何配置呢?


<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>

不需要使用插槽就可以完成行编辑,是不是很开心。上述完整代码见 https://github.com/snowzijun/vue-element-table/blob/master/example/views/demo/row-edit.vue


效果如下图所示:




其他功能

除了上面的功能之外,表格还可以配置其他许多功能,比如


可以指定字段为链接列,需要给列配置link属性

可以通过插槽自定义顶部按钮,行操作按钮,行字段等

可以在按钮区域右侧通过插槽配置其他内容

其他等等

表格开发说明

通过上面的代码示例,我们已经知道了封装之后的表格可以完成哪些事情,接下来一起来看看表格是如何实现的。完整代码见 https://github.com/snowzijun/vue-element-table/tree/master/src/components/zj-table


表格布局

整个表格是通过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

         }}

       />

     )

   }

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


分享本文至:

日历

链接

个人资料

蓝蓝设计的小编 http://www.lanlanwork.com

存档