日日加班至夜半,环视四周无人走;

明晚八点准时走,谁不打卡谁是狗。

应用过element-ui的表格的同学应该都有这样的领会,做一个简略的表格还比拟容易,但如果这个表格蕴含了顶部的按钮,还有分页,甚至再蕴含了行编辑,那开发工作量就成倍的减少,特地是在开发管理系统的时候,表格一个接一个的去开发, 即浪费时间,还对集体没有什么晋升。明天小编带来了本人封装的一个表格,让你用JSON就能够简略的生成表格。

本文次要集中于应用阐明与外围代码阐明,残缺代码请拜访 https://github.com/snowzijun/vue-element-table,如果感觉有用,麻烦给小编一个star,你的每一个star都是对小编的反对,以后性能比拟简陋,本仓库将继续更新。同时您也能够微信搜寻【前端有的玩】公众号,与小编进行沟通。

表格需要

个别管理系统对表格会有以下需要

  1. 能够分页(须要有分页条)
  2. 能够多选(表格带复选框)
  3. 顶部须要加一些操作按钮(新增,删除等等)
  4. 表格每行行尾有操作按钮
  5. 表格行能够编辑

如下图为一个示例表格

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

  1. 须要应用表格的插槽性能,开发每一行的按钮
  2. 须要通过款式调整顶部按钮,表格,分页条的布局款式
  3. 须要监听分页的事件而后去刷新表格数据
  4. 顶部按钮或操作按钮如果须要获取表格数据,须要调用表格提供的api
  5. 对于有行编辑的需要,还须要通过插槽去渲染行编辑的内容,同时要管制行编辑的开关

不仅仅开发表格比拟麻烦,而且还要思考团队合作,如果每个人实现表格的形式存在差异,那么可能前期的保护老本也会变得很高。那怎么办呢?

表格配置

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

成果如下图所示:

其余性能

除了下面的性能之外,表格还能够配置其余许多性能,比方

  1. 能够指定字段为链接列,须要给列配置link属性
  2. 能够通过插槽自定义顶部按钮,行操作按钮,行字段等
  3. 能够在按钮区域右侧通过插槽配置其余内容
  4. 其余等等

表格开发阐明

通过下面的代码示例,咱们曾经晓得了封装之后的表格能够实现哪些事件,接下来一起来看看表格是如何实现的。残缺代码见 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函数别离渲染对应区域,而后将三个区域组合在一起。

渲染表格列

通过前文的解说,咱们能够将表格的列分为以下几种

  1. 惯例列
  2. 行编辑列
  3. 操作按钮列
  4. 插槽列
  5. 链接列(文档后续欠缺)
  6. 嵌套列(文档后续欠缺)
    $_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          }}        />      )    }

总结

这个表格蕴含了许多性能,文章长度优先,如果感觉有用,能够通过拜访 https://github.com/snowzijun/vue-element-table 查看残缺代码,本仓库代码及文档小编将继续欠缺,欢送star。缘始积攒,心愿你能够关注一下下方公众号,这是对小编最大的反对

结语

不要吹灭你的灵感和你的想象力; 不要成为你的模型的奴隶。 ——文森特・梵高