通过属性透传,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
        })
    }
}
相关推荐
hhb_6185 分钟前
TypeScript泛型实战:企业级请求封装全解析
javascript·ubuntu·typescript
BomanGe224 分钟前
NSK直线导轨LH55EL与NH55EM替代指南
前端·javascript·数据库·经验分享·规格说明书
云水一下24 分钟前
Vue.js从零到精通系列(四):前端路由与Vue Router——打造多页单页应用
前端·javascript·vue.js
baozj27 分钟前
把徒步轨迹做成 3D 地形模型:开源工具「印迹 TrailPrint 3D」
前端·vue.js·github
Momo__29 分钟前
alien-signals — 驱动 Vue 3.6 响应式引擎的那个 1KB 库
前端·vue.js·响应式编程
用户831348593069831 分钟前
Vue3+Cesium实现3DTiles模型实时调节(离地面高度/xyz轴旋转/模型经纬度偏移)
vue.js·cesium
zhedream32 分钟前
Vue 3 Teleport 报错实录:从 patch 时机到 `defer` 属性
前端·vue.js
雁北向32 分钟前
自定义指令 数值输入显示优化 巴飞特 测试
前端·vue.js
研☆香34 分钟前
jQuery补充知识点
前端·javascript·jquery
先吃饱再说36 分钟前
JavaScript栈和队列:从“冰柜里的雪糕”到“排队打饭”
javascript·数据结构