在公司实习使用vue+element-ui框架进行前端开发,使用表格el-table较为多,有些业务逻辑比较相似,有些地方使用的重复性高,如果多个页面使用相同的功能,就要多次重复写逻辑上差不多的代码,所以打算对表格这个组件进行封装,将相同的代码和逻辑封装在一起,把不同的业务逻辑抽离出来。话不多说,下面就来实现一下吧。
一、原生el-tbale代码——简单の封装这里直接引用官方的基础使用模板,直接抄过来(✪ω✪),下面代码中主要是抽离html部分,可以看出每个el-table-column中都含有prop、label、width属性,只不过这些属性值不太一样罢了,其余的部分都差不多一样,所以表头(表格每列el-table-column的定义)这里可以封装一下,把不同的地方封装成一个数组对象结构,然后通过for循环来完成html中的部分。
封装前
<template> <el-table :data="tableData"> <el-table-column prop="date" label="日期"> </el-table-column> <el-table-column prop="name" label="姓名"> </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>
表格の样子
封装后
<template> <el-table :data="tableData"> <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" /> <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>封装代码的相关解释