vue分页器实现

613 阅读1分钟

模仿elementUI,使用方式在最下面

<!--分页器代码-->
<template>
  <div class="fr page">
    <div class="pagination">
      <button
          :disabled="myCurrentPage <= 1"
          :class="{cursor: myCurrentPage <= 1 }"
          @click="setCurrentPage(myCurrentPage - 1)"
      >
        上一页
      </button>
      <button :class="{ active: myCurrentPage === 1 }" @click="setCurrentPage(1)">
        1
      </button>
      <button v-show="startEnd.start > 2" @click="setCurrentPage(startEnd.start)">...</button>
      <button
          ref="btn"
          v-for="item in mapBtnsCount"
          :key="item"
          @click="setCurrentPage(startEnd.start + item - 1)"
          :class="{ active: myCurrentPage === startEnd.start + item - 1 }"
      >
        {{ startEnd.start + item - 1 }}
      </button>
      <button
          v-show="startEnd.end < totalPages - 1"
          @click="setCurrentPage(startEnd.end)"
      >...
      </button>
      <button
          :class="{ active: myCurrentPage === totalPages }"
          v-show="totalPages > 1"
          @click="setCurrentPage(totalPages)"
      >
        {{ totalPages }}
      </button>
      <button
          :disabled="myCurrentPage >= totalPages"
          @click="setCurrentPage(myCurrentPage + 1) "
          :class="{cursor: myCurrentPage >= totalPages}"
      >
        下一页
      </button>
      <span class="goPage">
        前往
        <div>
          <input type="number" v-model.lazy.number="goPageInput">
        </div>
      </span>
      <!--  选择一页显示几条数据    -->
      <select v-model="countData" class="select">
        <option v-for="list of pageSizes" :key="list" :value="list">{{ list }}条/页</option>
      </select>
      <span class="total">共{{ total }}条</span>
    </div>
  </div>
</template>

<script
export default {
  name: "Pagination",
  // 声明数据类型并接收
  props: {
    // 当前页码
    currentPage: {
      type: Number,
      default: 1,
    },
    // 要显示页码的数字 一般 7 就行了 奇数
    pagerCount: {
      type: Number,
      validator(val) {
        // 验证,验证通过才会有用
        // 大于等于 5 且小于等于 21 的奇数
        // 返回true验证成功,
        // 返回false验证失败
        return val >= 5 && val <= 21 && val % 2 === 1;
      },
      default: 7  
    },
    // 每页显示多少条数据
    pageSize: {
      type: Number,
      default: 5
    },
    // 更改每页显示几条数据
    pageSizes: {
      type: Array,
      default() {
        return [5, 10, 15, 20]
      }
    },
    // 总页数
    total: {
      type: Number,
      // required: true
      default: 0
    }
  },
  data() {
    return {
      // 为了方便修改myCurrentPage,定义data数据  响应式数据
      // 原因:props数据只读不能修改
      myCurrentPage: this.currentPage
      // goPage: 1,
    }
  },
  // 侦听器 监听某一个属性的值
  watch: {
    // 用于调用父组件传递的事件 修改总页数
    myCurrentPage(currentPage) {
      // this.$listeners['current-change'](currentPage)
      // 执行自定义事件
      this.$emit("currentChange", currentPage)
    },
    // 更新页码
    currentPage(currentPage) {
      this.myCurrentPage = currentPage
    },
  },
  computed: {
    // 修改一页显示多少条数据
    countData: {
      get() {
        // 父组件传递的页码
        return this.pageSize
      },
      set(val) {
        // 更新页码
        this.$emit('currentPageSize', val)
      }
    },
    // 总页数
    totalPages() {
      // 向上取整 即小数部分直接舍去,并向正数部分进1 用总数据条数 / 每页数量
      return Math.ceil(this.total / this.pageSize)
    },
    // 计算中间按钮的开始和结束的按钮值
    startEnd() {
      const {myCurrentPage, pagerCount, totalPages} = this
      // 中间start-end总计的按钮数量(不包含开头和结尾)
      const count = pagerCount - 2;
      // 中间的一半 向下取整  即小数部分直接舍去
      const halfCount = Math.floor(count / 2)
      let start, end;
      /*
       start 开头
         1. start = myCurrentPage - halfCount
           1...3 4 [5] 6 7...10
              3   =       5       -    2
           问题:
             1 [2] 3 4 5 6 ...10
              0    =      2        -    2
           解决:修正start的值,不能小于1
           问题:
             1 ... 5 6 7 8 [9] 10
             7  =   9  - 2

       end 结尾
         2. end = start + count - 1
           1...3 4 [5] 6 7...10
              7   =  3  +  5 - 1

        1 [2] 3 4 5 6 ...10
        1...3 4 [5] 6 7...10
        1 ... 5 6 7 8 [9] 10
        1 [2] 3
        [1] --> 如果start大于总页数,不显示
        */

      if (myCurrentPage >= totalPages - halfCount) {
        //1 ... 5 6 7 8 [9] 10
        start = totalPages - count
      } else {
        // 正常情况
        start = myCurrentPage - halfCount
      }

      if (start <= 1) {
        start = 2
      }

      // 正常情况
      end = start + count - 1;
      if (end >= totalPages) {
        // 1 [2] 3
        end = totalPages - 1
      }

      // 返回计算结果
      return {
        start,
        end
      }
    },
    // 跳转页码输入框的值
    goPageInput: {
      get() {
        return this.myCurrentPage
      },
      set(value) {
        // 判断输入的值,边界值
        if (value < 1) {
          value = 1
        } else if (value > this.totalPages) {
          value = this.totalPages
        }
        this.myCurrentPage = parseInt(value)
        // 更新页码
        this.setCurrentPage(value)
      }
    },
    // 总共要遍历的按钮数量
    mapBtnsCount() {
      // 解构赋值
      const {start, end} = this.startEnd
      // 用最终的页码 - 最小的页码 + 1
      const count = end - start + 1
      return count >= 1 ? count : 0
    }
  },
  methods: {
    // 更新页码
    setCurrentPage(currentPage) {
      this.goPage = currentPage
      this.myCurrentPage = currentPage
      // 返回顶部参照京东
      window.scrollTo(0, 0)
    }
  }
}
</script>

<style lang="less" scoped>
// 超过设定最大边界 结束与开始把鼠标样式更改
.cursor {
  cursor: not-allowed !important;
}

//选择一页显示多少条数据的样式
.select {
  background-color: #FFF;
  border: 1px solid #DCDFE6;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  color: #606266;
  display: inline-block;
  height: 28px;
  line-height: 28px;
  outline: 0;
  padding: 0 5px;
  -webkit-transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
  transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
  border-radius: 3px;
  font-size: 13px;
}

.total {
  display: inline-block;
  font-size: 13px;
  min-width: 35.5px;
  height: 28px;
  line-height: 28px;
  vertical-align: top;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  margin-left: 10px;
  font-weight: 400;
  color: #606266;
}

.page {
  width: 100%;
  height: 66px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
}

.pagination {
  display: flex;
  white-space: nowrap;
  font-weight: 700;
  align-items: center;

  button {
    margin: 0 5px;
    padding: 0 4px;
    font-size: 13px;
    min-width: 30px;
    height: 28px;
    line-height: 28px;
    cursor: pointer;
    box-sizing: border-box;
    text-align: center;
    border: none;
    outline: none;
    display: block;
    background-color: #f4f4f5;
    color: #606266;
    border-radius: 2px;
  }
}

// 去除number类型表单的上下箭头
input::-webkit-inner-spin-button {
  -moz-appearance: textfield;
  -webkit-appearance: none !important;
}

.goPage {
  display: inline-block;
  font-size: 13px;
  min-width: 35.5px;
  height: 28px;
  line-height: 28px;
  margin-right: 10px;
  vertical-align: top;
  box-sizing: border-box;

  div {
    position: relative;
    font-size: 14px;
    display: inline-block;
    width: 50px;
    line-height: 18px;
    padding: 0 2px;
    height: 28px;
    text-align: center;
    margin: 0 2px;
    box-sizing: border-box;
    border-radius: 3px;

    input {
      text-align: center;
      line-height: normal;
      -webkit-appearance: none;
      background-color: #fff;
      background-image: none;
      border-radius: 4px;
      border: 1px solid #dcdfe6;
      box-sizing: border-box;
      color: #606266;
      display: inline-block;
      font-size: inherit;
      height: 28px;
      line-height: 28px;
      outline: none;
      transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
      width: 100%;

    }
  }
}

button.active {
  background-color: #409eff;
  color: #fff;
}
</style>

使用方式

<div>
/*
 <Pagination
     ref="pagination"   // 用于父组件直接修改该组件的值
     @currentPageSize="currentPageSize"  // 更新一页显示多少条数据
     @currentChange="currentChange"  // 更新页码
     :currentPage="options.pageNo"  // 当前页码
     :pageSize="options.pageSize"  // 每页数量
     :pagerCount="7" // 默认7 || 奇数 不小于5 一般7就行
     :pageSizes="[5,10,15,20]"  // 用于修改每页显示几条数据
     :total="total"  // 数据总数
   />
*/
  <Pagination
    ref="pagination"
    @currentPageSize="currentPageSize"
    @currentChange="currentChange"
    :currentPage="options.pageNo"
    :pageSize="options.pageSize"
    :pagerCount="7"
    :pageSizes="[5,10,15,20]"
    :total="total" />
</div>

<script >
export default {
  data() {
    return {
      // 分页器初始值
      options: {
        pageNo: 1, // 页码
        pageSize: 5 // 每页数量
      },
      total:0  // 总数量
    }
  },
  methods:{
    // 获取数据的请求
    async getOrderLists() {
      let {pageNo, pageSize} = this.options
      // 获取新数据请求
      let {code, data, message} = await this.$API.center.getOrderLists(pageNo, pageSize)
      if (code !== 200) {
        this.$message.error(message || '获取订单失败')
        return
      }
      // 重新赋值商品数量总数
      this.total = data.total
      // 提交的商品订单
      this.orderList = data.records
    },
    // 更新页码
    currentChange(pageNo) {
      // 更新页码
      this.options.pageNo = pageNo
      // 更新数据
      this.getOrderLists()
    },
    // 更新一页显示几条数据  内部逻辑可自行更改
    currentPageSize(pageSize) {
      this.options.pageSize = pageSize
      // 向上取整 即小数部分直接舍去,并向正数部分进1   用总数据条数 / 每页数量
      let pageNo = Math.ceil(this.total / this.options.pageSize)
      // 修改每页显示几条数据后再次修改默认的页码会自动调用获取数据请求
      // 判断新(页码)结束值 >= 旧的结束值(页码)
      if (pageNo >= this.options.pageNo) {
        pageNo = this.options.pageNo
      }
      // 判断新(页码)结束值 === 旧的结束值(页码)
      if (pageNo === this.options.pageNo) {
        // 调用获取数据请求
        this.getOrderLists()
      }
      // 更新页码
      this.options.pageNo = pageNo
      // 更新分页器组件的输入框值
      this.$refs.pagination.goPageInput = pageNo
      // 把page赋值为空
      pageNo = null
    },
  }
}
</script>