element-ui的el-table表格实现无限滚动,使用自带的infinite-scroll

萧凡
2023-12-01

背景:
需要在el-tablel里实现滚动到底部加载更多数据
问题:使用element自带的无限滚动,但是实际用下来发现,指令只能作用于当前绑定的元素上,如下:

    <ul class="infinite-list" v-infinite-scroll = "load" style="overflow:auto; height: 200px;">
      <li v-for="(item, index) in tableData" :key="index">{{ item.name }}</li>
    </ul>

对于不是真正的出现滚动条的标签,却无能为力

<el-table
   border
   height="400"
   v-infinite-scroll="load"
   :data="tableData"
   >
   <el-table-column prop="date" label="日期" width="180"></el-table-column>
   <el-table-column prop="name" label="姓名" width="150"></el-table-column>
   <el-table-column prop="address" label="地址" width="240"></el-table-column>
 </el-table>

标签不是真正的容器,绑定了指令,无法生效
实现
第一步:判断滚到底部

const { scrollTop, scrollHeight, clientHeight }  = targetEl
if(scrollHeight === scrollTop + clientHeight) {
    console.log('到底部了!')
}

简洁版:

export default {
	name: 'load-more',
	bind(el, binding, vnode) {
		binding.handler = function(){
			const {scrollTop, scrollHeight, clientHeight} = el
			if (scrollHeight === scrollTop + clientHeight) {
				binding.value && binding.value()
			}
		}
		el.addEventListener('scroll', binding.handler)
	},
	unbind(el, binding) {
		el.removeEventListener('scroll', binding.handler)
		el = null
	}
}

把写好的指令在组件内注册后,即可生效。

我们的指令能够支持:

  1. 可指定作用的元素
  2. 是否禁止无限加载
  3. 支持设置滚动到底部的偏移量
  4. 支持防抖

完整代码:

<template>
  <div>
    <el-table
      border
      height="400"
      :data="visibleData"
      :row-key="getRowKeys"
      v-load-more.expand="{func: loadmore, target: '.el-table__body-wrapper', delay: 300}"
      :load-more-disabled="disabledLoad"
      @selection-change="handleSelectionChange"
      >
      <el-table-column type="selection" :reserve-selection="true" width="55"> </el-table-column>
      <el-table-column prop="date" label="日期" width="180"></el-table-column>
      <div slot="append" style="text-align: center;">滚动到底部加载更多</div>
    </el-table>
  </div>
</template>

<script>
const debounce = function (func, delay) {
  let timer = null
  return function () {
      if (timer) clearTimeout(timer)
      timer = null
      let self = this
      let args = arguments
      timer = setTimeout(() => {
          func.apply(self, args)
      }, delay)
    }
  }
export default {
    directives: {
    'load-more': {
      bind (el, binding, vnode) {
          const { expand } = binding.modifiers
          // 使用更丰富的功能,支持父组件的指令作用在指定的子组件上
          if (expand) {
              /**
               * target 目标DOM节点的类名
               * distance 减少触发加载的距离阈值,单位为px
               * func 触发的方法
               * delay 防抖时延,单位为ms
               * load-more-disabled 是否禁用无限加载
               */
              let { target, distance = 0, func, delay = 200 } = binding.value
              if (typeof target !== 'string') return
              let targetEl = el.querySelector(target)
              if (!targetEl) {
                  console.log('找不到容器')
                  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) {
          let { 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
          }
      }
    }
  },
  data() {
    return  {
      visibleCount: 10,
      tableData: [
        {id: '234', date: '2016-06-01'},
        {id: '34', date: '2016-06-01'},
        {id: '24', date: '2016-06-01'},
        {id: '23242', date: '2016-06-01'},
        {id: '23243', date: '2016-06-01'},
        {id: '23244', date: '2016-06-01'},
        {id: '23245', date: '2016-06-01'},
        {id: '23246', date: '2016-06-01'},
        {id: '23247', date: '2016-06-01'},
        {id: '23248', date: '2016-06-01'}
      ],
      multipleSelection: []
    }
  },
  created() {
    let count = 1
    while(count >= 0) {
      count--
      this.tableData.push(
        { id: '1213', date: '2016-05-02' },
        { id: '11123', date: '2016-05-02' },
        { id: '123023', date: '2016-05-02' },
        { id: '1253', date: '2016-05-02' },
        { id: '1243', date: '2016-05-02' },
        { id: '1263', date: '2016-05-02' },
        { id: '1273', date: '2016-05-02' },
        { id: '1283', date: '2016-05-02' },
        { id: '1293', date: '2016-05-02' },
        { id: '1203', date: '2016-05-02' },
        { id: '1323', date: '2016-05-02' },
        { id: '1423', date: '2016-05-02' },
        { id: '1523', date: '2016-05-02' },
        { id: '1623', date: '2016-05-02' },
        { id: '1723', date: '2016-05-02' },
        { id: '1823', date: '2016-05-02' },
        { id: '1923', date: '2016-05-02' },
        { id: '1023', date: '2016-05-02' },
        { id: '12323', date: '2016-05-02' },
        { id: '134323', date: '2016-05-02' }
      )
    }
  },
  computed: {
    disabledLoad() {
      return false
    },
    visibleData() {
      return this.tableData.slice(0, Math.min(this.tableData.length, this.visibleCount))
    }
  },
  methods: {
    loadmore() {
      console.log('滚动到底部了');
      this.visibleCount += 10;
    },
    handleSelectionChange(val) {
      this.multipleSelection = val;
      console.log(this.multipleSelection)
      const ids = this.multipleSelection.map(item => {
        return item.id
      })
      console.log(ids)
    },
    getRowKeys(row) {
      return row.id
    }
  }
}
</script>

注:
el-table里的:row-key="getRowKeys"@selection-change="handleSelectionChange"
配合el-table-column里的:reserve-selection="true"
实现了无限加载的同时记住已选内容

参考文章:
element-ui表格 实现滚动到底部加载更多

 类似资料: