vue2封装通用el-table,实现滚动触底加载

1,550 阅读1分钟

滚动触底加载方案

单页面表格,不涉及多tab不涉及v-if可以使用插件el-table-infinite-scroll(2.0版本为vue2版本,一堆bug)

<template>
        <el-table v-load-more.expand="{ func: loadMore, target: '.el-table__body-wrapper', delay: 300 }" :data="data"
            style="width: 100%" :height="height" header-align="center" v-bind="$attrs">
            <el-table-column type="index" width="50" label="序号" v-if="columnObj.selection">
            </el-table-column>
            <el-table-column :show-overflow-tooltip="columnObj.showToolTip" v-for="(column, columnIndex) in columnData"
                :key="columnIndex" :prop="column.prop" :label="column.label" :width="column.width" :align="columnObj.align">
                <template slot-scope="scope">
                    <!-- 带button的操作栏 -->
                    <div v-if="column.isOperation && buttonGroup">
                        <span v-for="item in buttonGroup">
                            <el-button @click="item.buttonClick(scope.row)" :type="item.type"
                                :style="{ color: item.color }">{{
                                    item.label }}</el-button>
                        </span>
                    </div>
                    <!-- 可跳转/点击 -->
                    <span class="pointer" v-else-if="column.isJump" @click="column.jumpClick(scope.row)">
                        {{ scope.row[column.prop] }}
                    </span>
                    <!-- 时间格式 -->
                    <span v-else-if="column.isDate">
                        {{ parseTime(scope.row[column.prop]) }}
                    </span>
                    <!-- 映射字段 -->
                    <span v-else-if="column.isMapText">
                        {{ scope.row[column.prop] | mapTextFilter(column.list) }}
                    </span>
                    <!-- 自定义项 -->
                    <span v-else-if="columnObj.cusTomFields.includes(column.prop)">
                        <slot :name="column.prop" :row="scope.row"></slot>
                        <!-- 示例  <template slot="status" slot-scope="data"> {{ data.row.status }} </template> -->
                    </span>
                    <!-- 可双击 -->
                    <span class="pointer" v-else-if="column.isDblClick"
                        @dblclick="column.dblclick(scope.row, scope.column, scope.$index)">
                        {{ scope.row[column.prop] }}
                    </span>
                    <!-- 默认显示 -->
                    <span v-else> {{ scope.row[column.prop] }}</span>
                </template>
            </el-table-column>
        </el-table>
</template>

接收参数

 props: {
        data: {
            type: Array,
            default: () => [],
            require: true
        },
        /**
         * @description: 列配置
         * @author: wxy
         * @param {*} selection  false 默认显示序号
         * @param {*} showToolTip true  默认超出显示...
         * @param {*} align true  默认居中
         * @param {*} cusTomFields []  需要自定义的列表项
         * @return {*}
         */
        columnObj: {
            type: Object,
            default: () => {
                return {
                }
            },
            require: true
        },
        height: {
            type: String | Number,
            default: null
        }
    },

映射逻辑

  filters: {
        mapTextFilter(e, list) {
            const obj = list.find(item => {
                return item.value === e
            })
            return obj?.label
        }
    },
    computed: {
        columnData() {
            let { columnData } = this.columnObj
            // 无操作按钮时不显示操作栏
            if (this.buttonGroup.length < 1) {
                columnData = columnData.filter(item => {
                    return item.isOperation !== true
                })
            }
            return columnData
        },
        buttonGroup() {
            // 操作栏 过滤掉无权限的按钮(auth)
            const { columnData } = this.columnObj
            const obj = columnData.find(item => {
                return item.isOperation
            })
            const res = obj?.operation.filter(item => {
                return item.auth !== false
            })
            return res || []
        }
    },

增加默认值

  watch: {
        columnObj: {
            handler (val) {
                if (val.align === undefined) val.align = 'center'
                if (val.selection === undefined) val.selection = false
                if (val.showToolTip === undefined) val.showToolTip = true
                if (val.cusTomFields === undefined) val.cusTomFields = []
            },
            immediate: true
        },
    },

触底加载更多指令注册(亲测比插件好用)

const debounce = function (func, delay) {
        let timer = null
        return function () {
            if (timer) clearTimeout(timer)
            timer = null
            const self = this
            const args = arguments
            timer = setTimeout(() => {
                func.apply(self, args)
            }, delay)
        }
 }
  directives: {
        'load-more': {
            bind(el, binding, vnode) {
                const { expand } = binding.modifiers
                // 使用更丰富的功能,支持父组件的指令作用在指定的子组件上
                if (expand) {
                    /**
                     * target 目标DOM节点的类名
                     * distance 减少触发加载的距离阈值,单位为px
                     * func 触发的方法
                     * delay 防抖时延,单位为ms
                     * load-more-disabled 是否禁用无限加载
                     */
                    const { target, distance = 1, func, delay = 200 } = binding.value
                    if (typeof target !== 'string') return
                    const targetEl = el.querySelector(target)
                    if (!targetEl) {
                        console.log('Container Not Found')
                        return
                    }
                    binding.handler = debounce(function () {
                        const { scrollTop, scrollHeight, clientHeight } = targetEl
                        let disabled = el.getAttribute('load-more-disabled')
                        disabled = vnode[disabled] || disabled

                        if (scrollHeight <= scrollTop + clientHeight + distance) {
                            if (disabled) return
                            func && func()
                        }
                    }, delay)
                    targetEl.addEventListener('scroll', binding.handler)
                } else {
                    binding.handler = helper.debounce(function () {
                        const { scrollTop, scrollHeight, clientHeight } = el
                        if (scrollHeight === scrollTop + clientHeight) {
                            binding.value && binding.value()
                        }
                    }, 200)
                    el.addEventListener('scroll', binding.handler)
                }
            },
            unbind(el, binding) {
                const { arg } = binding
                // 使用更丰富的功能,支持父组件的指令作用在指定的子组件上
                if (arg === 'expand') {
                    /**
                     * target 目标DOM节点的类名
                     * offset 触发加载的距离阈值,单位为px
                     * method 触发的方法
                     * delay 防抖时延,单位为ms
                     */
                    const { target } = binding.value
                    if (typeof target !== 'string') return
                    let targetEl = el.querySelector(target)
                    targetEl && targetEl.removeEventListener('scroll', binding.handler)
                    targetEl = null
                } else {
                    el.removeEventListener('scroll', binding.handler)
                    el = null
                }
            }
        }
    },
   methods: {
        loadMore() {
            let nowTime = new Date().getTime()
            let diffTime = (nowTime - this.lastTime) / 1000
            if (diffTime > 2 && this.isFirstLoad) {
                console.log('执行loadMore''用时间戳避免调用两次');
                this.$emit('loadMore')
            }
            this.lastTime = nowTime
        }
    },