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 相关文章推荐
基于jQuery通过jQuery.form.js插件使用ajax提交form表单
Aug 17 Javascript
JS打字效果的动态菜单代码分享
Aug 21 Javascript
你不知道的高性能JAVASCRIPT
Jan 18 Javascript
Vue 2.X的状态管理vuex记录详解
Mar 23 Javascript
BootStrap+Mybatis框架下实现表单提交数据重复验证
Mar 23 Javascript
vue v-model实现自定义样式多选与单选功能
Jul 05 Javascript
Vue Prop属性功能与用法实例详解
Feb 23 Javascript
JavaScript函数式编程(Functional Programming)声明式与命令式实例分析
May 21 Javascript
Vue2.x通用条件搜索组件的封装及应用详解
May 28 Javascript
javascript头像上传代码实例
Sep 28 Javascript
laravel实现中文和英语互相切换的例子
Sep 30 Javascript
详解微信小程序轨迹回放实现及遇到的坑
Feb 02 Javascript
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 var_dump遍历对象属性的函数与应用代码
2010/06/04 PHP
php中Snoopy类用法实例
2015/06/19 PHP
php lcg_value与mt_rand生成0~1随机小数的效果对比分析
2017/04/05 PHP
最佳6款用于移动网站开发的jQuery 图片滑块插件小结
2012/07/20 Javascript
使用jQuery jqPlot插件绘制柱状图
2014/12/18 Javascript
jQuery点缩略图弹出层显示大图片
2015/02/13 Javascript
在JavaScript中处理字符串之fontcolor()方法的使用
2015/06/08 Javascript
JS实现灵巧的下拉导航效果代码
2015/08/25 Javascript
JavaScript常用基础知识强化学习
2015/12/09 Javascript
JS实现随页面滚动显示/隐藏窗口固定位置元素
2016/02/26 Javascript
node.js实现爬虫教程
2020/08/25 Javascript
详谈表单重复提交的三种情况及解决方法
2017/08/16 Javascript
使用vs code开发Nodejs程序的使用方法
2017/09/21 NodeJs
JS动态插入脚本和插入引用外部链接脚本的方法
2018/05/21 Javascript
解决select2在bootstrap modal中不能正常使用的问题
2018/08/09 Javascript
解决Vue+Electron下Vuex的Dispatch没有效果问题
2019/05/20 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
layui多iframe页面控制定时器运行的方法
2019/09/05 Javascript
Javascript中window.name属性详解
2020/11/19 Javascript
利用ctypes提高Python的执行速度
2016/09/09 Python
python去除空格和换行符的实现方法(推荐)
2017/01/04 Python
python 获取文件下所有文件或目录os.walk()的实例
2018/04/23 Python
使用Python的SymPy库解决数学运算问题的方法
2019/03/27 Python
python pandas获取csv指定行 列的操作方法
2019/07/12 Python
python使用minimax算法实现五子棋
2019/07/29 Python
Python中join()函数多种操作代码实例
2020/01/13 Python
python自动脚本的pyautogui入门学习
2020/04/01 Python
python实现交并比IOU教程
2020/04/16 Python
python 服务器运行代码报错ModuleNotFoundError的解决办法
2020/09/16 Python
Python读取图像并显示灰度图的实现
2020/12/01 Python
pandas统计重复值次数的方法实现
2021/02/20 Python
SQL里面如何插入自动增长序列号字段
2012/03/29 面试题
Linux上比较文件的命令都有哪些
2013/09/28 面试题
婚庆司仪主持词
2014/03/15 职场文书
2014年社会实践活动总结范文
2014/04/29 职场文书
保洁员岗位职责
2015/02/04 职场文书