【VUE】element-ui表格 实现滚动到底部加载更多数据

背景

调用某系统的远程接口并不支持分页,但是支持分页查询,也就是说分页查了但是不给你返回total数量,你说这是有多坑!!
商量无果,因此解决办法就是我这个后端工程师去实现滚动加载报表的功能…

问题

在element-ui的文档中,存在这么一个指令v-infinite-scroll,但是实际用下来发现,指令只能作用于当前绑定的元素上 文档

初步实现-DEMO

经过我一番搜索,找到了一篇真的不错的文章:
element-ui表格 实现滚动到底部加载更多
这篇文章基本上可以解决我的问题,但是在实操过程中,会发现作者写的滚动到底部加载,会有自适应问题,因此我这边调整了一下distance的参数就解决了。

我这边整了一个element的demo,可以直接拷贝使用:

<template>
<!-- 滚动加载 -->
    <div class="wrap">
	  	<el-table
			height="600"
		  	:data="visibleData"
		  	style="width: 100%"
			v-load-more.expand="{func: loadmore, target: '.el-table__body-wrapper', delay: 300}"
			:load-more-disabled="disabledLoad">
            <el-table-column
				prop="id"
				label="ID">
		  	</el-table-column>
		 	<el-table-column
				prop="date"
				label="日期">
		  	</el-table-column>
			<div slot="append" style="text-align:center;">{{content}}</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 {
    data () {
		return {
			visibleCount: 30,
			tableData: [],
            content: '滚动到底部加载更多'
		}
	},
    created() {
		let count = 120
		while(count >= 0) {
			count--
			this.tableData.push({
                id: count,
				date: "2016-05-02"
			})
		}
	},
    computed: {
		// 是否禁止无限加载
		disabledLoad() {
			return false
		},
			
		visibleData() {
			return this.tableData.slice(0, Math.min(this.tableData.length, this.visibleCount))
		}
	},
	
	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 = 1, func, delay = 200 } = binding.value
					if (typeof target !== 'string') return
					let targetEl = el.querySelector(target)
                    console.info(targetEl)
					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) {
				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
				}
			}
		}
	},
	
	methods: {
		loadmore () {
			console.log('滚动到底部了')
			this.visibleCount += 10
            if(this.visibleCount >= this.tableData.length){
                this.content = '暂无更多数据'
            }
		}
	}
}
</script>

最终升级版本-表格滚动组件

我在想,这个表格滚动加载实现起来并不轻松,如果可以抽象成一个组件来使用,会更加灵活。
因此在上面demo的基础上我做了一些改动:

  1. 动态加载表格列
  2. 自定义初始化数量
  3. 不在初始化的时候加载全部数据,而是在滚动到底部的时候进行加载

组件代码scrollTable.vue

<!-- 滚动加载表格组件 -->
<template>
    <div class="wrap">
	  	<el-table
			height="600"
		  	:data="visibleData"
		  	style="width: 100%"
			v-load-more.expand="{func: loadmore, target: '.el-table__body-wrapper', delay: 300}"
			:load-more-disabled="disabledLoad">
            <!-- 动态列表格 -->
            <el-table-column :prop="index" :label="item" sortable v-for="(item, index) in tableHeader"
                       :key="index">
            </el-table-column>
            <!-- 底部文案 -->
			<div v-if="visibleData.length>=15" slot="append" style="margin-top:10px; margin-bottom:10px; text-align:center; font-size:15px ">{{content}}</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 {
    props:{
        //表格列信息
        tableHeader: {
            type: Object,
            required: true
        },
        //表格数据 注意需要增量(第一次是30条 第二次为30+10条....)
        tableData: {
            type: Array,
            required: true
        },
        //用于下拉查询新一页的数据信息 入参(页码,页数大小)
        method: { 
            type: Function
        },
        //初始行数(最好不要小于当前页面size,否则滚动下载有失效的可能,且size必须为10的倍数)
        initCount: {
            type: Number,
            required: true
        }
    },
    mounted() {
       
    },
    data () {
		return {
			visibleCount: 30,
            content: '滚动到底部加载更多',
            pageIndex: 1,
            pageSize: 10,
        }    
	},
    created() {
        console.info("创建了...")
        this.visibleCount = this.initCount
        this.pageIndex = this.initCount/this.pageSize
	},
    computed: {
		// 是否禁止无限加载
		disabledLoad() {
			return false
		},
		
		visibleData() {
			return this.tableData.slice(0, Math.min(this.tableData.length, this.visibleCount))
		}
	},
	
	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 = 1, func, delay = 200 } = binding.value
					if (typeof target !== 'string') return
					let 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) {
				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
				}
			}
		}
	},
	
	methods: {
		async loadmore () {
			console.log('滚动到底部了')
            this.pageIndex++
            await this.method(this.pageIndex ,this.pageSize)
            this.visibleCount += 10
            if(this.visibleCount > this.tableData.length){
                this.content = '暂无更多数据'
            }
		},
	}
}
</script>

使用方式:
因为涉及到公司业务代码,因此我这里把业务代码去掉了(伪代码,不能直接copy运行

<template>
  <div>
    <div class="container">
      <el-form :inline="true" :model="query" class="demo-form-inline">
        <!-- 查询条件 -->
        <!----------------->
        <el-form-item>
          <el-button type="primary" @click="handleSearch" :loading="loading">查询</el-button>
          <el-button type="primary" @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      <!-- 动态加载数据 -->
      <scroll-table v-if="hackReset" :tableHeader="tableHeader" :tableData="tableData" :method="scrollMethod" :initCount="initCount"></scroll-table>
    </div>
  </div>
</template>

<script>
import scrollTable from '../share/scrollTable';
export default {
  name: 'StockPage',
  components:{
    scrollTable
  },
  data() {
    return {
      query: {
        info: '',
      },
      //表头
      tableHeader:{
        id: "ID",
        date: "日期",
        page: 1,
        size: 20
      },
      //表数据信息
      tableData:[], //当前查询条件分页累加数据信息
      dataList:[], //分页查询数据信息
      initCount:20, //初始化行数
      hackReset: true,//重新刷新子组件渲染
    }
  },
  created() {
  },
  methods: {
    //查询
    handleSearch() {
      //点击查询,需要重新渲染组件
      this.rebuileComponents()
    },
    //动态加载表数据方法
    async scrollMethod(pageIndex, pageSize){
      this.query.page = pageIndex
      this.query.size = pageSize
      if(this.checkQuery()){
        let result = await findPage(this.query)
        if (result) {
              this.dataList = result.list
              //累加
              if(this.dataList.length>0){
                  let i =0 ;
                  for(i;i< this.dataList.length ;i++){
                      this.tableData.push(this.dataList[i])
                  }
              }
            }
        }
      } 
    }, 
    //校验
    checkQuery(){
      //校验查询条件
      return true;
      
    },
    //强制重新加载子组件
    rebuileComponents() {
      // 销毁子标签
      this.hackReset = false;
      // 重新创建子标签
      this.$nextTick(() => {
        this.hackReset = true;
      });
    },
    //重置
    async handleReset() {
      
    },
}
</script>

<style scoped>

</style>

如果文章对你有帮助,不要忘了给我点个赞吼( ̄▽ ̄)~
欢迎关注我的微信公众号:松鼠技术站

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值