element el-table表格的二次封装实现(附表格高度自适应)

(编辑:jimmy 日期: 2024/12/23 浏览:2)

前言

在公司实习使用vue+element-ui框架进行前端开发,使用表格el-table较为多,有些业务逻辑比较相似,有些地方使用的重复性高,如果多个页面使用相同的功能,就要多次重复写逻辑上差不多的代码,所以打算对表格这个组件进行封装,将相同的代码和逻辑封装在一起,把不同的业务逻辑抽离出来。话不多说,下面就来实现一下吧。

一、原生el-tbale代码——简单の封装

这里直接引用官方的基础使用模板,直接抄过来("htmlcode">

 <template>
  <el-table
   :data="tableData"
   style="width: 100%">
   <el-table-column
    prop="date"
    label="日期"
    width="180">
   </el-table-column>
   <el-table-column
    prop="name"
    label="姓名"
    width="180">
   </el-table-column>
   <el-table-column
    prop="address"
    label="地址">
   </el-table-column>
  </el-table>
 </template>

 <script>
  export default {
   data() {
    return {
     tableData: [{
      date: '2016-05-02',
      name: '王小虎',
      address: '上海市普陀区金沙江路 1518 弄'
     }, {
      date: '2016-05-04',
      name: '王小虎',
      address: '上海市普陀区金沙江路 1517 弄'
     }, {
      date: '2016-05-01',
      name: '王小虎',
      address: '上海市普陀区金沙江路 1519 弄'
     }, {
      date: '2016-05-03',
      name: '王小虎',
      address: '上海市普陀区金沙江路 1516 弄'
     }]
    }
   }
  }
 </script>

表格の样子

element el-table表格的二次封装实现(附表格高度自适应)

封装后

<template>
 <el-table :data="tableData" style="width: 100%">
  <template v-for="(item, key) in header">
   <el-table-column
    :key="key"
    :prop="itm.prop "
    :label="itm.label "
    :width="itm.width "
   >
   </el-table-column>
  </template>
 </el-table>
</template>

<script>
export default {
 data() {
  return {
   header: [
    { prop: "date", label: "日期", width: "180" },
    { prop: "name", label: "姓名", width: "180" },
    { prop: "address", label: "地址" }
   ],
   tableData: [
    {
     date: "2016-05-02",
     name: "王小虎",
     address: "上海市普陀区金沙江路 1518 弄"
    },
    {
     date: "2016-05-04",
     name: "王小虎",
     address: "上海市普陀区金沙江路 1517 弄"
    },
    {
     date: "2016-05-01",
     name: "王小虎",
     address: "上海市普陀区金沙江路 1519 弄"
    },
    {
     date: "2016-05-03",
     name: "王小虎",
     address: "上海市普陀区金沙江路 1516 弄"
    }
   ]
  };
 }
};
</script>

现在数据还比较少,可能看不出封装组件封装的优势,但是相对于之前代码,这里逻辑上看起来更加清晰,而且修改列的时候直接改动data中的header数据即可,不用再去html代码中去“开刀”( ̄▽ ̄)/。上面是最最最简单的封装了,严格来说只是简单的抽离了一下代码中的数据结构,在正常的业务中肯定不止这么简单的封装,接下来才是重点─━ _ ─━",那就是render函数,关于render可以参考一下这篇博客,使用render函数就可以轻而易举的将这部分逻辑代码抽离出来了。

el-table真正の二次封装

二次封装源代码

<template>
 <el-table
  empty-text="暂无数据"
  ref="table"
  :data="tableList"
  border
  stripe
  fit
  highlight-current-row
  :height="inTableHeight"
  @selection-change="selectionChange"
  @row-click="rowClick"
 >
  <!-- 选择框 -->
  <el-table-column
   v-if="select"
   type="selection"
   fixed="left"
   width="55"
   align="center"
  />
  <template v-for="(itm, idx) in header">
   <!-- 特殊处理列 -->
   <el-table-column
    v-if="itm.render"
    :key="idx"
    :prop="itm.prop "
    :label="itm.label "
    :width="itm.width "
    :sortable="itm.sortable "
    :align="itm.align "
    :fixed="itm.fixed "
    :show-overflow-tooltip="itm.tooltip"
    min-width="50"
   >
    <template slot-scope="scope">
     <ex-slot
      :render="itm.render"
      :row="scope.row"
      :index="scope.$index"
      :column="itm"
     />
    </template>
   </el-table-column>
   <!-- 正常列 -->
   <el-table-column
    v-else
    :key="idx"
    :prop="itm.prop "
    :label="itm.label "
    :width="itm.width "
    :sortable="itm.sortable "
    :align="itm.align "
    :fixed="itm.fixed "
    :formatter="itm.formatter"
    :show-overflow-tooltip="itm.tooltip"
    min-width="50"
   />
  </template>
 </el-table>
</template>

<script>
// 自定义内容的组件
var exSlot = {
 functional: true,
 props: {
  row: Object,
  render: Function,
  index: Number,
  column: {
   type: Object,
   default: null
  }
 },
 render: (h, context) => {
  const params = {
   row: context.props.row,
   index: context.props.index
  };
  if (context.props.column) params.column = context.props.column;
  return context.props.render(h, params);
 }
};

export default {
 components: { exSlot },
 props: {
  tableList: {
   type: Array,
   default: () => []
  },
  header: {
   type: Array,
   default: () => []
  },
  select: {
   type: Boolean,
   default: () => false
  },
  height: {
   type: [Number, String, Function],
   default: () => null
  }
 },
 data() {
  return {
   inTableHeight: null
  };
 },
 created() {
  //该阶段可以接收父组件的传递参数
  this.inTableHeight = this.height;
 },
 mounted() {
  this.$nextTick(() => {
   //表格高度自适应浏览器大小
   this.changeTableHight();
   if (!this.height) {
    window.onresize = () => {
     this.changeTableHight();
    };
   }
  });
 },
 destroyed() {
  //高度自适应事件注销
  window.onresize = null;
 },
 watch: {
  /**
   * 数据变化后 高度自适应
   */
  tableList() {
   this.$nextTick(() => {
    this.changeTableHight();
   });
  }
 },
 methods: {
  /**
   * 选择框选择后更改,事件分发
   */
  selectionChange(selection) {
   this.$emit("selection-change", selection);
  },
  /**
   * 点击事件
   */
  rowClick(row, column, event) {
   this.$emit("row-click", row, column, event);
  },
  /**
   * 高度自适应
   * 当表格展示空间小于460按460px展示,大于的时候高度填充
   */
  changeTableHight() {
   if (this.height) {
    //如果有传进来高度就取消自适应
    this.inTableHeight = this.height;
    this.$refs.table.doLayout();
    return;
   }
   let tableHeight = window.innerHeight || document.body.clientHeight;
   //高度设置
   let disTop = this.$refs.table.$el;
   //如果表格上方有元素则减去这些高度适应窗口,66是底下留白部分
   tableHeight -= disTop.offsetTop + 66;
   if (disTop.offsetParent) tableHeight -= disTop.offsetParent.offsetTop;
   this.inTableHeight = tableHeight < 460 "htmlcode">
<template>
 <div class="hello">
  <xd-table :table-list="tableData" :header="header" height="300"></xd-table>
 </div>
</template>

<script>
export default {
 name: "HelloWorld",
 data() {
  return {
   header: [
    { prop: "w", label: "w" },
    { prop: "x", label: "x",
     formatter: (row) => {
      return row.x.toFixed(3);
     },
    },
    { prop: "d", label: "d",
     formatter: (row) => {
      return row.d.toFixed(2);
     },
    },
    {
     label: "操作",
     render: (h, data) => {
      return (
       <el-button
        type="primary"
        onClick={() => {
         this.handleClick(data.row);
        }}
       >
        点我获取行数据
       </el-button>
      );
     },
    },
   ],
   tableData: [
    { w: 1, x: 99.25123, d: 0.23892 },
    { w: 1, x: 255.6666, d: 0.99134 },
   ],
  };
 },
 methods: {
  handleClick(row) {
   console.log(row);
  },
 },
};
</script>

引用组件之前一定要记得先注册,这里我只使用了几个属性,其他属性没有使用,因为是demo,主要还是展示render内嵌代码的方法,还有一个就是官方formatter方法的使用。
有一个需要注意点就是render内我使用了JSX模板语法这里需要在VUE项目中单独去配置一下JSX语法,如果不想使用JSX,直接写也可以,因为不使用JSX语法写出来的内嵌模板代码比较难读所以我就不展示了,个人建议还是使用JSX语法,虽然和原生vue有些地方使用方法不太一样。

效果截图

element el-table表格的二次封装实现(附表格高度自适应)

结语

这次封装vue组件,花了我将近半个月的时间,从刚开始的接触到发现bug然后去修改,来来回回终于精简封装出现在这个二次封装的组件,可能对于熟悉vue和element的人来说这个封装其实很简单,但是对于我来说这个算是我这段实习期封装的比较好的一个组件了吧,当然除了封装这个组件还有别的事在做,不可能放着公司的活不干(哈哈哈哈︿( ̄︶ ̄)︿),总之在封装组件过程中学习到了不少东西,也算是一个大大的进步,于是来写一篇博客来记录一下