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


Posted in Javascript onJanuary 19, 2021

前言

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

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

这里直接引用官方的基础使用模板,直接抄过来(✪ω✪),下面代码中主要是抽离html部分,可以看出每个el-table-column中都含有prop、label、width属性,只不过这些属性值不太一样罢了,其余的部分都差不多一样,所以表头(表格每列el-table-column的定义)这里可以封装一下,把不同的地方封装成一个数组对象结构,然后通过for循环来完成html中的部分。

封装前

<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 ? itm.prop : null"
    :label="itm.label ? itm.label : null"
    :width="itm.width ? itm.width : null"
   >
   </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代码中去“开刀”( ̄? ̄)/。上面是最最最简单的封装了,严格来说只是简单的抽离了一下代码中的数据结构,在正常的业务中肯定不止这么简单的封装,接下来才是重点─━ _ ─━✧

二、el-tbale代码——复杂の封装

在真正的开发过程中,表格不仅仅要展示数据,还要完成一些额外的任务,比如CRUD(增删改查操作)和数据格式转化,表格内每一条数据都有可能被单独修改或者执行一些功能性的交互,这时候就要在单元格内内嵌一些按钮、输入框、标签等等的代码,element官方给出的方法是使用插槽slot,获取对应行的数据使用slot-scope,在对应的列中设置相应的代码,但是这里给我们二次封装就会带来不小的问题,如果只是单纯的修改数据的格式使用官方提供的formatter属性还可以实现,但是要内嵌代码就会比较麻烦,内嵌代码必然就会带来封装上的困难,这也是我在封装代码的时候遇到的最大的阻碍,如果要想封装好这个表格,就必须将这部分代码抽离出组件外,在查询阅读了大量博客之后(其实是我菜了,学艺不精(T?T)),我终于找到了将内嵌代码剥离出组件的方法?(๑╹◡╹)ノ",那就是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 ? itm.prop : null"
    :label="itm.label ? itm.label : null"
    :width="itm.width ? itm.width : null"
    :sortable="itm.sortable ? itm.sortable : false"
    :align="itm.align ? itm.align : 'center'"
    :fixed="itm.fixed ? itm.fixed : null"
    :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 ? itm.prop : null"
    :label="itm.label ? itm.label : null"
    :width="itm.width ? itm.width : null"
    :sortable="itm.sortable ? itm.sortable : false"
    :align="itm.align ? itm.align : 'center'"
    :fixed="itm.fixed ? itm.fixed : null"
    :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 ? 460 : tableHeight;
   //重绘表格
   this.$refs.table.doLayout();
  }
 }
};
</script>
<style></style>

封装代码的相关解释

以上就是我封装的代码,部分属性或者方法由于没有使用到所以我就没有将对应的方法和属性封装进去,如果你们开发中有用到对应的地方其实可以照猫画虎的填上去即可,我封装表格的时候在属性这里使用了三目运算符,用于做一些兼容,如果不传对应的属性就给个默认值,比如align属性,我设置的是默认居中。还有就是方法,在表格的方法引用方面,其实就是把官方的方法用$emit事件将对应的参数和方法名用同样的方法分发给父组件,这样父组件使用完全可以参照element官方文档使用这些方法,在组件内我只是进行了一次转发而已,我自己写的时候并没有用到太多的方法,所以只封装了一两个,如果有需要可以自行添加。除了上述两个封装,有一个特别的地方就是勾选框,不能放在循环内,不然会出现错误,可能是索引的问题吧,所以我单独使用一个参数来控制是否显示选择框。另外就是,在公司产品要求表格能够自适应页面的高度,这个功能我也是修改了好久,460是最小的高度,关于高度自适应的全部在changeTableHight()方法中,如果不需要这个功能,将函数和所有引用该函数的地方删除即可。

height:如果不传入这个属性,那么表格高度就如上面所说的是自适应高度,可以通过这个属性来指定表格的高度。
formatter:这个属性在列中如果使用插槽就会失效,所以我设置了两个列,如果有render方法说明单元格要内嵌代码,就是用特殊列,反之就是正常列,所以formatter和render不能同时使用。

render:终于到了最关键的地方了( ̄? ̄)/,这个可是我封装表格的最大难点了,render对我个人理解而言就是虚拟结点,在DOM和CSSOM树合并为render树的阶段,对代码进行修改。

以上就是我封装表格的详细解释了,可能有遗漏的部分,毕竟封装这个表格也让我学了不少东西,所以之前有些地方可能解释不清楚或者不到位,还望各位大佬指正。

三、父组件引用封装的组件

封装这么久的组件,当然要使用起来才知道的到底好不好用,关于引用方面,首先要在引用的地方进行组件注册,如果全局注册过了,可以忽略在局部注册,关于组件的注册这里就不做详解了(o゚?゚)o  。我使用了全局注册,所以这里是直接引入我自己封装好的组件。

<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的人来说这个封装其实很简单,但是对于我来说这个算是我这段实习期封装的比较好的一个组件了吧,当然除了封装这个组件还有别的事在做,不可能放着公司的活不干(哈哈哈哈︿( ̄︶ ̄)︿),总之在封装组件过程中学习到了不少东西,也算是一个大大的进步,于是来写一篇博客来记录一下

到此这篇关于element el-table表格的二次封装实现(附表格高度自适应)的文章就介绍到这了,更多相关element el-table二次封装内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木! 

Javascript 相关文章推荐
javascript 跳转代码集合
Dec 03 Javascript
IE的事件传递-event.cancelBubble示例介绍
Jan 12 Javascript
JS自调用匿名函数具体实现
Feb 11 Javascript
jQuery使用height()获取高度需要注意的地方
Dec 13 Javascript
基于jQuery实现仿淘宝套餐选择插件
Mar 04 Javascript
jQuery 遍历map()方法详解
Nov 04 Javascript
基于jQuery实现一个marquee无缝滚动的插件
Mar 09 Javascript
yii form 表单提交之前JS在提交按钮的验证方法
Mar 15 Javascript
node.js连接MongoDB数据库的2种方法教程
May 17 Javascript
微信jssdk逻辑在vue中的运用详解
Nov 14 Javascript
Vue+abp微信扫码登录的实现代码示例
Jan 06 Javascript
vue中h5端打开app(判断是安卓还是苹果)
Feb 26 Vue.js
JS实现纸牌发牌动画
Jan 19 #Javascript
微信小程序canvas实现签名功能
Jan 19 #Javascript
vue二选一tab栏切换新做法实现
Jan 19 #Vue.js
微信小程序选择图片控件
Jan 19 #Javascript
jQuery冲突问题解决方法
Jan 19 #jQuery
js实现随机点名
Jan 19 #Javascript
js实现有趣的倒计时效果
Jan 19 #Javascript
You might like
基于PHP Web开发MVC框架的Smarty使用说明
2013/04/19 PHP
php自定义函数截取汉字长度
2014/05/15 PHP
PHP PDOStatement::bindColumn讲解
2019/01/30 PHP
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
理解javascript回调函数
2014/12/28 Javascript
JQuery radio(单选按钮)操作方法汇总
2015/04/15 Javascript
javascript实现在网页任意处点左键弹出隐藏菜单的方法
2015/05/13 Javascript
jQuery实现非常实用漂亮的select下拉菜单选择效果
2015/11/06 Javascript
JavaScript类的写法
2016/09/17 Javascript
基于JavaScript实现屏幕滚动效果
2017/01/18 Javascript
React简单介绍
2017/05/24 Javascript
javascript基础进阶_深入剖析执行环境及作用域链
2017/09/05 Javascript
详解VUE自定义组件中用.sync修饰符与v-model的区别
2018/06/26 Javascript
如何将HTML字符转换为DOM节点并动态添加到文档中详解
2018/08/19 Javascript
vue路由中前进后退的一些事儿
2019/05/18 Javascript
ES6 Object.assign()的用法及其使用
2020/01/18 Javascript
[01:45]2014DOTA2 TI预选赛预选赛 大神专访第二弹!
2014/05/20 DOTA
[38:23]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第一场
2014/05/24 DOTA
python dict 字典 以及 赋值 引用的一些实例(详解)
2017/01/20 Python
利用Django内置的认证视图实现用户密码重置功能详解
2017/11/24 Python
python pycurl验证basic和digest认证的方法
2018/05/02 Python
一文带你了解Python中的字符串是什么
2018/11/20 Python
python 还原梯度下降算法实现一维线性回归
2020/10/22 Python
python利用后缀表达式实现计算器功能
2021/02/22 Python
美国最好的保健品打折网店:Swanson
2017/08/04 全球购物
Shop Apotheke瑞士:您的健康与美容网上商店
2019/10/09 全球购物
自荐书模板
2013/12/19 职场文书
文秘专业个人求职信
2013/12/22 职场文书
经典演讲稿汇总
2014/05/19 职场文书
2014年师德师风工作总结
2014/11/25 职场文书
实习介绍信范文
2015/05/05 职场文书
2015年市场营销工作总结
2015/07/23 职场文书
学雷锋感言
2015/08/03 职场文书
PHP基本语法
2021/03/31 PHP
Go 实现英尺和米的简单单位换算方式
2021/04/29 Golang
Python 恐龙跑跑小游戏实现流程
2022/02/15 Python