Vue.js开发分页组件

Vue.js开发分页组件

前言

目前有很多成熟的框架,比如element-ui等,为何还需要自己造轮子?首先别人的轮子会考虑到跟多功能,但有很多功能我们是用不到的,故用的轮子看着过于臃肿,导致页面性能受影响,其次用轮子出bug不好找到问题,最后就是自己定制的轮子才最符合自己项目需求,用久了别人的框架,总感觉某些功能需求实现有点曲线救国、南辕北辙的迹象。

1. 需求

1.1 提出需求

  • 动态改变每页容纳的条目数(page-size)
  • 实现上一页、下一页功能
  • 当页面过多时可以动态用省略号代替部分页面
  • 可以输入页码,按回车或者确认按钮可以直接跳转至指定页面
  • 当页面状态改变时,可触发事件

1.2 需求分析

  • 需求1页码组件可通过props属性接收父组件传递的参数,问题不大。
  • 需求2在页码组件中摆两button控件,绑定相应事件,问题也不大。
  • 需求3需要动态省略部分页面,并用省略号代替省略部分,有点啰嗦。
  • 需求4在页码组件中摆个input控件和确认button控件,绑定相应事件,问题也不大。
  • 需求5 在页码组件中通过emit方法对父组件传值,问题也不大,就是需要注意些坑。

总之,一通分析下来,页码组件还是不难实现的。接下来问题不大的需求不会重点讲,最后我会把自己写的源码贴出来,相信自己看代码就会了。所以本文主要讲的就是需求3了。

2. UI界面

在这里插入图片描述
UI有点丑。。。麻雀虽小五脏俱全,各位将就着看吧。

3. js逻辑设计

从上图UI来看,有些是固定的,稍微有点变化的就是页码元素(page-item)了。具体的场景大家都很熟,但是还是想不厌其烦的描述一下,以便加深此组件的机制。场景:当页码数量较少时,所有页码都显示。当页码数量过多时,需要根据当前页码的位置来决定哪些显示哪些不显示。所以有哪些页码需要显示出来取决于当前页码的位置。

在这里我准备分四种情况来分析,分别是无省略号、仅右边出现省略号、仅左边出现省略号、两边出现省略号。为了实现统一,准备用两个变量不同的值来表示这四种情况,分别表示的是左边省略号后第一个页码索引showPageStart和右边省略号前一个页码索引showPageEnd。如果没有左边省略号showPageStart为2,如果没有右边省略号showPageEnd为总页数pageCount

3.1 无省略号

判断条件:pageCount小于等于需要显示的页码数pageItemCount(父组件传入,不传默认为5)。
变量赋值:showPageStart为1,showPageEndpageCount

3.2 仅右边有省略号

判断条件:当前页码索引小于(pageItemCount - 3) / 2。为什么要减去3?减去首页、尾页和当前页。为什么除2?因为对称轴是当前页码。
变量赋值:showPageStart为1,showPageEndpageItemCount – 1.

3.3 仅左边有省略号

判断条件:当前页码索引大于等于 pageCount - (this.pageItemCount - 3) / 2
变量赋值:showPageStart为1,showPageEnd为pageItemCount – 1.

3.4 两边都有省略号

判断条件:不是以上情况
变量赋值:showPageStartpageIndex - ((pageItemCount - 3) / 2),但不能小于2, showPageEndpageIndex + ((pageItemCount - 3) / 2)

4. html界面设计

有两种设计思路,对pageCount进行,根据不同条件显示不同页码;另一种就是对pageItemCount进行迭代,根据不同条件显示不同页码。我参考的链接思路是方法一,传送门,并且它通过JQuery挂载样式,显得有些笨重,但是毕竟我也是参考此链接为基础,又借鉴同事的思路才有了第二种方法,反正都不容易~~~~ 第二种方法优势在于pageItemCount是可以远小于pageCount的,当pageCount非常大的时候,第一种方法页面就炸了,当时试的是10万。然而方法二由于迭代次数不会很大,所以基本不会出现性能问题。

5 github源码

针对于此页码组件,本人放在github上,有疑问欢迎大家提issue或者在底下评论,有什么不足也欢迎大家补充。用到技术仅仅是vue.js,构建项目脚手架是vue-cli,icon样式是font awesome。
最后献出自己的页面效果,比较原始,大家各取所需:
在这里插入图片描述

6 核心代码展示

当然有些高手为了一个小小组件都懒得去GitHub了,故此贴出核心代码。但不保证是最新的,如果想具体了解怎么运用的小白,可转移至github,手把手运行起来可能理解会更深刻。

<template>
  <div class="pagination">
    <select v-if="pageSizeList.length>0" v-model="pageSizeComponents" @change="selectValueChange">
      <option v-for="item in pageSizeList" :value="item" >{{item}}</option>
    </select>
    <button
      :disabled="leftArrowDisabled"
      class="active-color"
      @click="handleCurrentChange(currentPageIndex-1)">
      <i
        class="fa fa-angle-left"
        aria-hidden="true"/>
    </button>
    <button
      :class="1===currentPageIndex?'active-color':''"
      @click="handleCurrentChange(1)">
      {{ 1 }}
    </button>
    <span
      v-if="1<showPageStart-1">
      ...
    </span>
    <div
      v-for="item in (pageItemCount>pageCount?pageCount:pageItemCount)"
      :key="item"
      class="flex-row">
      <button
        v-if="item>1&&item<(pageItemCount>pageCount?pageCount:pageItemCount)"
        :class="(showPageStart+item-2)===currentPageIndex?'active-color':''"
        @click="handleCurrentChange(showPageStart+item-2)">
        {{ showPageStart+item-2 }}
      </button>
    </div>
    <span
      v-if="pageCount>showPageEnd+1">
      ...
    </span>
    <button
      v-if="pageCount>1"
      :class="pageCount===currentPageIndex?'active-color':''"
      @click="handleCurrentChange(pageCount)">
      {{ pageCount }}
    </button>
    <button
      :disabled="rightArrowDisabled||pageCount===1"
      class="active-color"
      @click="handleCurrentChange(currentPageIndex+1,$event)">
      <i
        class="fa fa-angle-right"
        aria-hidden="true"/>
    </button>
    <input
      v-model="inputIndex"
      placeholder="输入页码"
      size="5"
      @keyup.enter="inputChange">
    <button
      class="active-color"
      @click="inputChange">
      <i
        style="color: #fff"
        class="fa fa-hand-pointer-o"
        aria-hidden="true"/>
    </button>
  </div>
</template>

<script>
  export default {
    name: 'PageIndicator',
    props: {
      currentPage: {type: Number, default: 1},
      total: {type: Number, required: true},
      pageItemCount: {type: Number, default: 5},
      pageSize: {type: Number, default: 10},
      pageSizeList: {type: Array, default: []}
    },
    // props:["currentPage","total","pageItemCount"],
    data () {
      return {
        inputIndex: '',
        currentPageIndex: 1,
        //为第一页或者最后一页时,首页,尾页不能点击
        leftArrowDisabled: false,
        rightArrowDisabled: false,
        //总页数
        pageCount: 0,
        //开始显示的分页按钮
        showPageStart: 2,
        //结束显示的分页按钮
        showPageEnd: Infinity,
        pageSizeComponents:''
      }
    },
    mounted () {
      if(this.pageSizeList.length>0){
        this.pageSizeComponents = this.pageSizeList[0]
      }else{
        this.pageSizeComponents = this.pageSize
      }
      this.handleCurrentChange(1)
    },
    methods: {
      handleCurrentChange (pageIndex) {
        let currentPageCount = Math.ceil(this.total / this.pageSizeComponents)
        // console.log(pageIndex);
        //判断数据是否需要更新
        if (currentPageCount !== this.pageCount) {
          pageIndex = 1
          this.pageCount = currentPageCount
        } else if (this.currentPageIndex === pageIndex && currentPageCount === this.pageCount) {
          console.log('not refresh')
          // this.$emit('onPageSelected', pageIndex);
          return
        }
        if (pageIndex > 0) {
          if (pageIndex > this.pageCount) {
            pageIndex = this.pageCount
          }
          //计算分页按钮数据
          if (this.pageCount > this.pageItemCount) {
            if (pageIndex <= (this.pageItemCount - 3) / 2) {
              this.showPageStart = 2
              this.showPageEnd = this.pageItemCount - 1
              console.log('showPage1')
            } else if (pageIndex >= this.pageCount - (this.pageItemCount - 3) / 2) {
              this.showPageStart = this.pageCount - this.pageItemCount + 2
              this.showPageEnd = this.pageCount
              console.log('showPage2')
            } else {
              console.log('showPage3')
              this.showPageStart = pageIndex - ((this.pageItemCount - 3) / 2)
              if (this.showPageStart < 2) {
                this.showPageStart = 2
              }
              this.showPageEnd = pageIndex + ((this.pageItemCount - 3) / 2)
            }
          }else{
            this.showPageStart = 2
            this.showPageEnd = this.pageCount
          }
          this.showPageStart = Math.floor(this.showPageStart)
          this.showPageEnd = Math.floor(this.showPageEnd)
        } else {
          pageIndex = 1
        }
        this.currentPageIndex = pageIndex
        this.parseArrowStatus()
        this.$emit('on-page-selected', pageIndex, this.pageSizeComponents)
      },

      //判断左右箭头禁用状态
      parseArrowStatus () {
        //如果当前页首页或者尾页,则上一页首页就不能点击,下一页尾页就不能点击
        this.leftArrowDisabled = false
        this.rightArrowDisabled = false
        if (this.currentPageIndex === 1) {
          this.leftArrowDisabled = true
        } else if (this.currentPageIndex === this.pageCount) {
          this.rightArrowDisabled = true
        }
      },
      inputChange () {
        this.handleCurrentChange(parseInt(this.inputIndex))
      },
      selectValueChange () {
        this.handleCurrentChange(1)
      }
    },

  }
</script>

<style scoped>
  .flex-row {
    display: inline;
  }

  .pagination button {
    margin: 0 3px;
  }

  .active-color {
    background-color: #409eff;
    color: #ffffff;
  }

</style>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章