本文正在参加「金石计划」
一:引言
本篇文章仿写antd组件库的pagination组件,也就是我们常用的分页组件。文章重点是模拟pagination组件的内部功能实现。
二:组件分析
观察下图,我们设计一个可配置的分页组件,其配置项包含如下
- className 类名
- total 总数量
- pageSize 一页的容量
- pageNumber 页码
- leftText 左分页控制组件
- rightText 右分页控制组件
- showQuickJumper 是否支持快速跳转
- onChange 分页时触发回调函数
三:问题拆解
在设计组件前,我们可以先考子问题,最后组装起来就实现了目标组件。
问题1:分页左右切换如何控制?
- 首先我们需要为分页组件传递一个total总数,分页组件根据总数/页容量自动计算展示的分页盒子数,通过我们只需通过index标记当前展示的页码,当点击左右分页时更新index就可。注意左右切换边界条件。
问题2:分页的快速跳转如何实现?
分页的快速跳转十分容易,只需要监听输入框输入的数字,然后更新index页码即可。
问题3:分页过多时,省略号逻辑如何处理?(重难点,比较复杂)
这个问题是分页组件的难点,难点,我也是在andt官网不停的切换才总结出对应规律,其规则主要是
-
页码总数<=7时全部展示
-
否则执行
- 当前页码<=4时,展示前6个+省略+最后一个
- 当前页码>=5 且 <= 总页码-4时,展示第1个+省略+当前页码为中心的5个+省略+最后一个
- 当前页码> 总页码-4时,展示第1个+省略+后6个
五:pagination代码实现
总体代码除了分页过多有省略号时逻辑比较复杂,其余逻辑都十分简单。
import React, { useEffect, useState } from "react";
interface PaginationProps {
pageSize?:number
pageNumber?:number
total?:number,
leftText?:string,
rightText?:string
showQuickJumper?:boolean
onChange?:(e:number)=>void
}
const Pagination = (props:PaginationProps={pageSize:10,pageNumber:1,total:0}) => {
const {pageSize,pageNumber,total,onChange,showQuickJumper,leftText,rightText} = props;
const [paginationPageSize,setPaginationPageSize] = useState(pageSize);//页面容量
const [paginationPageNumber,setPaginationPageNumber] = useState(pageNumber);//页面当前编号
const [paginationTotal,setPaginationTotal] = useState(total);//总数量
const [totalPageSize,setTotalPageSize] = useState(0);//总页码
useEffect(()=>{
setTotalPageSize(Math.ceil(paginationTotal!/paginationPageSize!));
},[])//paginationPageNumber
const initPage = () => {
if(totalPageSize<=7) {
return (
Array(totalPageSize).fill(1).map((item,index)=>(
<div
className={paginationPageNumber===(index+1)?"pagination_item pagination_item_actived":"pagination_item"}
key={index+Math.random()}
>
{index+1}
</div>
))
)
}
if(paginationPageNumber!<=4) {
//前6个+省略+最后一个
return (
<>
{
Array(6).fill(1).map((item,index)=>(
<div
className={paginationPageNumber===(index+1)?"pagination_item pagination_item_actived":"pagination_item"}
key={index+Math.random()}
>
{index+1}
</div>
))
}
<div
className='pagination_simple'
onClick={()=>{
if(paginationPageNumber!+5>=totalPageSize) {
setPaginationPageNumber(totalPageSize);
onChange&&onChange!(totalPageSize);
}else {
setPaginationPageNumber(paginationPageNumber!+5);
onChange&&onChange!(paginationPageNumber!+5);
}
}}
>
...
</div>
<div
className={paginationPageNumber===(totalPageSize)?"pagination_item pagination_item_actived":"pagination_item"}
key={paginationPageNumber!+Math.random()}
>
{totalPageSize}
</div>
</>
)
}
//第1个+省略+当前页码前后5个+省略+最后一个
if(paginationPageNumber!>=5 && paginationPageNumber!<=totalPageSize-4) {
return (
<>
<div
className={paginationPageNumber===(1)?"pagination_item pagination_item_actived":"pagination_item"}
key={0+Math.random()}
>
1
</div>
<div
className='pagination_simple'
onClick={()=>{
if(paginationPageNumber!-5<=1) {
setPaginationPageNumber(1);
onChange&&onChange!(1);
}else {
setPaginationPageNumber(paginationPageNumber!-5);
onChange&&onChange!(paginationPageNumber!-5);
}
}}
>
...
</div>
{
Array(5).fill(paginationPageNumber!-2).map((item,index)=>(
<div
className={paginationPageNumber===(item+index)?"pagination_item pagination_item_actived":"pagination_item"}
key={item-Math.random()}
>
{item+index}
</div>
))
}
<div
className='pagination_simple'
onClick={()=>{
if(paginationPageNumber!+5>=totalPageSize) {
setPaginationPageNumber(totalPageSize);
onChange&&onChange!(totalPageSize);
}else {
setPaginationPageNumber(paginationPageNumber!+5);
onChange&&onChange!(paginationPageNumber!+5);
}
}}
>
...
</div>
<div
className={paginationPageNumber===(totalPageSize)?"pagination_item pagination_item_actived":"pagination_item"}
key={totalPageSize-Math.random()}
>
{totalPageSize}
</div>
</>
)
}
//展示第1个+省略+后6个
return (
<>
<div
className={paginationPageNumber===(1)?"pagination_item pagination_item_actived":"pagination_item"}
key={0+Math.random()}
>
1
</div>
<div
className='pagination_simple'
onClick={()=>{
if(paginationPageNumber!-5<=1) {
setPaginationPageNumber(1);
onChange&&onChange!(1);
}else {
setPaginationPageNumber(paginationPageNumber!-5);
onChange&&onChange!(paginationPageNumber!-5);
}
}}
>
...
</div>
{
Array(6).fill(totalPageSize-5).map((item,index)=>(
<div
className={paginationPageNumber===(item+index)?"pagination_item pagination_item_actived":"pagination_item"}
key={item-Math.random()}
>
{item+index}
</div>
))
}
</>
)
}
return (
<div className='pagination'>
<div
onClick={()=>{
if(paginationPageNumber!==1)
{
setPaginationPageNumber(paginationPageNumber!-1);
onChange&&onChange!(paginationPageNumber!-1);
}
}}
className={paginationPageNumber===1?"pagination_left pagination_left_disabled":"pagination_left"}
>
{leftText}
</div>
{
initPage()
}
<div
className={paginationPageNumber===Math.ceil(paginationTotal!/paginationPageSize!)?"pagination_right pagination_right_disabled":"pagination_right"}
onClick={()=>{
if(paginationPageNumber!==Math.ceil(paginationTotal!/paginationPageSize!)) {
setPaginationPageNumber(paginationPageNumber!+1);
onChange&&onChange!(paginationPageNumber!+1);
}
}}
>
{rightText}
</div>
<div className="pagination_info">每页{paginationPageSize}条</div>
{
showQuickJumper && (
<div className="pagination_jump">
<span>跳至</span>
<input type="text" onKeyDown={(e)=>{
if(e.code==='Enter'){
//获取输入的数字进行跳转
if(Number(e.currentTarget?.value)>=1 && Number(e.currentTarget?.value)<=totalPageSize!) {
setPaginationPageNumber(Number(e.currentTarget?.value));
onChange && onChange(Number(e.currentTarget?.value))
}
}
}}/>
<span>页</span>
</div>
)
}
</div>
)
}
Pagination.defaultProps = {
pageNumber:1,
pageSize:10,
total:0,
showQuickJumper:false,
leftText:'<',
rightText:'>'
}
export default Pagination;
六:功能演示
演示1:默认
<Pagination total={60}/>
演示2:添加自动跳转
<Pagination total={80} showQuickJumper/>
演示3:添加左右跳转配置
<Pagination total={80} showQuickJumper leftText="左" rightText="右"/>
演示4:添加跳转回调函数
<Pagination total={80} showQuickJumper onChange={(e)=>console.log(e)}/>
演示5:分页数超过7时省略逻辑
<Pagination total={200}/>
总结
今天pagination组件到此结束,希望大家多多支持,我们下一个组件见。
原文链接:https://juejin.cn/post/7212603037667819581 作者:前端兰博