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>

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