乐趣区

关于element-ui:封装elementui表格我是这样做的

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

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

应用过 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。缘始积攒,心愿你能够关注一下下方公众号,这是对小编最大的反对

结语

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

退出移动版