概述
对于前端实际项目开发,相信都用过分页组件,各种组件库都有提供现成的组件,对于分页组件,大多数的组件库的分页组件的功能基本是类似的,不同的基本就是样式的些许不同,我们使用的时候,直接拿来就能用,对于这么常见的组件,我觉得还是有必要自己去实现一下的,对于我们自己组建编码能还是有提高的,现在记录实现分页组件的过程。
最终效果
注意事项
对于分页组件,我们要去自己写一个,一定要考虑以下几个问题:
- 分页分页,当然需要数据条数,以及每页有多少条,这样才能通过公式计算总条数/每页条数=总页数。
- 可能会遇到总条数/每页条数除不进的问题,那不用问,我们肯定需要将步骤1的结果向上取整。
- 分页组件应该支持上下翻页,页数过多自动折叠以及切换当前页的条数。
- 最后就是自定义事件,我们分页组件主要考虑两个自定义事件(因为我们实际工作中主要用到这两个事件去更新数据):
- 当前页码变更
- 每页条数变更
难点
分页组件的难点主要就一个地方:怎么根据总条数和当前页码动态折叠过多的分页。
页码过多解决方案
当前实现的分页组件关于折叠过多页数分为一下情况(自己实现的话可以根据自己需求改):
- 总页码数<5的直接返回
- 总页码数>5页,当时当前页码数<=3
- 总页数-当前页数<=3
- 排除其他的,剩下的就是有向前和向后的操作点
组件用法
具体用户其实和组件库的用法都差不多,传递的props也大同小异。
App.vue
<template>
<div class="app">
<div class="flex">
<div class="item">
<my-page
:total="1332"
:current="1"
:page-size="10"
:show-total="true"
:show-sizer="true"
@on-change="handlePageChange"
@on-page-size-change="handlePageSizeChange"
:page-size-opts="[10, 20, 30, 40]"
></my-page>
</div>
</div>
</div>
</template>
<script>
import MyPage from "./components/MyPage/MyPage.vue";
export default {
components: { MyPage },
},
methods: {
// 页码变更
handlePageChange(page) {
console.log("当前页码:" + page);
},
// 每页条数变化
handlePageSizeChange(pageSize) {
console.log("每页条数:" + pageSize);
},
},
};
</script>
<style lang="less">
.app {
padding: 20px;
.flex {
display: flex;
.item {
margin: 0 20px;
}
}
}
</style>
具体实现
.components/MyPage/MyPage.vue
<template>
<div class="my-page">
<!-- 总条数 -->
<span class="my-page-total" v-if="showTotal">共{{ total }}条</span>
<ul class="page-list">
<!-- 上一页 -->
<li
:class="['page-list-item', currentPage <= 1 ? 'disbled' : '']"
@click="handleBeforOrAfterClick('before')"
>
<
</li>
<!-- 中间页码数据 -->
<template v-for="(pageItem, index) in pageList">
<!-- 向前5页 -->
<li
:class="['page-list-item', 'page-list-item-prev']"
@click="handlePageItemClick(pageItem, 'prev')"
:key="index"
title="向前5页"
v-if="pageItem == 'prev'"
@mouseover="prevStatus = false"
@mouseleave="prevStatus = true"
>
<i v-if="prevStatus"> ...</i>
<i v-else><<</i>
</li>
<!-- 正常页码 -->
<li
:class="['page-list-item', 'page-list-item-next']"
@click="handlePageItemClick(pageItem, 'next')"
:key="index"
title="向后5页"
v-else-if="pageItem == 'next'"
@mouseenter="nextStatus = false"
@mouseleave="nextStatus = true"
>
<i v-if="nextStatus"> ...</i>
<i v-else>>></i>
</li>
<!-- 向后五页 -->
<li
:class="['page-list-item', currentPage == pageItem ? 'active' : '']"
@click="handlePageItemClick(pageItem)"
:key="index"
:title="pageItem"
v-else
>
{{ pageItem }}
</li>
</template>
<!-- 下一页 -->
<li
:class="[
'page-list-item',
currentPage >= totalPage && totalPage > 0 ? 'disbled' : '',
]"
@click="handleBeforOrAfterClick('after')"
>
>
</li>
</ul>
<!-- 每页条数电梯 -->
<div class="page-size-select" v-if="showSizer">
<select @change="handlePageSizeChange">
<option
:value="pageSizeItem"
v-for="pageSizeItem in pageSizeOpts"
:key="pageSizeItem"
>
{{ pageSizeItem }}条/页
</option>
</select>
</div>
</div>
</template>
<script>
export default {
props: {
// 总页码条数
total: {
type: Number,
default: 0,
},
// 当前页
current: {
type: Number,
default: 1,
},
// 每页条数,默认10条
pageSize: {
type: Number,
default: 10,
},
// 是否显示总条数
showTotal: {
type: Boolean,
default: false,
},
// 每页大小
showSizer: {
type: Boolean,
default: false,
},
// 页码条数下拉
pageSizeOpts: {
type: Array,
default() {
return [10, 20, 30, 40];
},
},
},
data() {
return {
// 当前页码
currentPage: this.current || 1,
// 当前每页条数
currentPageSize: this.pageSize,
// 是否显示向后5条的>>
prevStatus: true,
// 是否显示向前5条的<<
nextStatus: true,
};
},
computed: {
// 根据总条数和每页条数计算得出总页数,记得向上取整
totalPage() {
return Math.ceil(this.total / this.currentPageSize);
},
// 动态根据当前页,重新组装分页列表数据
pageList() {
//情况1: 总页码数<5的直接返回
if (this.totalPage <= 5) {
return this.totalPage;
}
//情况2: 总页码数>5页,当时当前页码数<=3
if (this.currentPage <= 3) {
return [1, 2, 3, "next", this.totalPage];
}
// 情况3:总页数-当前页数<=3
if (this.currentPage > 3 && this.totalPage - this.currentPage <= 2) {
return [
1,
"prev",
this.totalPage - 2,
this.totalPage - 1,
this.totalPage,
];
}
// 情况4:排除其他的,剩下的就是有向前和向后的操作点
return [
1,
"prev",
this.currentPage - 2,
this.currentPage - 1,
this.currentPage,
this.currentPage + 1,
this.currentPage + 2,
"next",
this.totalPage,
];
},
},
methods: {
// 页码项点击
handlePageItemClick(item, type) {
// 重置向上和向下翻五页
this.prevStatus = true;
this.nextStatus = true;
switch (type) {
// 向前5页
case "prev":
this.currentPage -= 5;
if (this.currentPage < 1) {
this.currentPage = 1;
}
break;
// 向后5页
case "next":
this.currentPage += 5;
if (this.currentPage > this.totalPage) {
this.currentPage = this.totalPage;
}
break;
default:
// 默认常规页码点击
if (this.currentPage == item) {
return;
}
this.currentPage = item;
}
// 发布页码变更事件
this.$emit("on-change", this.currentPage);
},
// 上一页和下一页点击
handleBeforOrAfterClick(type) {
switch (type) {
case "before":
this.currentPage--;
if (this.currentPage < 1) {
this.currentPage = 1;
return;
}
break;
case "after":
this.currentPage++;
if (this.currentPage > this.totalPage) {
this.currentPage = this.totalPage;
return;
}
break;
}
// 发布页码变更事件
this.$emit("on-change", this.currentPage);
},
// 每页条数大小变化
handlePageSizeChange(e) {
// value是字符串,记得转成数字
this.currentPageSize = Number(e.target.value);
// 重置当前页码
this.$emit("on-page-size-change", this.currentPageSize);
this.currentPage = 1;
},
},
};
</script>
<style lang="less">
.my-page {
display: flex;
align-items: center;
.page-list {
.page-list-item {
display: inline-block;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
min-width: 32px;
height: 32px;
line-height: 30px;
list-style: none;
text-align: center;
cursor: pointer;
color: #666;
font-family: Arial;
border: 1px solid #dcdee2;
border-radius: 4px;
transition: all 0.2s ease-in-out;
margin-right: 6px;
margin-left: 6px;
&:hover {
border-color: #2d8cf0;
color: #2d8cf0;
}
}
.active.page-list-item {
border-color: #2d8cf0;
color: #fff;
background-color: #2d8cf0;
}
.disbled.page-list-item {
cursor: not-allowed;
background-color: #fff;
color: #dcdee2;
}
.disbled:hover {
border-color: #dcdee2;
}
}
}
</style>
.components/MyPage/index.js
//按需导出
import MyPage from "./MyPage.vue";
export default { MyPage };
总结
其实组件库的很多组件还是很有意思的,可以自己试着去想想那些华丽的组件的怎么实现的,然后通过代码写出来,这对于框架的运用会更加熟练。