基于 antd 风格的 element-table + pagination 的二次封装

前言

本次封装基于 antd 风格, 实现高度可配置的表格封装配置。本来想通过 vue.extends 去封装的,奈何几个月没写过 vue ,而且对 vueextends 不熟悉所以放弃了...

之前有小伙伴确实引用了我的代码,发现封装出现了一些纰漏,对此十分抱歉,之前封装的太仓促了。几个月前的代码,现在重新封装又有了新的体会。

更新时间 【2018.11.09】,效果如下:

image

API 说明

  • columns : 必选, 列描述数据对象, Array
  • dataSource : 必选, 数据数组
  • options : 必选, 表格参数控制, maxHeight、stripe 等等..
  • fetch : 获取数据的 Function
  • pagination : 分页信息,不传则不显示分页
  • row-click :当某一行被点击时会触发该事件
  • selection-change : 当选择项发生变化时会触发该事件
  • 其他的 api 可以自行添加

其他说明我在代码注释中写的很清楚了,请自行查看。

根据条件渲染: 只通过 render 去判断参数不同而渲染不一样的表格数据。 render 函数可以渲染任何你想要的组件

值得注意的是,this 对象的绑定不要出错了,如果需要更多增强的功能,各位可以自行添加...

Home.vue 组件

<template>
    <div>
      <h2>Home</h2>
      <CommonTable 
        :columns="columns" 
        :dataSource="tableData" 
        :options="options"
        :fetch="fetchTableData"
        :pagination="pagination"
        @row-click="handleRowClick"
        @selection-change="handleSelectionChange"
        />
    </div>
</template>

<script>
import axios from 'axios'
import CommonTable from '../components/Table'

export default{
  components:{
    CommonTable
  },
  data(){
    return {
      columns: [
         {
          prop: 'id',
          label: '编号',
          width: 60
        },
        {
          prop: 'title',
          label: '标题',
          // render 可以根据你想要渲染的方式渲染
          // jsx 不提供 v-model 指令,若你想要使用,,推荐使用插件 babel-plugin-jsx-v-model
          // jsx https://github.com/vuejs/babel-plugin-transform-vue-jsx
          render: (row, index) => { 
            return (
              <span style="color: blue" onClick={e => this.handleClick(e, row)}>{row.title}</span>
            )
          }
        },
        {
          prop: 'author',
          label: '作者'
        },
        {
          button: true,
          label: '按钮组',
          group: [{
            // you can props => type size icon disabled plain
            name: '编辑',
            type: 'warning',
            icon: 'el-icon-edit',
            plain: true,
            onClick: (row, index) => { // 箭头函数写法的 this 代表 Vue 实例 
              console.log(row, index)
            }
          }, {
            name: '删除',
            type: 'danger',
            icon: 'el-icon-delete',
            disabled: false,
            onClick(row) { // 这种写法的 this 代表 group 里的对象
              this.disabled = true
              console.log(this)
            }
          }]
        }
      ],
      tableData: [
        {
          id: 1,
          title: '标题1',
          author: '郭大大'
        },
        {
          id: 2,
          title: '标题2',
          author: '郭大大2'
        }
      ],
      pagination: {
        total: 0,
        pageIndex: 1,
        pageSize: 15
      },
      options: {
        mutiSelect: true,
        index: true, // 显示序号, 多选则 mutiSelect
        loading: false, // 表格动画
        initTable: true, // 是否一挂载就加载数据
      }
    }
  },
  methods: {
    handleClick(e, row){
      //transform-vue-jsx 的nativeOnClick 失效 , 所以采用 event.cancelBubble 控制点击事件的冒泡... 如果点击事件不影响你的点击行事件,可以不传
      e.cancelBubble = true // 停止冒泡,否则会触发 row-click
      console.log(row)
    },
    fetchTableData() {
       this.options.loading = true
       axios.post('https://www.easy-mock.com/mock/5b3f80edfa972016b39fefbf/example/tableData', {
        pageIndex: this.pagination.pageIndex,
        pageSize: this.pagination.pageSize
      }).then(res => {
        const { list, total } = res.data.data
        this.tableData = list
        this.pagination.total = total
        this.options.loading = false
      }).catch((error) => {
        console.log(error)
        this.options.loading = false
      })
    },
    handleRowClick(row, event, column){ // 点击行的事件,同理可以绑定其他事件
      console.log('click row:',row, event, column)
    },
    handleSelectionChange(selection){
      console.log(selection)
    }
  }
}
</script>

Table.vue 组件

<template>
  <div>
    <el-table
      v-loading="options.loading"
      :data="dataSource"
      :max-height="options.maxHeight"
      :stripe="options.stripe"
      :border="options.border"
      @row-click="handleRowClick"
      @selection-change="handleSelectionChange"
      header-row-class-name="table-header-row">

      <!--selection选择框-->
      <el-table-column v-if="options.mutiSelect" type="selection" style="width:50px" align="center"></el-table-column>

      <!--序号-->
      <el-table-column v-if="options.index" label="序号" type="index" width="50" align="center"></el-table-column>

      <!--数据列-->
      <template v-for="(column, index) in columns">
        <el-table-column
          :key="index"
          :prop="column.prop"
          :label="column.label"
          :align="column.align||'center'"
          :width="column.width"
          :fixed="column.fixed">
          <template slot-scope="scope">

            <template v-if="!column.render">
              {{scope.row[column.prop]}}
            </template>

             <!-- render -->
            <template v-else>
              <RenderDom :row="scope.row" :index="index" :render="column.render" />
            </template>

            <!-- render button -->
            <template v-if="column.button">
              <template v-for="(btn, i) in column.group">
                <el-button
                  :key="i"
                  :type="btn.type" :size="btn.size || 'mini'" :icon="btn.icon" :disabled="btn.disabled" :plain="btn.plain"
                   @click.stop="btn.onClick(scope.row, scope.$index)"
                  >{{btn.name}}</el-button>
              </template>
            </template>

            <!-- slot 你可以其他常用项 -->

          </template>

        </el-table-column>
      </template>

    </el-table>

     <!-- 分页 -->
    <el-pagination
        v-if="pagination"
        :total="pagination.total"
        :page-sizes="[20, 50, 100, 500, 5000]"
        layout="total, sizes, prev, pager, next, jumper"
        @size-change="handleSizeChange"
        @current-change="handleIndexChange"
        style="margin-top: 20px;text-align: right"
    ></el-pagination>

  </div>
</template>

<script>
  export default {
    components: {
      RenderDom: {
        functional: true, // 函数式组件 - 无 data 和 this 上下文 => better render
        props: {
          row: Object,
          index: Number,
          render: Function
        },
        /**
         * @param {Function} createElement - 原生创建dom元素的方法, 弃用,推荐使用 jsx
         * @param {Object} ctx - 渲染的节点的this对象
         * @argument 传递参数 row index
         */
        render(createElement, ctx){
          const { row, index } = ctx.props
          return ctx.props.render(row, index)
        }
      }
    },
    props:{
      dataSource: Array,
      options: Object,   // 表格参数控制 maxHeight、stripe 等等...
      columns: Array,
      fetch: Function,   // 获取数据的函数
      pagination: Object // 分页,不传则不显示
    },
    created() {
      // 传入的options覆盖默认设置
      this.$parent.options = Object.assign({
          maxHeight: 500,
          stripe: true, // 是否为斑马纹
          border: true
      }, this.options)

      this.options.initTable && this.fetch()
    },
    methods: {
      handleSizeChange(size) { // 切换每页显示的数量
        this.pagination.pageSize = size
        this.fetch()
      },
      handleIndexChange(current) { // 切换页码
        this.pagination.pageIndex = current
        this.fetch()
      },
      handleSelectionChange(selection) {
        this.$emit('selection-change', selection)
      },
      handleRowClick(row, event, column) {
        this.$emit('row-click', row, event, column)
      }
    }
  }
</script>

<style>
.el-table th,
.el-table tr.table-header-row {
  background: #e5c5d2; /* 示例, 对表格样式上的修饰 */
}
</style>

结语

上述代码封装完整性可能不是这么高,但思路在呢,如果需要更多配置,各位可以在进行加强...

吐槽一下,本来是想 props 数据来重写 table 参数,类似 react:

<Home>
  <ComonTable {...props} >
</Home>

// ComonTable
<el-table {...props.options}>
</el-table>

所以想到继承,自己又不熟悉。 而且发现 vue 展开绑定多个属性是不可以的: 可能是我没 google 到。如果可以,请大佬告知一声,谢谢

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348

推荐阅读更多精彩内容

  • 作业一 全局把控五分钟:拿到一本书只管去做,把目录翻过一遍,把整书又翻过一遍,里面没有太多的插图,有的只是一些简笔...
    詹绯阅读 127评论 0 0
  • 上高中的时候因为种种原因我转学了,转到另外一个教学质量比较好的学校。转学过去第一天我见到了我的室长Z,比...
    xuanyuanqingy阅读 198评论 0 0
  • 在经历一整年的大肆炒作后,许多关注智能硬件的人们,纷纷发现他们「被骗了」:2014年乃至之前涌现出的一大堆所谓的「...
    斐德瑞克王阅读 369评论 0 2