前言
银五金六, 你申哥我又回来了,我一直以为自己狂的所有面试都能通过。所以才有了下面的一幕。。。这次是一个字节跳动的二面,晚上8点开始,首先是一段自我介绍,我叫申哥,21年来自XX学院,熟悉vue2,vue3源码,各种组件库源码,webpack源码,一阵狂吹之后,首先让我写了一个列表,样式类似下面这种:
我天,字节跳动就考这么so easy 的东西? 这还不进大厂?三下五除,我便写出来了。之后是一个分页选择器,我写着写着就把自己写哭了,太tmd难了!!
需求
大概让我实现一个100页,显示第一页和最后一页,有省略号的分页器,效果如下:
准备阶段
楼主目前主要使用vue项目,所以只讲vue实现不过你是react用户大体思路可以参考一下
- element UI 分页器 Demo 地址 element.eleme.cn/#/zh-CN/com…
- element ui 源码 地址: github.com/ElemeFE/ele…
- 随便找一个vue项目或者 使用 vue create App 命令自己用脚手架搭建一个项目,方便使用vue项目
分页器如何使用
<el-pagination
:current-page="currentPage4"
:page-size="100"
:total="400">
</el-pagination>
pagination分页器的属性比较多,我们就不全都实现了,只是实现它的核心部分,就是中间的切换页码的按钮
主要使用的属性
| 属性名 | 含义 |
|---|---|
| pageSize | 每页显示条目个数 |
| total | 总条目数 |
| pageCount | 总页数 |
| pagerCount | 页码按钮的数量,当总页数超过该值时会折叠 |
| currentPage | 当前页数 |
| disabled | 是否禁用 |
实现分页器
步骤一: 分析分页器结构
步骤二: 首页和尾页
第一页和尾页一定会显示, onPagerClick 事件之所以写在Ul上,是利用了事件委托,使当点击某个li的时候都会触发onPagerClick,为了简化内容,css写在最后
<template>
<ul @click="onPagerClick" class="el-pager">
<!-- 首页 -->
<li
:class="{ active: currentPage === 1, disabled }"
v-if="pageCount > 0"
class="number"
>
1
</li>
<!-- 最后一页 -->
<li
:class="{ active: currentPage === pageCount, disabled }"
class="number"
v-if="pageCount > 1"
>
{{ pageCount }}
</li>
</ul>
</template>
步骤三: 前省略号和后面省略号显示判断
pageCount 总页数, pagerCount 页码按钮的数量,当总页数超过该值时会折叠,即pageCount > pagerCount
// 当前页大于按钮数量的一半,左面的省略号显示
// 这里举个例子,比如最多展示7个按钮(pagerCount为7),则只要当前页大于3的时候,左面的省略号显示
if (currentPage > (pagerCount + 1) / 2) {
showPrevMore = true
}
// 当前页比中间页码小的时候,右面的省略号一定显示
if (currentPage < pageCount - halfPagerCount) {
showNextMore = true
}
// 页码数量 > 页码按钮的数量,当总页数超过该值时会折叠
// 此时需要显示折叠按钮
/* 处理省略号-start */
if (pageCount > pagerCount) {
// 当前页大于按钮数量的一半,左面的省略号显示
// 这里举个例子,比如最多展示7个按钮(pagerCount为7),则只要当前页大于3的时候,左面的省略号显示
if (currentPage > (pagerCount + 1) / 2) {
showPrevMore = true
} // 当前页比中间页码小的时候,右面的省略号一定显示
if (currentPage < pageCount - halfPagerCount) {
showNextMore = true
}
}
步骤四:省略号中间分页按钮
/* 处理剩余显示的按钮-start */
// array数组为要显示的页码数字的集合
const array = []
// 当前页在页码的右半部分
// 只显示左面的省略号,则array为...8, 9, 10,直到最大页码数
if (showPrevMore && !showNextMore) {
const startPage = pageCount - (pagerCount - 2)
for (let i = startPage; i < pageCount; i++) {
array.push(i)
}
//
// 只显示右面的省略号 当前页在页码的左半部分
} else if (!showPrevMore && showNextMore) {
// 这种情况比较简单2, 3, 4...
for (let i = 2; i < pagerCount; i++) {
array.push(i)
}
// 左右两边省略号都显示
} else if (showPrevMore && showNextMore) {
// Math.floor(pagerCount / 2) 的意思是取中间分页按钮的左右一边, -1 是因为要减去第一页和最后一页
const offset = Math.floor(pagerCount / 2) - 1
for (let i = currentPage - offset; i <= currentPage + offset; i++) {
array.push(i)
}
} else {
// 没有省略号,则依次显示分页按钮
for (let i = 2; i < pageCount; i++) {
array.push(i)
}
}
步骤五:利用事件冒泡写按钮点击事件
ui
<ul @click="onPagerClick" class="el-pager">
<!-- 首页 -->
<li
:class="{ active: currentPage === 1, disabled }"
v-if="pageCount > 0"
class="number"
>
js
methods: {
onPagerClick(e) {
const text = e.target.innerText
if (isNaN(text)) {
} else {
const page = Number(text)
console.log(page)
this.$emit("change", page)
}
},
},
项目源码
<template>
<ul @click="onPagerClick" class="el-pager">
<!-- 首页 -->
<li
:class="{ active: currentPage === 1, disabled }"
v-if="pageCount > 0"
class="number"
>
1
</li>
<!-- 上面的更多 -->
<li
class="el-icon more btn-quickprev"
:class="[quickprevIconClass, { disabled }]"
v-if="showPrevMore"
@mouseenter="onMouseenter('left')"
@mouseleave="quickprevIconClass = 'el-icon-more'"
></li>
<!-- 页码 -->
<li
v-for="pager in pagers"
:key="pager"
:class="{ active: currentPage === pager, disabled }"
class="number"
>
{{ pager }}
</li>
<!-- 下面的更多 -->
<li
class="el-icon more btn-quicknext"
:class="[quicknextIconClass, { disabled }]"
v-if="showNextMore"
@mouseenter="onMouseenter('right')"
@mouseleave="quicknextIconClass = 'el-icon-more'"
></li>
<!-- 最后一页 -->
<li
:class="{ active: currentPage === pageCount, disabled }"
class="number"
v-if="pageCount > 1"
>
{{ pageCount }}
</li>
</ul>
</template>
<script type="text/babel">
export default {
name: "ElPager",
props: {
currentPage: Number,
pageCount: Number,
pagerCount: Number,
disabled: Boolean,
},
watch: {
showPrevMore(val) {
if (!val) this.quickprevIconClass = "el-icon-more"
},
showNextMore(val) {
if (!val) this.quicknextIconClass = "el-icon-more"
},
},
methods: {
onPagerClick(event) {
const target = event.target
if (target.tagName === "UL" || this.disabled) {
return
}
let newPage = Number(event.target.textContent)
const pageCount = this.pageCount
const currentPage = this.currentPage
const pagerCountOffset = this.pagerCount - 2
if (target.className.indexOf("more") !== -1) {
if (target.className.indexOf("quickprev") !== -1) {
newPage = currentPage - pagerCountOffset
} else if (target.className.indexOf("quicknext") !== -1) {
newPage = currentPage + pagerCountOffset
}
}
/* istanbul ignore if */
if (!isNaN(newPage)) {
if (newPage < 1) {
newPage = 1
}
if (newPage > pageCount) {
newPage = pageCount
}
}
if (newPage !== currentPage) {
this.$emit("change", newPage)
}
},
onMouseenter(direction) {
if (this.disabled) return
if (direction === "left") {
this.quickprevIconClass = "el-icon-d-arrow-left"
} else {
this.quicknextIconClass = "el-icon-d-arrow-right"
}
},
},
computed: {
pagers() {
const pagerCount = this.pagerCount
const halfPagerCount = (pagerCount - 1) / 2
const currentPage = Number(this.currentPage)
const pageCount = Number(this.pageCount)
let showPrevMore = false
let showNextMore = false
// 页码数量 > 页码按钮的数量,当总页数超过该值时会折叠
// 此时需要显示折叠按钮
if (pageCount > pagerCount) {
if (currentPage > pagerCount - halfPagerCount) {
showPrevMore = true
}
if (currentPage < pageCount - halfPagerCount) {
showNextMore = true
}
}
const array = []
if (showPrevMore && !showNextMore) {
const startPage = pageCount - (pagerCount - 2)
for (let i = startPage; i < pageCount; i++) {
array.push(i)
}
} else if (!showPrevMore && showNextMore) {
for (let i = 2; i < pagerCount; i++) {
array.push(i)
}
} else if (showPrevMore && showNextMore) {
const offset = Math.floor(pagerCount / 2) - 1
for (
let i = currentPage - offset;
i <= currentPage + offset;
i++
) {
array.push(i)
}
} else {
for (let i = 2; i < pageCount; i++) {
array.push(i)
}
}
this.showPrevMore = showPrevMore
this.showNextMore = showNextMore
return array
},
},
data() {
return {
current: null,
showPrevMore: false,
showNextMore: false,
quicknextIconClass: "el-icon-more",
quickprevIconClass: "el-icon-more",
}
},
}
</script>
<style scoped></style>