封装的分页组件的结构如下:
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