实现element-ui表格组件下拉加载更多

801 阅读1分钟

概述

后台管理系统当中,最常见的莫过于表格,当出现大量数据的时候,一般采用分页加载的方式,更多的是表格组件结合分页组件实现分页加载数据,最近项目需求要根据表格高度,下拉到最底部然后加载更多数据,类似于手机app当中的我们常见的下拉触底加载数据的方式,本质上思路一致。

实现

基本原理

根据相关dom属性,总结出滚动条触底的一般公式为:

//target为滚动父级元素
target.scrollHeight === parseInt(target.clientHeight + target.scrollTop + '')

基本结构

App.vue

<template>

<div class='app'>
  <el-table
        v-loadMore="{
          loadFn: loadMore,
          isLock: isLock
        }"
        :height="400"
        :data="tableData"
        style="width: 100%"
      >
        <el-table-column prop="date" label="日期" width="180" show-overflow-tooltip> </el-table-column>
        <el-table-column prop="name" label="姓名" width="180" show-overflow-tooltip> </el-table-column>
        <el-table-column prop="address" label="地址" show-overflow-tooltip> </el-table-column>
        <el-table-column prop="date" label="日期" width="180" show-overflow-tooltip> </el-table-column>
        <el-table-column prop="name" label="姓名" width="180" show-overflow-tooltip> </el-table-column>
        <el-table-column prop="address" label="地址" show-overflow-tooltip> </el-table-column>
        <template #append v-if="tableData.length">
          <div class="append">
            <div class="load-more" v-if="hasMore">
              <analyze-icon v-if="!loading" class="icon" value="load-more" />
              <i class="el-icon-loading icon" v-else></i>
              <span class="tips">{{ loading ? '表格数据加载中…' : '加载更多' }} </span>
            </div>
            <div class="no-more" v-else>
              <el-divider class="divider">已加载全部</el-divider>
            </div>
          </div>
        </template>
      </el-table>
</div>
</template>
<script>
    export default {
    data(){
        return {
          requestStatus : 'FINISH',
          currPage :1,
          hasMore:false,
          tableData : [
            {
              date: '2016-05-02',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1518 弄'
            },
            {
              date: '2016-05-04',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1517 弄'
            },
            {
              date: '2016-05-01',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1519 弄'
            },
            {
              date: '2016-05-03',
              name: '王小虎',
              address: '上海市普陀区金沙江路 1516 弄'
            }
              ];
        }
       },
       computed:{
          isLock() {
            return this.requestStatus === 'PENDING';
          },
          loading() {
            return this.requestStatus === 'PENDING';
          }
       },
         created() {
              this.tableData = new Array(40).fill(0).map((item) => {
                return {
                  date: '2016-05-02',
                  name: '王小虎',
                  address: '上海市普陀区金沙江路 1518 弄'
                };
              });
              console.log(this.tableData);
      },
       methods:{
         loadMore() {
            if (!this.hasMore) return;
            this.currPage++;
            this.getTableList();
          },
         getTableList() {
            this.requestStatus ='PENDING';
            getAnalyzeTableList({
              id: this.$route.query.id as string,
              bodyParams: {
                currPage: this.currPage,
                pageSize: 20,
              }
            })
              .then((res) => {
               this.hasMore = res.currPage < res.totalPage;
                this.requestStatus = 'FINISH';
              })
              .catch(() => {
                this.requestStatus ='FAILED';
              });
          },
       }
    }
</script>

全局指令

import Vue from 'vue';
const loadMoreOption = {
  bind(el: any, binding: any, vnode: any) {
    const selectWrap = el.querySelector('.el-table__body-wrapper');
    selectWrap && selectWrap.addEventListener('scroll', handleScroll);
    function handleScroll(e: MouseEvent) {
      const target: HTMLElement = e.target as HTMLElement;
      if (target.scrollHeight === parseInt(target.clientHeight + target.scrollTop + '')) {
        binding?.value.loadFn();
      }
    }
    vnode.componentInstance._handleScroll = handleScroll;
    vnode.componentInstance._scrollWrap = selectWrap;
  },
  update(el: any, binding: any, vnode: any) {
    // 组件数据更新所做的逻辑为优化频繁下拉触底请求数据的情况发生,上次结果出来后,才能发送下次请求
    const handleScrollFn = vnode.componentInstance._handleScroll;
    const selectWrap = vnode.componentInstance._scrollWrap;
    if (binding.value.isLock) {
      selectWrap.removeEventListener('scroll', handleScrollFn);
    } else {
      selectWrap.addEventListener('scroll', handleScrollFn);
    }
  }
};
Vue.directive('loadMore', loadMoreOption);