动手打造antd组件之pagination

873 阅读3分钟

本文正在参加「金石计划」

一:引言

本篇文章仿写antd组件库的pagination组件,也就是我们常用的分页组件。文章重点是模拟pagination组件的内部功能实现。

二:组件分析

观察下图,我们设计一个可配置的分页组件,其配置项包含如下

image.png

  • className 类名
  • total 总数量
  • pageSize 一页的容量
  • pageNumber 页码
  • leftText 左分页控制组件
  • rightText 右分页控制组件
  • showQuickJumper 是否支持快速跳转
  • onChange 分页时触发回调函数

三:问题拆解

在设计组件前,我们可以先考子问题,最后组装起来就实现了目标组件。

问题1:分页左右切换如何控制?

  • 首先我们需要为分页组件传递一个total总数,分页组件根据总数/页容量自动计算展示的分页盒子数,通过我们只需通过index标记当前展示的页码,当点击左右分页时更新index就可。注意左右切换边界条件。

问题2:分页的快速跳转如何实现?

分页的快速跳转十分容易,只需要监听输入框输入的数字,然后更新index页码即可。

问题3:分页过多时,省略号逻辑如何处理?(重难点,比较复杂)

这个问题是分页组件的难点,难点,我也是在andt官网不停的切换才总结出对应规律,其规则主要是

  1. 页码总数<=7时全部展示

  2. 否则执行

    • 当前页码<=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}/>
 

4.gif

演示2:添加自动跳转

    <Pagination total={80} showQuickJumper/>
    
    

6.gif

演示3:添加左右跳转配置

   <Pagination total={80} showQuickJumper leftText="左" rightText="右"/>
   
   

演示4:添加跳转回调函数

   <Pagination total={80} showQuickJumper onChange={(e)=>console.log(e)}/>

6.gif

演示5:分页数超过7时省略逻辑

<Pagination total={200}/>

7.gif

总结

今天pagination组件到此结束,希望大家多多支持,我们下一个组件见。