实现vue分页page组件

1,638 阅读2分钟

概述

对于前端实际项目开发,相信都用过分页组件,各种组件库都有提供现成的组件,对于分页组件,大多数的组件库的分页组件的功能基本是类似的,不同的基本就是样式的些许不同,我们使用的时候,直接拿来就能用,对于这么常见的组件,我觉得还是有必要自己去实现一下的,对于我们自己组建编码能还是有提高的,现在记录实现分页组件的过程。

最终效果

动画.gif

注意事项

对于分页组件,我们要去自己写一个,一定要考虑以下几个问题:

  1. 分页分页,当然需要数据条数,以及每页有多少条,这样才能通过公式计算总条数/每页条数=总页数
  2. 可能会遇到总条数/每页条数除不进的问题,那不用问,我们肯定需要将步骤1的结果向上取整。
  3. 分页组件应该支持上下翻页,页数过多自动折叠以及切换当前页的条数。
  4. 最后就是自定义事件,我们分页组件主要考虑两个自定义事件(因为我们实际工作中主要用到这两个事件去更新数据):
    • 当前页码变更
    • 每页条数变更

难点

分页组件的难点主要就一个地方:怎么根据总条数和当前页码动态折叠过多的分页。

页码过多解决方案

当前实现的分页组件关于折叠过多页数分为一下情况(自己实现的话可以根据自己需求改):

  1. 总页码数<5的直接返回
  2. 总页码数>5页,当时当前页码数<=3
  3. 总页数-当前页数<=3
  4. 排除其他的,剩下的就是有向前和向后的操作点

组件用法

具体用户其实和组件库的用法都差不多,传递的props也大同小异。

App.vue

<template>
  <div class="app">
    <div class="flex">
      <div class="item">
        <my-page
          :total="1332"
          :current="1"
          :page-size="10"
          :show-total="true"
          :show-sizer="true"
          @on-change="handlePageChange"
          @on-page-size-change="handlePageSizeChange"
          :page-size-opts="[10, 20, 30, 40]"
        ></my-page>
      </div>
   
    </div>
  </div>
</template>

<script>
import MyPage from "./components/MyPage/MyPage.vue";
export default {
  components: { MyPage },
  },

  methods: {
    // 页码变更
    handlePageChange(page) {
      console.log("当前页码:" + page);
    },
    // 每页条数变化
    handlePageSizeChange(pageSize) {
      console.log("每页条数:" + pageSize);
    },
  },
};
</script>

<style lang="less">
.app {
  padding: 20px;
  .flex {
    display: flex;
    .item {
      margin: 0 20px;
    }
  }
}
</style>

具体实现

.components/MyPage/MyPage.vue

<template>
  <div class="my-page">
    <!-- 总条数 -->
    <span class="my-page-total" v-if="showTotal">共{{ total }}条</span>
    <ul class="page-list">
      <!-- 上一页 -->
      <li
        :class="['page-list-item', currentPage <= 1 ? 'disbled' : '']"
        @click="handleBeforOrAfterClick('before')"
      >
        &lt;
      </li>
      <!-- 中间页码数据 -->
      <template v-for="(pageItem, index) in pageList">
        <!-- 向前5页 -->
        <li
          :class="['page-list-item', 'page-list-item-prev']"
          @click="handlePageItemClick(pageItem, 'prev')"
          :key="index"
          title="向前5页"
          v-if="pageItem == 'prev'"
          @mouseover="prevStatus = false"
          @mouseleave="prevStatus = true"
        >
          <i v-if="prevStatus"> ...</i>
          <i v-else><<</i>
        </li>
        <!-- 正常页码 -->
        <li
          :class="['page-list-item', 'page-list-item-next']"
          @click="handlePageItemClick(pageItem, 'next')"
          :key="index"
          title="向后5页"
          v-else-if="pageItem == 'next'"
          @mouseenter="nextStatus = false"
          @mouseleave="nextStatus = true"
        >
          <i v-if="nextStatus"> ...</i>
          <i v-else>>></i>
        </li>
        <!-- 向后五页 -->
        <li
          :class="['page-list-item', currentPage == pageItem ? 'active' : '']"
          @click="handlePageItemClick(pageItem)"
          :key="index"
          :title="pageItem"
          v-else
        >
          {{ pageItem }}
        </li>
      </template>
      <!-- 下一页 -->
      <li
        :class="[
          'page-list-item',
          currentPage >= totalPage && totalPage > 0 ? 'disbled' : '',
        ]"
        @click="handleBeforOrAfterClick('after')"
      >
        &gt;
      </li>
    </ul>
    <!-- 每页条数电梯 -->
    <div class="page-size-select" v-if="showSizer">
      <select @change="handlePageSizeChange">
        <option
          :value="pageSizeItem"
          v-for="pageSizeItem in pageSizeOpts"
          :key="pageSizeItem"
        >
          {{ pageSizeItem }}条/页
        </option>
      </select>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    // 总页码条数
    total: {
      type: Number,
      default: 0,
    },
    // 当前页
    current: {
      type: Number,
      default: 1,
    },
    // 每页条数,默认10条
    pageSize: {
      type: Number,
      default: 10,
    },
    // 是否显示总条数
    showTotal: {
      type: Boolean,
      default: false,
    },
    // 每页大小
    showSizer: {
      type: Boolean,
      default: false,
    },
    // 页码条数下拉
    pageSizeOpts: {
      type: Array,
      default() {
        return [10, 20, 30, 40];
      },
    },
  },
  data() {
    return {
      // 当前页码
      currentPage: this.current || 1,
      // 当前每页条数
      currentPageSize: this.pageSize,
      // 是否显示向后5条的>>
      prevStatus: true,
      // 是否显示向前5条的<<
      nextStatus: true,
    };
  },
  computed: {
    // 根据总条数和每页条数计算得出总页数,记得向上取整
    totalPage() {
      return Math.ceil(this.total / this.currentPageSize);
    },
    // 动态根据当前页,重新组装分页列表数据
    pageList() {
      //情况1: 总页码数<5的直接返回
      if (this.totalPage <= 5) {
        return this.totalPage;
      }
      //情况2: 总页码数>5页,当时当前页码数<=3
      if (this.currentPage <= 3) {
        return [1, 2, 3, "next", this.totalPage];
      }
      // 情况3:总页数-当前页数<=3
      if (this.currentPage > 3 && this.totalPage - this.currentPage <= 2) {
        return [
          1,
          "prev",
          this.totalPage - 2,
          this.totalPage - 1,
          this.totalPage,
        ];
      }
      // 情况4:排除其他的,剩下的就是有向前和向后的操作点
      return [
        1,
        "prev",
        this.currentPage - 2,
        this.currentPage - 1,
        this.currentPage,
        this.currentPage + 1,
        this.currentPage + 2,
        "next",
        this.totalPage,
      ];
    },
  },
  methods: {
    // 页码项点击
    handlePageItemClick(item, type) {
      // 重置向上和向下翻五页
      this.prevStatus = true;
      this.nextStatus = true;
      switch (type) {
        // 向前5页
        case "prev":
          this.currentPage -= 5;
          if (this.currentPage < 1) {
            this.currentPage = 1;
          }
          break;
        // 向后5页
        case "next":
          this.currentPage += 5;
          if (this.currentPage > this.totalPage) {
            this.currentPage = this.totalPage;
          }
          break;
        default:
          // 默认常规页码点击
          if (this.currentPage == item) {
            return;
          }
          this.currentPage = item;
      }
      // 发布页码变更事件
      this.$emit("on-change", this.currentPage);
    },
    // 上一页和下一页点击
    handleBeforOrAfterClick(type) {
      switch (type) {
        case "before":
          this.currentPage--;
          if (this.currentPage < 1) {
            this.currentPage = 1;
            return;
          }
          break;
        case "after":
          this.currentPage++;
          if (this.currentPage > this.totalPage) {
            this.currentPage = this.totalPage;
            return;
          }
          break;
      }
      // 发布页码变更事件
      this.$emit("on-change", this.currentPage);
    },
    // 每页条数大小变化
    handlePageSizeChange(e) {
      // value是字符串,记得转成数字
      this.currentPageSize = Number(e.target.value);
      // 重置当前页码
      this.$emit("on-page-size-change", this.currentPageSize);
      this.currentPage = 1;
    },
  },
};
</script>

<style lang="less">
.my-page {
  display: flex;
  align-items: center;
  .page-list {
    .page-list-item {
      display: inline-block;
      vertical-align: middle;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none;
      min-width: 32px;
      height: 32px;
      line-height: 30px;
      list-style: none;
      text-align: center;
      cursor: pointer;
      color: #666;
      font-family: Arial;
      border: 1px solid #dcdee2;
      border-radius: 4px;
      transition: all 0.2s ease-in-out;
      margin-right: 6px;
      margin-left: 6px;
      &:hover {
        border-color: #2d8cf0;
        color: #2d8cf0;
      }
    }
    .active.page-list-item {
      border-color: #2d8cf0;
      color: #fff;
      background-color: #2d8cf0;
    }
    .disbled.page-list-item {
      cursor: not-allowed;
      background-color: #fff;
      color: #dcdee2;
    }
    .disbled:hover {
      border-color: #dcdee2;
    }
  }
}
</style>

.components/MyPage/index.js

//按需导出
import MyPage from "./MyPage.vue";

export default { MyPage };

总结

其实组件库的很多组件还是很有意思的,可以自己试着去想想那些华丽的组件的怎么实现的,然后通过代码写出来,这对于框架的运用会更加熟练。