文章目錄
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,showPageEnd
爲pageCount
。
3.2 僅右邊有省略號
判斷條件:當前頁碼索引小於(pageItemCount - 3) / 2
。爲什麼要減去3?減去首頁、尾頁和當前頁。爲什麼除2?因爲對稱軸是當前頁碼。
變量賦值:showPageStart
爲1,showPageEnd
爲pageItemCount – 1
.
3.3 僅左邊有省略號
判斷條件:當前頁碼索引大於等於 pageCount - (this.pageItemCount - 3) / 2
,
變量賦值:showPageStart
爲1,showPageEnd爲pageItemCount – 1
.
3.4 兩邊都有省略號
判斷條件:不是以上情況
變量賦值:showPageStart
爲pageIndex - ((pageItemCount - 3) / 2)
,但不能小於2, showPageEnd
爲 pageIndex + ((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>