简介
本文介绍基于 vue2 框架实现基本的分页器,包括页码前进/后退、页码点击跳转、显示 ... 、显示总页数、显示总数据条数 等功能。
效果预览
快速跳转
实现思路
视图部分
视图方面比较简单,将各功能部分依次搭建即可,包括前进键(>>)、后退键(<<)、中间以及首尾(1和页码上限)的页码、...、以及页码总计和数据总计。
代码示例
<template>
<div class="pagination">
<div class="pagination_wrap">
<div class="pagination_total-data pagination_info">
<span>共{{ total }}条数据</span>
</div>
<div class="pagination_action-group">
<div
class="prev pagination_btn"
:class="{ disabled: isPrevDisabled }"
:disabled="isPrevDisabled"
@click="handlePrev"
>
<a>«上一页</a>
</div>
<div class="pagination_btn-group" @click="handlePageSelect">
<template v-if="isShowPrevDot">
<li class="pagination_btn">
<a data-page="1">1</a>
</li>
<li class="pagination_dotted">
<span>...</span>
</li>
</template>
<li
class="pagination_btn"
:class="{ active: currentPage === i }"
v-for="i in pageList"
:key="i"
>
<a :data-page="i">{{ i }}</a>
</li>
<template v-if="isShowNextDot">
<li class="pagination_dotted">
<span>...</span>
</li>
<li class="pagination_btn">
<a :data-page="totalPages">{{ totalPages }}</a>
</li>
</template>
</div>
<div
class="next pagination_btn"
:class="{ disabled: isNextDisabled }"
:disabled="isNextDisabled"
@click="handleNext"
>
<a>下一页»</a>
</div>
</div>
<div class="pagination_total-pages pagination_info">
<span>共{{ totalPages }}页</span>
</div>
</div>
</div>
</template>
样式部分
样式部分(scss),通过 Flex 布局使元素水平排列,并赋予基本样式
// 全局 css 变量
:root {
--gap-small: 5px;
--gap-primary: 15px;
}
// 覆盖浏览器默认样式
body {
li {
box-sizing: border-box;
}
// 注意添加下面这条样式,否则部分浏览器在 ... 旁边会显示一个额外的 ·
[class*='dotted']::marker {
content: '';
}
}
.pagination {
width: 100%;
overflow: hidden;
.pagination_wrap {
margin: 18px 0;
display: flex;
justify-content: center;
.pagination_action-group {
margin-left: 0;
margin-bottom: 0;
vertical-align: middle;
display: flex;
.pagination_btn {
line-height: 18px;
display: inline-block;
a {
position: relative;
line-height: 18px;
text-decoration: none;
background-color: #fff;
border: 1px solid #e0e9ee;
font-size: 14px;
padding: 9px 18px;
color: #333;
}
&.active {
a {
background-color: #fff;
color: #e1251b;
border-color: #fff;
cursor: default;
}
}
&.prev,
&.next {
a {
background-color: #fafafa;
cursor: pointer;
}
}
&.disabled {
a {
color: #999;
cursor: default;
}
}
}
.pagination_dotted {
span {
position: relative;
line-height: 18px;
text-decoration: none;
background-color: #fff;
font-size: 14px;
border: 0;
padding: 9px 18px;
color: #333;
}
}
.pagination_btn-group {
display: flex;
}
}
.pagination_info {
color: #333;
font-size: 14px;
}
.pagination_total-data {
margin-right: var(--gap-primary);
}
.pagination_total-pages {
margin-left: var(--gap-primary);
}
}
}
数据和逻辑
接下里是关键的逻辑实现,包括前进、后退、页码点选跳转、显示 ... 等功能。
数据部分
props 中定义的 4 个变量与数据显示相关,通常从服务端获取,且不支持内部修改,因此由父组件传递进来;data 和 computed 中定义的 8 个变量与视图构建或更新相关,属于组件内私有,仅支持内部修改。
props: {
total: Number, // 数据总数
currentPage: Number, // 当前页码数
pagerCount: Number, // 允许同时显示的页码数
totalPages: Number, // 页码总数
},
data() {
return {
pageList: [], // 中间区域显示的页码范围
start: 0, // 中间区域显示的开始页码指针
end: 0, // 中间区域显示的结束页码指针
isShowPrevDot: false,
isShowNextDot: false,
};
},
computed: {
isPrevDisabled() {
return this.currentPage === 1;
},
isNextDisabled() {
return this.currentPage === this.totalPages;
},
// 总页数超过当前允许的同时显示页数,才有可能需要显示 ...
isDotNeeded() {
return this.totalPages > this.pagerCount;
},
},
逻辑处理部分
逻辑处理部分包括:
- 两个内部变量更新方法:setupPageList(start, end) 和 setupPagination(currentPage, totalPages, pagerCount)
- 一个 emit 通知方法:updateCurrentPage(newPage)
- 三个事件处理方法:handlePrev() 、handleNext() 、handlePageSelect(e)
- 一个侦听器:侦听 currentPage 并初始化
methods: {
// 根据开始和结束指针,重新生成中间区域的页码
setupPageList(start, end) {
let pageList = [];
for (let i = start; i <= end; i++) {
pageList.push(i);
}
return pageList;
},
// 根据操作后的当前页码,重新判定是否显示两侧的 ... ,以及更新中间区域的页码范围
setupPagination(currentPage, totalPages, pagerCount) {
// 初始化 start 和 end
let start = 1, end = totalPages;
if (this.isDotNeeded) {
// 从当前页码开始,向左右两侧延伸 pagerCount 一半的距离
let halfOffset= Math.floor(pagerCount / 2, 0);
start = Math.max(1, currentPage - halfOffset);
end = Math.min(totalPages, currentPage + halfOffset);
// 若中间页码数量仍小于 pagerCount,先拉长到 pagerCount
if (end - start + 1 < pagerCount) {
if (end - pagerCount > -1) {
// 左侧仍有剩余页码
start = end - pagerCount + 1;
} else {
end = start + pagerCount - 1;
}
}
// 根据延伸后的左右指针,判定是否需要显示左右两侧的 ...
this.isShowPrevDot = start > 1;
this.isShowNextDot = totalPages - end > 0;
} else {
this.isShowPrevDot = false;
this.isShowNextDot = false;
}
// 根据 ... 的判定结果,赋值两侧指针的最终值
this.start = Math.max(1, start + (this.isShowPrevDot ? 1 : 0));
this.end = Math.min(totalPages, end - (this.isShowNextDot ? 1 : 0));
// 根据最新的指针,生成中间区域的页码
this.pageList = [].concat(this.setupPageList(this.start, this.end));
},
handlePrev() {
const oldPage = this.currentPage;
const newPage = Math.max(1, oldPage - 1);
if (oldPage !== newPage) {
this.updateCurrentPage(newPage);
// 由于侦听器的存在,不需要手动触发分页器更新;next 与 select 同理
// this.setupPagination(newPage);
}
},
handleNext() {
const oldPage = this.currentPage;
const newPage = Math.min(this.totalPages, oldPage + 1);
if (oldPage !== newPage) {
this.updateCurrentPage(newPage);
}
},
handlePageSelect(e) {
const newPage = +e.target.dataset.page;
this.updateCurrentPage(newPage);
},
// 通知父组件修改当前页
updateCurrentPage(newPage) {
this.$emit('updateCurrentPage', newPage);
},
},
// 由于数据从服务端获取,需要在当前组件或作为父组件的业务组件中监听初始数据的变化,并触发分页器的初始化
// 同时,用户改变当前页码时,也会触发分页器更新
watch: {
currentPage: {
immediate: true,
handler() {
this.$nextTick(() => {
this.setupPagination(
this.currentPage,
this.totalPages,
this.pagerCount
);
});
},
},
},
标签:newPage,vue,分页,color,start,totalPages,currentPage,基本思路,页码
From: https://www.cnblogs.com/cjc-0313/p/16805657.html