el-pagination 大数据量假分页,不允许直接到最大页数

 业务背景:后端是从es查询的数据,每次返回400条,前端自己分页,每次以一个scrollId去查询下一个400条,这里就设计到如何控制分页,到了临界值时最大分页数应该增加所以采用改变total的方式来控制页码,不能直接跳转到最大的页数,只能慢慢向后点,页数递增

效果图:

ESPagination组件代码: 

<template>
  <div class="pagination-wrapper">
    <div v-if="pageLayout.total" class="pager-count">共 {{virtualpagination.total}} 条</div>
    <pagination
      v-bind="[$props, $attrs]"
      v-on="$listeners"
      :total="virtualpagination.virTotal"
      :pager-count="virtualpagination.pagerCount"
      :page.sync="virtualpagination.page"
      :limit.sync="virtualpagination.size"
      :page-sizes="pageSizes"
      :layout="pageLayout.layout"
      :disabled="isDisabled"
      @pagination="paginationChange" />
  </div>
</template>

<script>
export default {
  props: {
    // 请求数据的函数
    getDataFunc: {
      type: Function,
      required: true
    },
    // 传入上面的参数
    getDataParams: {
      type: Object,
      default: () => {}
    },
    // 每次scrollId获取的数据量
    eachScrollGetCount: {
      type: Number
    },
    // limit 下拉框数据
    pageSizes: {
      type: Array,
      default: () => [10, 20, 30, 40]
    },
    // layout 布局
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper'
    },
    // 显示多少个页码
    pagerCount: {
      type: Number
    },
    // 最大的请求总数,如当数量超过10000时下一页第10001条请求会报错所以做限制
    maxCount: {
      type: Number
    },
    // 是否禁用
    disabled: {
      type: Boolean
    }
  },
  computed: {
    // 当前layout的total作单独处理
    pageLayout() {
      const layout = this.layout.split(',').map(item => item.trim())
      const totalIndx = layout.findIndex(item => item === 'total')
      if (totalIndx !== -1) {
        layout.splice(totalIndx, 1)
      }
      return {
        layout: layout.join(','),
        total: totalIndx !== -1
      }
    },
    // 是否是disabled
    isDisabled() {
      return this.disabled || this.virtualpagination.loading
    }
  },
  data() {
    return {
      virtualpagination: {
        loading: false,
        // curPage
        page: 1,
        // curLimit
        size: this.pageSizes[0] || 10,
        // 假分页的传给组件的total值
        virTotal: 0,
        // 数据返回的真实total,可以用于显示
        total: 0,
        // 每次scroll请求的数据量,如每次scrollId请求400条前端分页处理
        eachScrollGetCount: this.eachScrollGetCount || 400,
        // 每次请求的scrollId,第一次请求为'',后面请求为返回的scrollId
        scrollId: '',
        // 当前分页显示的最大页码数量
        pagerCount: this.pagerCount || 7,
        // 请求回来的所有tableData数据,往后翻页增量的数据追加在后面,当前页数据从这里面截取
        allTableData: [],
        // 当前分页呈现的tableData
        curPageData: [],
        // 分页变动的前一页
        prevPage: 1,
        // 最大的请求总数,如当数量超过10000时下一页第10001条请求会报错所以做限制
        maxCount: this.maxCount
      }
    }
  },
  watch: {
    // 每次数据变动设置table的data
    'virtualpagination.curPageData': function(data) {
      this.$emit('setTableData', data)
    }
  },
  methods: {
    // 分页切换
    paginationChange({ page: curPage, limit }) {
      this.pageChangeIsGetData(curPage, limit)
      this.setVirTotal(curPage, limit)
    },
    // 设置假分页total
    setVirTotal(curPage, limit) {
      const { virtualpagination: { pagerCount, virTotal, maxCount, total: trueTotal }, virtualpagination } = this
      // 当前虚拟的页码总数 = 当前总条数 / 每页数
      const curPageCount = virTotal / limit
      // 分页跨度,如果是7页,那么跨度就是4
      const splitNum = Math.ceil(pagerCount / 2)
      // 最小值边界,小于这个值时需要维持最小页码数
      const minTotalBoundary = limit * pagerCount
      // 点击分页后,通过计算tempTotal来控制当前显示的页码数量,tempTotal 为之前的virtualTotal + (两边的跨度页 - 前总页码数与当前页码数的差值)*分页数
      let temTotal = virtualpagination.virTotal + limit * (splitNum - (curPageCount - curPage))
      // 分页往后走,如从第5页跳到第7页
      if (curPageCount - curPage < splitNum) {
        // console.log('next')
        // 如果当前虚拟总数大于最大边界,以最大边界为准
        if (maxCount && temTotal > maxCount) {
          virtualpagination.virTotal = maxCount
        } else if (temTotal > trueTotal) {
          // 当前虚拟的页大于真实total时 以真实total为准
          virtualpagination.virTotal = trueTotal
        } else {
          virtualpagination.virTotal = temTotal
        }
      }
      // 分页往前走,如从第7页跳到第5页
      if (curPageCount - curPage > splitNum) {
        // console.log('prev')
        // 如果当前页数>5,往前走到小于5的位置,按照上面直接赋值,页面的页码会减小为4,所以做限制
        if (virtualpagination.virTotal >= minTotalBoundary && temTotal < minTotalBoundary) {
          virtualpagination.virTotal = minTotalBoundary
        } else {
          virtualpagination.virTotal = temTotal
        }
      }
      // 当limit change时如从10切为40,维持页码数量
      if (trueTotal > minTotalBoundary && virtualpagination.virTotal < minTotalBoundary) {
        virtualpagination.virTotal = minTotalBoundary
      }
    },
    // 每次分页改变的时候判断是否去请求数据
    async pageChangeIsGetData(curPage, limit) {
      const { virtualpagination: { allTableData, prevPage, eachScrollGetCount }, virtualpagination } = this
      // 根据每次请求的scroll固定数据量大小计算每次请求边界 400, 40页,limit:10
      const eachScrollCountMaxPage = Math.floor(eachScrollGetCount / limit)
      const rangeEnd = eachScrollCountMaxPage * Math.ceil(prevPage / eachScrollCountMaxPage)
      // 目前把数据存入前端就不需要跳转到前面的也去请求数据了, 并且如果是已经请求到很后面了,然后突然回到前面再到临界点时
      // 不应该再去请求数据(eg:curPage: 40, 现在跳到第1页或者往前走,30|31为临界点,到了30页,我再往后走点击31页这个临界值是不应该再次去请求数据的)
      if (curPage > rangeEnd && curPage > prevPage && curPage >= Math.ceil(allTableData.length / limit)) {
        // 往后走的分页,需要请求数据
        await this.getTableList()
      } else {
        // 往前或者在范围内的直接截取数据不用请求
        virtualpagination.curPageData = allTableData.slice((curPage - 1) * limit, curPage * limit)
      }
      // 将现在的的page赋予prevPage
      virtualpagination.prevPage = curPage
    },
    // 请求数据后设置表格数据和total值
    setTableData(data, condition) {
      const { virtualpagination: { allTableData, page: curPage, size: limit, pagerCount, virTotal }, virtualpagination } = this
      // 根据condition 判断是否直接清空数据赋值
      if (condition) {
        // 重新赋值
        virtualpagination.allTableData = data.data
      } else {
        // 追加在所有数据后面
        virtualpagination.allTableData.push(...data.data)
      }
      // scrollId 赋值(其实每次的scrollId都是一样的)
      virtualpagination.scrollId = data.scrollId
      // 当前页数据
      virtualpagination.curPageData = allTableData.slice((curPage - 1) * limit, curPage * limit)
      // 真实分页总数
      const trueTotal = data.totalCounts
      virtualpagination.total = trueTotal

      if (virTotal === 0 || trueTotal < virTotal) {
        // 第一次初始化的时候
        // 初始值为默认limit*pagerCount
        virtualpagination.virTotal = trueTotal <= pagerCount * limit ? trueTotal : pagerCount * limit
      }
    },
    // 获取表格信息
    async getTableList() {
      // 设置loading
      this.$emit('setLoading', true)
      this.virtualpagination.loading = true
      try {
        const { data } = await this.getDataFunc({
          scrollId: this.virtualpagination.scrollId,
          pageSize: this.virtualpagination.eachScrollGetCount,
          ...this.getDataParams
        })
        // 设置数据
        this.setTableData(data)
      } catch (err) {
        console.log(err)
      }
      // 设置loading
      this.$emit('setLoading', false)
      this.virtualpagination.loading = false
    }
  },
  mounted() {
    this.getTableList()
  }
}
</script>

<style lang="scss" scoped>
.pagination-wrapper {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
</style>

使用举例:

<template>
  <div class="box">
    <el-table
      class="detail-table"
      :data="tableData"
      v-loading="loading"
      height="380">
      <el-table-column
        v-for="column in tableColumns"
        :key="column.prop"
        show-overflow-tooltip
        :prop="column.prop"
        :label="column.label" />
    </el-table>
    <es-pagination
      style="padding-top: 20px;"
      :getDataFunc="getTaskPageInfo"
      :getDataParams="params"
      @setTableData="setTableData"
      @setLoading="setLoading" />
  </div>
</template>

<script>
import { getTaskPageInfo } from '@/api/modules/distributeCenter.js'
export default {
  data() {
    return {
      loading: false,
      tableData: [],
      getTaskPageInfo,
      // 请求的带上参数
      params: {
        taskId: this.$route.query.id,
        keyword: this.searchWord
      },
      tableColumns: [
        {
          prop: 'aaa',
          label: '企业aa'
        },
        {
          prop: 'bbb',
          label: '客户bb'
        }
      ]
    }
  },
  methods: {
    // 更新table数据
    setTableData(data) {
      this.tableData = data
    },
    // 请求数据的loading
    setLoading(loading) {
      this.loading = loading
    }
  }
}
</script>

<style lang="scss" scoped>
.detail-table /deep/ .el-table__header .cell{
   white-space:nowrap!important
}
</style>

上面是es数据集群的数据,现在又遇到一个不一样的需求,当前数据又三四十万左右,但是接口分页只能满足查前面 1 万条数据,超过了 1 万就会报错,所以还是采用上面的不能跳转的最大页数,分页页码一点点往后递增,也能支持数据量<1 万时正常分页

PaginationRange 组件:

<template>
  <div class="pagination-wrapper">
    <div v-if="pageLayout.total" class="pager-count">共 {{total}} 条</div>
    <pagination
      v-bind="[$props, $attrs]"
      v-on="$listeners"
      :total="virtualpagination.virTotal"
      :pager-count="virtualpagination.pagerCount"
      :page.sync="virtualpagination.page"
      :limit.sync="virtualpagination.size"
      :page-sizes="pageSizes"
      :layout="pageLayout.layout"
      @pagination="paginationChange" />
  </div>
</template>

<script>
export default {
  props: {
    total: {
      type: Number,
      default: 0
    },
    // limit 下拉框数据
    pageSizes: {
      type: Array,
      default: () => [10, 20, 30, 40]
    },
    // layout 布局
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper'
    },
    // 显示多少个页码
    pagerCount: {
      type: Number
    },
    // 最大的请求总数,如当数量超过10000时下一页第10001条请求会报错所以做限制
    maxCount: {
      type: Number
    }
  },
  computed: {
    // 当前layout的total作单独处理
    pageLayout: function() {
      const layout = this.layout.split(',').map(item => item.trim())
      const totalIndx = layout.findIndex(item => item === 'total')
      if (totalIndx !== -1) {
        layout.splice(totalIndx, 1)
      }
      return {
        layout: layout.join(','),
        total: totalIndx !== -1
      }
    }
  },
  watch: {
    total(val) {
      this.virtualpagination.total = this.total
      this.setTotal()
    }
  },
  data() {
    return {
      virtualpagination: {
        // curPage
        page: 1,
        // curLimit
        size: this.pageSizes[0] || 10,
        // 假分页total
        virTotal: 0,
        // 真实total
        total: this.total || 0,
        // 当前分页显示的最大页码数量
        pagerCount: this.pagerCount || 7,
        // 最大的请求总数,如当数量超过10000时下一页第10001条请求会报错所以做限制
        maxCount: this.maxCount
      }
    }
  },
  methods: {
    paginationChange({ page: curPage, limit }) {
      this.setVirTotal(curPage, limit)
      this.$emit('pagination', { page: curPage, limit })
    },
    // 设置假分页total
    setVirTotal(curPage, limit) {
      const { virtualpagination: { pagerCount, virTotal, maxCount, total: trueTotal }, virtualpagination } = this
      // 当前页码总数 = 当前总条数 / 每页数
      const curPageCount = virTotal / limit
      // 分页跨度,如果是5,那么跨度就是3
      const splitNum = Math.ceil(pagerCount / 2)
      // 最小值边界,小于这个值时需要维持最小页码数
      const minTotalBoundary = limit * pagerCount
      // 点击分页后,通过计算tempTotal来控制当前显示的页码数量,tempTotal 为之前的virtualTotal + (两边的跨度页 - 前总页码数与当前页码数的差值)*分页数
      let temTotal = virtualpagination.virTotal + limit * (splitNum - (curPageCount - curPage))
      // 分页往后走,如从第5页跳到第7页
      if (curPageCount - curPage < splitNum) {
        // console.log('next')
        // 如果当前虚拟总数大于最大边界,以最大边界为准
        if (maxCount && temTotal > maxCount) {
          virtualpagination.virTotal = maxCount
        } else if (temTotal > trueTotal) {
          // 当前虚拟的页大于真实total时 以真实total为准
          virtualpagination.virTotal = trueTotal
        } else {
          virtualpagination.virTotal = temTotal
        }
      }
      // 分页往前走,如从第7页跳到第5页
      if (curPageCount - curPage > splitNum) {
        // console.log('prev')
        // 如果当前页数>5,往前走到小于5的位置,按照上面直接赋值,页面的页码会减小为4,所以做限制
        if (virtualpagination.virTotal >= minTotalBoundary && temTotal < minTotalBoundary) {
          virtualpagination.virTotal = minTotalBoundary
        } else {
          virtualpagination.virTotal = temTotal
        }
      }
      // 当limit change时如从10切为40,维持页码数量
      if (trueTotal > minTotalBoundary && virtualpagination.virTotal < minTotalBoundary) {
        virtualpagination.virTotal = minTotalBoundary
      }
    },
    // 数据请求回来后设置total
    setTotal() {
      const { virtualpagination: { virTotal, pagerCount, size, total }, virtualpagination } = this
      virtualpagination.total = total
      if (virTotal === 0 || total < virTotal) {
        // 第一次初始化的时候
        // 初始值为默认limit*pagerCount
        virtualpagination.virTotal = total <= pagerCount * size ? total : pagerCount * size
      }
    }
  }
}
</script>

<style lang="scss" scoped>
.pagination-wrapper {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
</style>

使用举例: 

<template>
  <div class="box">
    <el-table :data="tableData">...</el-table>
    <pagination-range
      :total="virtualpagination.total"
      :maxCount="virtualpagination.maxCount"
      @pagination="paginationChange" />
  </div>
</template>

<script>
import { getTableList } from '@/api/modules/aa.js'
export default {
  data() {
    return {
      virtualpagination: {
        page: 1,
        size: 10,
        // 真实total
        total: 0,
        // 能够翻页的最大数据范围
        maxCount: 10000
      },
      tableData: []
    }
  },
  methods: {
    // 分页事件
    paginationChange({ page, limit }) {
      this.virtualpagination.page = page
      this.virtualpagination.size = limit
      // 参数是直接在 getList 里面写的,所以把page和size放到virtualpagination上
      this.getData()
    },
    // 获取table列表数据
    async getData() {
      try {
        const { data } = await getTableList()
        // 设置当前tableData
        this.tableData = data.data
        // 设置真实total并传给分页组件
        this.virtualpagination.total = data.totalCounts
      } catch (err) {
        console.log(err)
      }
    }
  },
  mounted() {
    this.getData()
  }
}
</script>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值