实现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发现我这种方式还存在问题或者有其他更便捷的方法,欢迎评论区交流!

相关推荐
鹧鸪yy6 分钟前
认识Node.js及其与 Nginx 前端项目区别
前端·nginx·node.js
跟橙姐学代码7 分钟前
学Python必须迈过的一道坎:类和对象到底是什么鬼?
前端·python
汪子熙9 分钟前
浏览器里出现 .angular/cache/19.2.6/abap_test/vite/deps 路径究竟说明了什么
前端·javascript·面试
Benzenene!10 分钟前
让Chrome信任自签名证书
前端·chrome
yangholmes888810 分钟前
如何在 web 应用中使用 GDAL (二)
前端·webassembly
jacy12 分钟前
图片大图预览就该这样做
前端
林太白14 分钟前
Nuxt3 功能篇
前端·javascript·后端
YuJie16 分钟前
webSocket Manager
前端·javascript
Mapmost31 分钟前
Mapmost SDK for UE5 内核升级,三维场景渲染效果飙升!
前端
Mapmost34 分钟前
重磅升级丨Mapmost全面兼容3DTiles 1.1,3DGS量测精度跃升至亚米级!
前端·vue.js·three.js