封装element-ui表格,我是这样做的

封装element-ui表格,我是这样做的使用过element-ui的表格的同学应该都有这样的体会,做一个简单的表格还比较容易,但如果这个表格包含了顶部的按钮,还有分页,甚至再包含了行编辑,那开发工作量就成倍的增加,特别是在开发管理系统的时候,表格一个接一个的去开发, 即浪费时间,还对个人没有什么提升。今天小编带来了自…

日日加班至夜半,环视周围无人走; 明晚八点准时走,谁不打卡谁是狗。

使用过element-ui的表格的同学应该都有这样的体会,做一个简单的表格还比较容易,但如果这个表格包含了顶部的按钮,还有分页,甚至再包含了行编辑,那开发工作量就成倍的增加,特别是在开发管理系统的时候,表格一个接一个的去开发, 即浪费时间,还对个人没有什么提升。今天小编带来了自己封装的一个表格,让你用JSON就可以简单的生成表格。

本文主要集中于封装思想与核心代码说明,完整代码请访问 github.com/snowzijun/v…,如果觉得有用,麻烦给小编一个star,你的每一个star都是对小编的支持,当前功能比较简陋,本仓库将持续更新。同时您也可以微信搜索【前端有的玩】公众号,与小编进行沟通。

表格需求

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

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

如下图为一个示例表格

封装element-ui表格,我是这样做的

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

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

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

项目安装

安装插件

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

npm install vue-elementui-table -S

在项目中使用

main.js中添加以下代码

import ZjTable from 'vue-elementui-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>

通过上面的配置,就可以完成一个基础表格的开发,完整代码见 github.com/snowzijun/v…,效果如下图所示

封装element-ui表格,我是这样做的

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

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

封装element-ui表格,我是这样做的

添加顶部按钮

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

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

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

封装element-ui表格,我是这样做的

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

行操作按钮

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

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

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

封装element-ui表格,我是这样做的

最后再来一起看看行编辑

行编辑

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

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

效果如下图所示:

封装element-ui表格,我是这样做的

其他功能

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

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

表格开发说明

通过上面的代码示例,我们已经知道了封装之后的表格可以完成哪些事情,接下来一起来看看表格是如何实现的。完整代码见 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函数分别渲染对应区域,然后将三个区域组合在一起。

渲染表格列

通过前文的讲解,我们可以将表格的列分为以下几种

  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
          }}
        />
      )
    }

总结

这个表格包含了许多功能,文章长度优先,如果觉得有用,可以通过访问 github.com/snowzijun/v… 查看完整代码,本仓库代码及文档小编将持续完善,欢迎star。缘始积累,关注公众号,小编拉你进前端交流群

封装element-ui表格,我是这样做的

阅读小编近期的热门Vue相关文章,我是子君,每周一,不见不散

实战技巧,Vue原来还可以这样写 获赞 2700+

绝对干货~!学会这些Vue小技巧,可以早点下班和女神约会了 获赞 1200+

前方高能,这是最新的一波Vue实战技巧,不用则已,一用惊人 获赞 1000+

我在项目中是这样配置Vue的 获赞 1000+

学会使用Vue JSX,一车老干妈都是你的 获赞700+

让Vue项目更丝滑的几个小技巧 获赞 300+

看到赚到!重读vue2.0风格指南,我整理了这些关键规则 获赞 150+

结语

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

本文使用 mdnice 排版

今天的文章封装element-ui表格,我是这样做的分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17923.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注