基于elementui尝试封装一个表格组件

1,737 阅读4分钟

基于elementui封装一个表格组件

前言

在后台管理页面中,我们总是难免会和表单以及表格打交道,根据业务需求的不同,它们的复杂程度也不尽相同,上一篇封装了后台管理系统中的表单组件,本文打算继续分享一下封装一个简单的表格组件过程。

组件需求

通过归纳公司过往的后台管理系统,将本次表格需要满足的需求总结如下

  • 需要表格顶部具有页面唯一性按钮,如导出,批量操作按钮,如删除。
  • 要带有分页功能。
  • 需要具有行间编辑操作,如详情,修改,删除。
  • 表格自定义列样式。
  • 表格自定义展示列。

放上一个完成的实例

封装组件

基础结构

按照上面说到的表格组件的需求,我们先写出页面结构,将组件大致划分为了三个部分,顶部操作按钮,中间的表格主体,底部的分页。

<template>
  <div class="table">
    <div class="top-btn">
      <div class="btn-left">
        <el-button v-if="option.addBtn" type="primary" :size="option.addBtnSize || 'small'" @click="add">
          {{option.addBtnText}}
        </el-button>
      </div>
      <div class="btn-right">
        <el-tooltip v-if="option.refreshBtn" content="刷新" placement="top">
          <el-button @click="refresh"><i class="el-icon-refresh"></i></el-button>
        </el-tooltip>
      </div>
    </div>
    <el-table
      class="table-main"
      style="width: 100%"
      :data="tableData">
      <el-table-column
        v-for="item in option.group"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :min-width="item.minWidth? item.minWidth : '120'"
        :width="item.width">
      </el-table-column>
    </el-table>
    <el-pagination
      class="table-page"
      layout="total, sizes, prev, pager, next, jumper"
      :current-page="pageInfo.currentPage"
      :page-sizes="pageInfo.pageSizes"
      :page-size="pageInfo.pageSize"
      :total="pageInfo.total"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      >
    </el-pagination>
  </div>
</template>




表格基础配置

通过上面的规划,我们需要往组件中传入几个配置项以渲染我们需要的表格。

<script>
export default {
  name: "Table",
  data() {
    return {
      defaultTableOption: {
        addBtn: true, //是否展示新增按钮
        addBtnText: '新增', //新增按钮文字
        refreshBtn: true, //是否展示刷新表格按钮
        group: [], //表格列的配置 同elementui
      },
      defaultTablePage: {
        total: 0, // 分页总条数
        currentPage: 0, // 当前页码
        pageSize: 0, // 当前页显示多少条
        pageSizes: [10, 20, 50, 100] //可选当前页显示条数
      }
    }
  },
  props: {
    tableData: {
      type: Array,
      default: () => []
    },
    tableOption: {
      type: Object,
      default: () => {}
    },
    tablePage: {
      type: Object,
      default: () => {}
    }
  },
  computed: {
    option() {
      return Object.assign(this.defaultTableOption,this.tableOption)
    },
    pageInfo() {
      return Object.assign(this.defaultTablePage,this.tablePage)
    }
  },
  methods: {
    add() {
      this.$emit("add")
    },
    handleSizeChange() {
      this.$emit("handleSizeChange")
    },
    handleCurrentChange() {
      this.$emit("handleCurrentChange")
    },
    refresh() {
      this.$emit("refresh")
    }
  }
};
</script>

这里我们初始配置了表格的基本配置项,通过computed属性将用户传入配置属性合并最终渲染组件。

我们需要传入的配置项为:

  • tableData:接收数组,表格数据
  • tableOption:接收对象,表格的具体配置项,group为表格列配置,包括顶部按钮的配置
  • tablePage:接收数组,表格分页数据

自定义列

要提供给用户自己自定义列样式,所以我们要在组件中预留出插槽,然后改写我们的组件。

 <el-table
      class="table-main"
      style="width: 100%"
      :data="tableData">
      <el-table-column
        v-for="item in option.group"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :min-width="item.minWidth? item.minWidth : '120'"
        :width="item.width">
          <div v-if="item.type === 'text'">
            {{scope.row[item.prop]}}
          </div>
          <div v-if="item.type === 'customSlot'">
            <slot :name="item.prop" :row="scope.row"></slot>
          </div>
      </el-table-column>
</el-table>

可以看出我们为表格列配置中添加了type属性,当type的值为text则为普通数据展示,为customSlot时,预留插槽并将行数据传给父组件。

表格行末操作

一般我们会在表格的单行末尾添加一些单行操作按钮,如修改,删除,具体应当如何实现呢?

<el-table
      class="table-main"
      style="width: 100%"
      :data="tableData"
      @selection-change="handleSelectionChange">
      <el-table-column
        v-for="item in option.group"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :min-width="item.minWidth? item.minWidth : '120'"
        :width="item.width">
        <template slot-scope="scope">
          <div v-if="item.type === 'operation'">
            <el-button 
               v-if="option.rowViewBtn" 
               @click="handleView(scope.row)" 
               type="text" 
               size="small">查看</el-button>
            <el-button 
               v-if="option.rowEditBtn"
               @click="handleEdit(scope.row)" 
               type="text" 
               size="small">编辑</el-button>
            <el-button 
               v-if="option.rowDeleteBtn"
               @click="handleDelete(scope.row)" 
               type="text" 
               size="small">删除</el-button>
            <slot name="tailEdit" :row="scope.row"></slot>
          </div>
          <div v-if="item.type === 'text'">
            {{scope.row[item.prop]}}
          </div>
        </template>
      </el-table-column>
</el-table>
  • 首先我们通过表格列配置中的type来判断是普通数据展示,还是行末操作列。
  • 然后我们通过插槽来实现行末操作按钮,并且预留出一个插槽来继续供我们根据业务需求来展示不同的操作按钮。
  • 同时在tableOption中添加了三个配置项rowViewBtnrowEditBtnrowDeleteBtn来控制基础的查看,编辑,删除按钮是否存在。

在主页面中引用组件

<template>
  <div id="app">
    <ComTable :tableData="tableData" :tableOption="tableOption" :tablePage="tablePage" >
      <template slot="tailEdit" slot-scope="scope" >
        <span style="color: #409EFF">{{scope.row.age}}</span>
      </template>
    </ComTable>
  </div>
</template>

<script>
import ComTable from "./components/table.vue";

export default {
  name: "App",
  components: {
    ComTable,
  },
  data() {
    return {
      tableOption: {
        group: [
          {
            label: '姓名',
            type: 'text',
            prop: 'name'
          },
          {
            label: '年龄',
            type: 'text',
            prop: 'age'
          },
          {
            label: '性别',
            type: 'text',
            prop: 'sex'
          },
          {
            label: '操作',
            type: 'operation',
            prop: 'operation'
          },
        ],
      },
      tableData: [
        {
          name: '张三',
          sex: '男',
          age: '23',
        },
        {
          name: '李四',
          sex: '女',
          age: '19',
        },
        {
          name: '王二',
          sex: '男',
          age: '43',
        },
      ],
      tablePage: {}
    }
  },
};
</script>

实现效果:

行多选

顶部操作按钮进行批量操作,当然离不开多选行,所以针对组件进行对应修改

<el-table
      class="table-main"
      style="width: 100%"
      :data="tableData"
      @selection-change="handleSelectionChange">
      <el-table-column
        v-if="option.group.map(item => item.type).indexOf('selection') !== -1"
        type="selection"
        width="55">
      </el-table-column>
      <el-table-column
        v-for="item in option.group.filter(item => item.type !== 'selection')"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :min-width="item.minWidth? item.minWidth : '120'"
        :width="item.width">
      </el-table-column>
    </el-table>

所以当我们在表格列配置中添加type的值为selection的表格列时,表格便具备了行多选。

表格列显隐

我的做法时动态改变表格列数据达到表格显隐

<template>
  <div class="table">
    <div class="top-btn">
      <div class="btn-right">
        <el-tooltip v-if="option.columnEditBtn" content="显隐" placement="top">
          <el-button @click="dialogFlag = true"><i class="el-icon-s-operation"></i></el-button>
        </el-tooltip>
      </div>
    </div>
    <el-table
      class="table-main"
      style="width: 100%"
      :data="tableData">
      <el-table-column
        v-for="item in option.group.filter(item => item.type !== 'selection')"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :min-width="item.minWidth? item.minWidth : '120'"
        :width="item.width">
      </el-table-column>
    </el-table>
    <el-dialog
      title="展示列"
      :visible.sync="dialogFlag"
      width="30%">
      <div>
          <el-checkbox-group @change="showItemColumn" v-model="selectColumns">
            <el-checkbox v-for="item in defaultColumns" :key="item.label" :label="item.label"></el-checkbox>
          </el-checkbox-group>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button type="primary" @click="dialogFlag = false">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
export default {
  name: "Table",
  data() {
    return {
      dialogFlag: false,
      selectColumns: [],
      defaultColumns: [],
      defaultTableOption: {},
      defaultTablePage: {}
    }
  },
  props: {
    tableData: {
      type: Array,
      default: () => []
    },
    tableOption: {
      type: Object,
      default: () => {}
    },
    tablePage: {
      type: Object,
      default: () => {}
    }
  },
  computed: {
    option() {
      return Object.assign(this.defaultTableOption,this.tableOption)
    },
    pageInfo() {
      return Object.assign(this.defaultTablePage,this.tablePage)
    }
  },
  created() {
    this.selectColumns = this.option.group.map(item => item.label)
    this.defaultColumns = JSON.parse(JSON.stringify(this.option.group))
  },
  methods: {
    showItemColumn(e) {
      this.option.group = this.defaultColumns.filter(item => e.indexOf(item.label) !== -1)
    }
  }
};
</script>

效果如图:

然后它出bug了啊,直接替换被渲染的数组,导致了页面重绘,表格抽风般的抖动了,让我再想想怎么解决。

仔细看了一遍elementui中table组件的api文档后,发现官方在table组件身上提供了一个doLayout方法用于为表格重新布局。所以当我们修改列时,可以通过$nextTick在下一次dom更新完成后调用该方法。

修改后,再次显隐表格列时,没有表格不在抖动了。

总结

到这里,一个简单的表格组件就封装结束,如果你觉得感兴趣,你可以尝试访问github.com/LiveDragon/…查看完整代

感谢

最后感谢掘友们的支持,如果本文有帮助到你的地方,记得点赞哦。