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

相关推荐
星空33 分钟前
Map<String, String>`Map`是接口,不能直接 new
java·前端·算法
开开心心就好44 分钟前
低年级识字练笔顺工具,电脑和安卓端都能用
前端·javascript·网络·安全·scala·erlang·语音识别
杨利杰YJlio1 小时前
ITSK PE 26U5 测试版解读:组件完善、VMD 修复与服务器支持边界
前端·javascript·后端
志尊宝1 小时前
Vue3 零基础每日笔记(021):class 与 style 动态绑定——对象、数组、三元表达式写法汇总
前端·vue.js·笔记·前端框架·html5
夏幻灵1 小时前
Vue / React 列表渲染中的 key:为什么没有 key,Checkbox 可能“对号跑错”?
前端·javascript·vue.js
YWL1 小时前
vue-time-axis-plus进阶:双进度条+跨空白续播+播放指针,打造专业级监控回放体验
前端·javascript·vue·时间轴
mmsx1 小时前
MapLibre 实战 09|Bug 单写着"地图被风刮走了":一个瓦片源工厂,是三个线上事故换来的
android·前端·开源
不羁的木木1 小时前
给鸿蒙 App 增加打开外部网页能力 —— flutter_web_browser 的鸿蒙使用指南
前端·flutter·harmonyos
广州山泉婚姻1 小时前
微服务时代,前后端分离架构该怎样高效协同?
前端·后端
EatFan1 小时前
多租户系统到底怎么设计?从 tenant_id 到 JWT 租户隔离的真实实践
前端·后端·全栈·多租户