需求分析#
- 自定義顯示的頁碼按鈕的數量
- 自定義分頁組件的顏色主題
- 可以選擇顯示文本內容或者圖標來進行翻頁操作
實現#
- 分頁組件需要顯示的頁碼按鈕數量,可以通過 pagesToDisplay () 這個計算屬性定義
- 顏色主題可以通過綁定一個
paginationClass
的 class,返回顏色type
實現 - 文本或圖標的選擇可以通過嵌套條件判斷 template 實現
分頁原理#
實現分頁主要依靠這兩個參數,total (總條目數),perPage (每頁顯示的條目數量)。後端可以通過這兩個參數,返回相應的數據給前端。
整個分頁組件中,相對來說比較麻煩的地方在於頁碼列表的顯示邏輯。頁碼列表是一個返回 [最小頁碼,最大頁碼] 的數組。進而,問題被切分為如何求解頁碼列表數組中頁碼值的最大值和最小值。這裡定義兩個計算屬性 maxPage (),minPage () 來返回頁碼數組中頁碼值的最大值,和最小值。
頁面組件#
<template>
<ul class="pagination" :class="paginationClass">
<!-- 前一頁 -->
<li
class="page-item prev-page"
:class="{ disabled: value === 1,'no-arrows': noArrows }"
>
<a class="page-link" arial-label="Previous" @click="prevPage">
<!-- 是否有文本 -->
<template v-if="withText">
perv
</template>
<!-- 箭頭圖標 -->
<i class="arrow-left" v-else></i>
</a>
</li>
<!-- 當前顯示的分頁列表 -->
<li
class="page-item"
v-for="item in range(minPage, maxPage)"
:key="item"
:class="{active: value === item}"
>
<a class="page-link" @click="changePage(item)">{{item}}</a>
</li>
<!-- 後一頁 -->
<li
class="paga-item next-page"
:class="{disabled: value === totalPage, 'no-arrows': noArrows}"
>
<a class="page-link" arial-label="Next" @click="nextPage">
<!-- 文本內容 -->
<template v-if="withText">
Next
</template>
<i class="arrow-right" v-else></i>
</a>
</li>
</ul>
</template>
props#
props: {
// 顏色主題
type: {
type: String,
default: "primary",
// 驗證`type`的值
validator: value => {
return [
"default",
"primary",
"info",
"success",
"danger"
].includes(value);
}
},
// 是否帶有文本內容
withText: Boolean,
// 箭頭圖標
noArrows: Boolean,
// 頁碼數
pageCount: {
type: Number,
default: 0
},
// 每頁顯示的項數
perPage: {
type: Number,
default: 0
},
// 總項數
total: {
type: Number,
default: 0
},
// 當前頁碼的值
value: {
type: Number,
default: 1
}
}
data#
data: {
// 頁碼列表中顯示的頁碼數量
defaultPagesToDisplay: 5
}
computed#
computed: {
// 顏色主題
paginationClass() {
return `pagination-${this.type}`
},
// 總頁碼數
toatalPages() {
if (this.pageCount > 0) return this.pageCount;
if (this.total > 0 ) {
return Math.ceil(this.total / this.perPage);
}
},
// 顯示的頁碼按鈕數量
pagesToDisplay() {
if (this.totalPages > 0 && this.totalPages < this.defaultPagesToDisplay) {
return this.totalPages;
} else {
return this.defaultPagesToDisplay;
}
},
// 頁碼列表中最小的頁碼數
minPage() {
if (this.value >= this.pagesToDisplay) {
// 定義一個偏移量
const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
// 假定的最大頁碼數 = 當前頁碼值 + 偏移量
const newMaxPage = this.value + pagesToAdd;
if (newMaxPage > this.totalPages) {
return totalPages - this.pagesToDisplay + 1;
} else {
// 當前頁碼值始位處於頁碼列表的中間位置
return this.value - pagesToAdd;
}
} else {
// 當前頁碼值小於默認頁碼列表按鈕數量時,最小頁碼數返回1
return 1;
}
},
// 頁碼列表中最大的頁碼數
maxPage() {
if (this.value >= this.pagesToDisplay) {
const pagesToAdd = Math.floor(this.pagesToDisplay / 2);
const newMaxPage = this.value + pagesToAdd;
if (newMaxPage < this.totalPages) {
return newMaxPage;
} else {
return totalPages;
}
} else {
// 當前頁碼值小於默認頁碼列表按鈕數量時,最大頁碼數返回顯示的頁碼列表按鈕數
return this.pageToDisplay;
}
}
}
methods#
methods: {
// 頁碼列表數組
range(min, max) {
let arr =[];
for (let i = min; i <= max; i++) {
arr.push(i);
}
return arr;
},
// 跳轉頁碼
changePage(item) {
this.$emit("input", item);
},
// 下一頁
nextPage() {
if (this.value < this.totalPages) {
this.$emit("input", this.value + 1);
}
},
// 上一頁
prevPage() {
if (this.value > 1) {
this.$emit("input", this.value - 1);
}
}
}
最後#
想要不斷提高自己的技術水平,就不能只滿足於寫業務組件。在寫像分頁這種獨立組件的過程中,也讓我思考API的設計
,以及功能複雜性
的問題。無論是多麼複雜的組件,大體上都是由 prop,event,slot 三部分組成。具體的實現,主要還是依靠基礎的 javascript 的能力。
此前,我也仿照 element 寫了 radio 和 input 組件。希望自己能堅持去寫這些獨立組件,去發現更大的世界。本文的分頁組件源碼在這兒。