通过属性透传,iview Table组件实现自适应高度

前言

最近产品大大找我一顿吐槽,说我们的后台管理系统表格数据超出屏幕时,都得滑到最下面才能去拉横向滚动条,体验非常不好。

个人觉得键盘左右键也挺好的,要不再忍忍?

行,听产品的

实现思路

由于项目中涉及的页面过多,一个个页面修改肯定不现实。

可以利用vue的属性透传,在不改变原Table组件功能的前提下,包装原组件,新增自适应高度方法,列表页需要的就替换成新组件,其它页则不受影响。

属性透传涉及,属性,方法以及作用域插槽。

ref属性并不支持透传,为了方便原组件事件调用,可以在新组件中,对原组件方法进行代理访问.

实现效果图

具体实现

新建AutoHeightTable组件,进行属性透传

通过Vue提供的h函数方法,进行属性,方法和作用域插槽的透传。

$attrs为属性,$listeners为方法,$scopedSlots保存作用域插槽。

js 复制代码
import { h } from 'vue'

render() {
    return h('Table', {
        ref: 'table',
        props: {
            ...this.$attrs,
            // 传入计算后的最大高度
            height: this.maxHeight
        },
        on: this.$listeners,
        scopedSlots: this.$scopedSlots
    })
}

通过MutationObserver监听dom变化

通过MutationObserver监听表格dom的变化,dom变化后,计算当前页面的剩余空间,并将值赋予height属性。

用户缩小屏幕尺寸时,表格顶部的搜索框会换行,这个时候就需要重新计算表格高度。

由于产品要求默认充满屏幕,滚动条始终在底部,所以使用的height属性,个人觉得maxHeight会更加友好。
由于要保持页面源码的最小改动,默认底部距离bottom给了固定页脚高度80,如需对页脚高度进行计算,可通过传入类名或元素dom的方式进行计算。

js 复制代码
mounted() {
    this.initObServer()
},
beforeDestroy() {
    this.disconnectObserver()
},
methods: {
    initObServer() {
        if (!this.$refs.table) {
            return
        }
        // 通过MutationObserver监听dom变化
        this.observer = new MutationObserver(() => {
            this.getMaxHeight()
        })
        this.observer.observe(this.$refs.table.$el, {
            childList: true
        })
        // 监听尺寸变化
        window.addEventListener('resize', this.getMaxHeight)
    },
    disconnectObserver() {
        // 关闭监听
        if (this.observer) {
            this.observer.disconnect()
            this.observer = null
        }
        window.removeEventListener('resize', this.getMaxHeight)
    },
    // 计算高度
    getMaxHeight() {
        if (!this.$refs.table) {
            return
        }
        const { top } = this.$refs.table.$el.getBoundingClientRect()
        // 屏幕剩余空间 = 屏幕高度 - 表格顶部 - 表格底部
        this.maxHeight = window.innerHeight - top - this.bottom
    }
}

原Table组件方法代理

包装原组件经常会遇到一个问题

父组件通过ref直接调用组件内部的方法,而vue中并没有如react进行ref透传的方法。

父组件调用方法则必须通过refs.selection.refs.table.getSelection()。

html 复制代码
<AutoHeightTable
    border
    :loading="loading"
    :columns="columnsData"
    :data="tableData"
    ref="selection"></AutoHeightTable>
js 复制代码
// 获取表格选中项
this.$refs.selection.$refs.table.getSelection()

为了最小化改动源代码,通过属性代理的方式,来减少ref链的调用。

js 复制代码
// 代理属性
const PROXY_PROPERTIES = [
    'getSelection'
]

beforeCreate() {
    // ref无法透传,对相关属性进行代理
    PROXY_PROPERTIES.forEach(key => {
        Object.defineProperty(this, key, {
            get() {
                return this.$refs.table[key]
            }
        })
    })
}

此时源码中的调用便会正常获取到

js 复制代码
// 获取表格选中项
this.$refs.selection.getSelection()

完整代码

js 复制代码
import { h } from 'vue'

// 代理属性
const PROXY_PROPERTIES = [
    'getSelection'
]

export default {
    name: 'auto-table',
    props: {
        // 底部距离
        bottom: {
            type: Number,
            default: 80
        }
    },
    data() {
        return {
            maxHeight: 0
        }
    },
    beforeCreate() {
        // ref无法透传,对相关属性进行代理
        PROXY_PROPERTIES.forEach(key => {
            Object.defineProperty(this, key, {
                get() {
                    return this.$refs.table[key]
                }
            })
        })
    },
    mounted() {
        this.initObServer()
    },
    beforeDestroy() {
        this.disconnectObserver()
    },
    methods: {
        initObServer() {
            if (!this.$refs.table) {
                return
            }
            this.observer = new MutationObserver(() => {
                this.getMaxHeight()
            })
            this.observer.observe(this.$refs.table.$el, {
                childList: true
            })
            window.addEventListener('resize', this.getMaxHeight)
        },
        disconnectObserver() {
            if (this.observer) {
                this.observer.disconnect()
                this.observer = null
            }
            window.removeEventListener('resize', this.getMaxHeight)
        },
        getMaxHeight() {
            if (!this.$refs.table) {
                return
            }
            const { top } = this.$refs.table.$el.getBoundingClientRect()
            this.maxHeight = window.innerHeight - top - this.bottom
        }
    },
    render() {
        return h('Table', {
            ref: 'table',
            props: {
                ...this.$attrs,
                height: this.maxHeight
            },
            on: this.$listeners,
            scopedSlots: this.$scopedSlots
        })
    }
}
相关推荐
二十雨辰12 分钟前
[JS]面向对象ES6
前端·javascript·ajax
一只理智恩1 小时前
React中的useCallback
前端·javascript·react.js
林强1811 小时前
React Redux使用@reduxjs/toolkit的hooks
前端·javascript·react.js
biass2 小时前
k8s 部署RuoYi-Vue-Plus之mysql搭建
vue.js·mysql·kubernetes
GISer_Jing2 小时前
Javascript常见数据结构和设计模式
开发语言·javascript·数据结构
敲厉害的燕宝2 小时前
【若依前后端分离】通过输入用户编号自动带出部门名称(部门树)
javascript·vue.js·elementui
嘻嘻哈哈172 小时前
vue安装+测试
前端·javascript·vue.js
大学生小郑2 小时前
VUE与React的生命周期对比
前端·vue.js·react.js
JerryXZR2 小时前
Angular基础保姆级教程 - 1
前端·javascript·angular.js
金灰2 小时前
14-Django项目--文件上传-Excel
服务器·前端·javascript