文章目录
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>