实现el-table自适应高度(自定义指令踩坑版)

问题

使用 ElementUI 的 el-table 组件,默认情况下,当每页的数据过多时,浏览器就会撑开产生纵向的滚动条,这导致用户上下滚动查找表格数据时很不方便(滚到顶部看不到分页栏、滚到底部看不到搜索栏)

因此想把表格改成整个页面不产生纵向滚动条,即可视区域内能看到搜索栏+表格数据+分页栏,数据过多时,让表格自适应高度产生滚动条

思路

想实现上述需求的关键就是让表格自适应高度

首先我自己使用过的方法是通过 window.onresize 动态计算高度,然后赋值给 table 的 height 属性,但是这种方法先且不说能完全实现(如果搜索栏是可以展开收起的,这种情况没法监听window.onresize),光是使用就比较繁琐,先要封装计算方法,然后组件里还得写三步,不方便开发者使用。

js 复制代码
export default function() {
  return {
    /**
     * 基于Vue全局的封装 监听table高度变化
     * @param {String} myRef 表格的ref属性,不传默认是'table'。补充:就算默认table,也要确保有ref='table'属性,不然报错
     * @param {Number} customHeight 额外可调整的高度,如页码部分的高度
     */
    tableHeight: (myRef = 'table', customHeight = 0) => {
      this.$nextTick(() => {
        // window.innerHeight 可视区域的高度
        // this.$refs[myRef].$el.getBoundingClientRect().top 该元素到可视区域顶部的距离
        const offsetHeight = this.$refs[myRef].$el.getBoundingClientRect().top
        this.tableHeight = window.innerHeight - offsetHeight - customHeight
        window.onresize = () => {
          this.tableHeight = window.innerHeight - offsetHeight - customHeight
        }
      })
    }
  }
}
js 复制代码
import heightUtils from '@/utils/tableHeight'
Vue.prototype.$heightUtils = heightUtils
js 复制代码
data() {
  return {
    tableHeight: 0
  }
},
mounted() {
  this.$nextTick(() => {
    this.$heightUtils().tableHeight('table', 120)
  })
}

<template> 
  <table :height="tableHeight" />
</template>

以上方法固然不适用了,那么就换一种实现方式,其实网上有很多相关文章,但是我看百分之八十都是cv的,或者也有可能那些作者只考虑了最简单的表格场景,下面介绍我自己实践后的版本。

自定义指令

自定义指令

封装

directive/table/tableHeight.js

js 复制代码
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'

// 设置表格高度
const doResize = (el, binding, vnode) => {
  // 获取表格Dom对象
  const { componentInstance: $table } = vnode
  // 获取调用传递过来的数据
  const { value } = binding
  // 没有直接终止
  if (!$table) return
  // 获取距底部距离(用于展示页码等信息)
  const customHeight = (value && value.customHeight) || 30
  // 计算列表高度并设置
  const height = window.innerHeight - el.getBoundingClientRect().top - customHeight
  $table.layout.setHeight(height)
  $table.doLayout()
}

export default {
  // 初始化设置
  bind(el, binding, vnode) {
    // 设置resize监听方法
    el.resizeListener = () => {
      doResize(el, binding, vnode)
    }
    // 绑定监听方法到addResizeListener
    addResizeListener(window.document.body, el.resizeListener)
  },
  // 所在组件的 VNode 更新时设置
  // 如果搜索栏是可以展开收起的,用此方法更新
  update(el, binding, vnode) {
    doResize(el, binding, vnode)
  },
  // 销毁时设置
  unbind(el) {
    // 移除resize监听
    removeResizeListener(window.document.body, el.resizeListener)
  }
}

directive/index.js

js 复制代码
import tableHeight from './table/tableHeight'

const install = function(Vue) {
  Vue.directive('tableHeight', tableHeight)
}

export default install

main.js

js 复制代码
import directive from './directive'
Vue.use(directive)

使用

js 复制代码
<template>
  <table v-tableHeight="{customHeight: 76}" />
</template>

踩坑

以上代码的实现思路是网上能搜索到的(vue2/vue3都有,因为大体思路是一致的),但是其实是有坑的!

  1. 没有添加 overflow: auto 导致表格不能滚动,可以在table中添加style样式

  2. 表头没有固定,需要额外写样式 ::v-deep .el-table__header-wrapper{position: sticky; top: 0; z-index: 1;}

  3. 对于设置了 fixed 属性的table列,会出现问题,暂无解决方法

出现以上问题我的猜想是: $table.layout.setHeight(height) 只是单单设置了表格的高度,但其实 table 组件中的的 height 属性传进去之后,源码做了其他处理,比如fixed的高度等。

那么是不是可以把这个自适应高度添加到一个div外壳上,让div的高度自适应,然后table添加 height: 100%

完善

directive/table/tableHeight.js

js 复制代码
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event'

// 设置表格高度
const doResize = (el, binding) => {
  // 获取调用传递过来的数据
  const { value } = binding
  // 获取距底部距离(用于展示页码等信息)
  const customHeight = (value && value.customHeight) || 76
  // 计算列表高度
  const height = window.innerHeight - el.getBoundingClientRect().top - customHeight
  // 设置高度
  el.style.height = height + "px"
}

export default {
  // 初始化设置
  bind(el, binding) {
    // 设置resize监听方法
    el.resizeListener = () => {
      doResize(el, binding)
    }
    // 绑定监听方法到addResizeListener
    addResizeListener(window.document.body, el.resizeListener)
  },
  // 所在组件的 VNode 更新时设置
  // 页面上搜索表单是可以展开收起的,当展开更多表单搜索时,表格高度没变
  update(el, binding) {
    doResize(el, binding)
  },
  // 销毁时设置
  unbind(el) {
    // 移除resize监听
    removeResizeListener(window.document.body, el.resizeListener)
  }
}

directive/index.js

js 复制代码
import tableHeight from './table/tableHeight'

const install = function(Vue) {
  Vue.directive('tableHeight', tableHeight)
}

export default install

main.js

js 复制代码
import directive from './directive'
Vue.use(directive)

组件中

js 复制代码
<template>
  <div v-tableHeight="{customHeight: 76}">
    <table height="100%" />
  </div>
</template>

总结

大功告成,在自己项目中测了很多种情况,暂时未发现什么问题,如果jym发现我这种方式还存在问题或者有其他更便捷的方法,欢迎评论区交流!

相关推荐
lUie INGA17 小时前
rust web框架actix和axum比较
前端·人工智能·rust
OPHKVPS18 小时前
VoidStealer新型窃密攻击:首例利用硬件断点绕过Chrome ABE防护,精准窃取v20_master_key
前端·chrome
gechunlian8818 小时前
SpringBoot3+Springdoc:v3api-docs可以访问,html无法访问的解决方法
前端·html
驾驭人生18 小时前
ASP.NET Core 实现 SSE 服务器推送|生产级实战教程(含跨域 / Nginx / 前端完整代码)
服务器·前端·nginx
酉鬼女又兒19 小时前
零基础快速入门前端ES6 核心特性详解:Set 数据结构与对象增强写法(可用于备赛蓝桥杯Web应用开发)
开发语言·前端·javascript·职场和发展·蓝桥杯·es6
慧一居士19 小时前
Vue项目中,子组件调用父组件方法示例,以及如何传值示例,对比使用插槽和不使用插槽区别
前端·vue.js
我是伪码农19 小时前
HTML和CSS复习
前端·css·html
林恒smileZAZ19 小时前
前端实现进度条
前端
前端老石人19 小时前
邂逅前端开发:从基础到实践的全景指南
开发语言·前端·html
阿珊和她的猫20 小时前
以用户为中心的前端性能指标解析
前端·javascript·css