封装的分页组件的结构如下:
1.基本分页功能
(1)将数组分为指定大小的多个小数组的chunk方法
export function chunk(arr=[],size=1){
if(arr.length===0) return [];
return arr.reduce((total,currentValue)=>{
if(total[total.length-1].length===size){
total.push([currentValue]);//当小数组的长度与指定大小size相等时,重新创建一个小数组
}
else{
total[total.length-1].push(currentValue);//当小数组的长度小于指定大小size时,就往该小数组中添加数据
}
return total;
},[[]])//[[]]是为了将分割后的小数组仍保留在一个大数组中
}
2.页码显示策略
为了方便地跳转到任意页码,却又不至于在页面中显示太多页码,页码不是始终全部显示出来的,而是在页码少时全部显示,页码多时只显示部分页码。这就存在显示策略问题。
(1)我们从当前页码出发,比如模块中当前页码是第5页:
那么以该页码为中心,两边显示一定的页码,比如两边各显示2页;
另外首页和尾页需要始终显示出来,方便回到首页和跳转到尾页;
首页到第3页中间的页码以及第7页到页尾的页码都隐藏起来,并且支持点击左/右更多按钮,快捷跳转多页(比如5页)的功能。
(2)如果只存在8页,则去掉右边的更多按钮:
(3)如果当前页码在第4页,则去掉左边的更多按钮,显示右边的更多按钮:
简述如下:
(1)首页和尾页需要始终显示出来(如果只有1页则不显示尾页);
(2)除首尾页之外,当前页码左右最多只显示2页(共5页);
(3)其它页码折叠起来,用更多按钮(…)代替。
3.分页器
(1)分3步实现分页器功能:
1)实现首尾翻页
2)实现快捷分页
3)实现分页按钮组
(2)增加左/右按钮更多按钮的翻页功能
有了首尾页的翻页还不够,还需要继续完善更多按钮的快捷翻页功能。
先梳理下更多按钮的显示逻辑:
1)中间按钮一共5页,加上首尾按钮2页,一共7页,也就是说只有大于7页,才有可能显示更多按钮;
2)左右更多按钮会随着当前页码的不同而显示或隐藏,以第4页和倒数第4页为界;
3)当页码大于第4页时,应该显示左边更多按钮;
4)当页码小于倒数第4页,都应该显示右边更多按钮。
//Pager组件代码:
<template>
<ul class="pager">
<!-- 首页-->
<li class="number"
:class="{active: this.current===1}"
@click="setPage(1)">1</li>
<!-- 左边更多-->
<li class="more left"
v-if="totalPage>centerSize+2 && current-centerSize/2-1>1"
@click="setPage(current-jumpSize)">...</li>
<!-- 中间页码-->
<li class="number"
v-for="(page,index) in centerPages"
:class="{active: current===page}"
:key="index"
@click="setPage(page)">{{ page }}</li>
<!-- 右边更多-->
<li class="more right"
v-if="totalPage>centerSize+2 && current+centerSize/2+1<totalPage"
@click="setPage(current+jumpSize)">...</li>
<!-- 最后一页-->
<li class="number"
v-if="totalPage!==1"
:class="{active: this.current=== totalPage}"
@click="setPage(totalPage)">{{ totalPage }}</li>
</ul>
</template>
<script>
export default {
name: "Pager",
props:{
totalPage:Number,//数据总页数
defaultCurrentPage:Number,//默认当前页码
//中间页码数默认为5
centerSize:{
type:Number,
default(){
return 5;
}
},
jumpSize:{
type:Number,
default(){
return 5;
}
}
},
computed:{
// 中间页码计算
centerPages(){
let centerPage=this.current;
// 若当前页面大于this.current+2(以current为中心右边加两页) +1(尾页)>this.totalPage,则取this.totalPage-3为中心
if(this.current>this.totalPage-3){
centerPage=this.totalPage-3;//注意这里是centerPage,不是this.current
}
// 若当前页面小于或等于4,则取4为中心
if(this.current<4){
centerPage=4;
}
if(this.totalPage<=this.centerSize+2){
// 总页码较少时,则全部显示出来
const centerArr=[];
for(let i=2;i<this.totalPage;i++){
centerArr.push(i);
}
return centerArr;
}
else{
// 总页数较大时,只显示中间centerSize个页码
const centerArr=[];
for(let i=centerPage-2;i<=centerPage+2;i++){
centerArr.push(i);
}
return centerArr;
}
}
},
data(){
return{
// 因为不可以直接修改props传过来的值,所以在data中用current保存props中默认页面页数defaultCurrentPage的值,用watch监听props中defaultCurrentPage值的改变
// (前一页、后一页按钮被点击时,会传入新的值,但是data中的current不会更新,用watch监听并赋新的值,可解决该bug)
current:this.defaultCurrentPage,
}
},
watch:{
// 监听props中defaultCurrentPage值的改变,更新current的值
defaultCurrentPage:function(newValue,oldValue){
this.current=newValue;
}
},
methods:{
// 上一页、下一页按钮被点击时,页码发生改变,且要传出被点击的页码
setPage(page){
// 左边越界
if(page<1) this.current=1;
// 右边越界
else if(page>this.totalPage){
this.current=this.totalPage;
}
// 正常情况
else{
this.current=page;
}
// 发出pager中页码被改变的事件
this.$emit('change',this.current);
},
}
}
</script>
<style scoped>
ul{
list-style: none;
height: 32px;
}
ul li{
float: left;
width:30px;
height:30px;
line-height: 30px;
margin:0 8px 0 0;
padding:0 6px;
text-align: center;
border:1px solid #d9d9d9;
border-radius:2px;
}
.active{
border:1px solid #7d3990;
}
.more left:hover, .more right:hover{
background: #7d3990;
}
</style>
//Pagination代码:
<template>
<div id="pagination">
<!-- 前一页按钮-->
<input class="btn-prev" type="button" value="<" @click="setPage(current-1)">
<!-- 分页器-->
<pager :total-page="totalPage" :default-current-page="current" @change="pageChange"></pager>
<!-- 后一页按钮-->
<input class="btn-next" type="button" value=">" @click="setPage(current+1)">
</div>
</template>
<script>
import Pager from "./Pager";
export default {
name: "Pagination",
components:{
Pager
},
props:{
// 默认当前页码
defaultCurrentPage:{
type:Number,
default(){
return 1;
}
},
// 默认每页数据的条数
defaultPageSize:{
type:Number,
default(){
return 10;
}
},
// 数据的总条数
total:{
type:Number,
default(){
return 100;
}
},
},
data(){
return{
current:this.defaultCurrentPage
}
},
computed:{
// 计算数据总页数
totalPage(){
return Math.ceil(this.total/this.defaultPageSize);
}
},
methods:{
// 上一页、下一页按钮被点击时,页码发生改变,且要传出被点击的页码
setPage(page){
// 左边越界
if(page<1)this.current=1;
// 右边越界
else if(page>this.totalPage){
this.current=this.totalPage;
}
// 正常情况
else{
this.current=page;
}
// 发出事件,让外部知道页码改变了
this.$emit('change',this.current);
},
// 接收pager中发出的改变页码事件,并再次发送出去
pageChange(page){
this.$emit('change',page);
}
}
}
</script>
<style scoped>
#pagination{
display: flex;
}
.btn-prev, .btn-next{
width:30px;
height:30px;
line-height: 30px;
margin:0 8px 0 0;
padding:0 6px;
text-align: center;
border:1px solid #d9d9d9;
border-radius:2px;
background: #fff;
}
</style>
参考:https://juejin.im/post/6844904151730782221#heading-30