React+TS前台项目实战(十六)-- 全局常用组件Pagination封装

news2024/11/25 11:56:25

文章目录

  • 前言
  • Pagination组件
    • 1. 功能分析
    • 2. 代码+详细注释
    • 3. 使用方式
    • 4. 效果展示 [PC端&手机端]
  • 总结


前言

在上篇文章中,我们封装了表格组件Table,本文则继续封装配套使用的分页器组件。想看Table表格组件的,可自行查看全局常用组件Table封装


Pagination组件

1. 功能分析

(1)渲染一个带有分页功能的用户界面,包括导航到第一页、上一页、下一页和最后一页的按钮,并实现响应式布局
(2)提供一个输入框,允许用户手动输入页码,并提供一个按钮用于跳转到指定页码
(3)通过调用onChange prop来更新页码,并根据当前页数更新UI显示
(4)通过自定义的useIsMobile hook来判断设备类型,根据设备类型显示不同效果
(5)使用国际化语言显示文案

2. 代码+详细注释

// @/components/Pagination/index.tsx
import { useState, FC } from 'react'
import { useTranslation } from 'react-i18next'
import { PaginationLeft, PaginationRight, PaginationContainer } from './styled'
import { useIsMobile } from '@/hooks'
import Button from '@/components/Button'
import LeftArrow from './left_arrow.png'
import RightArrow from './right_arrow.png'
import DisLeftArrow from './disabled_left_arrow.png'
import DisRightArrow from './disabled_right_arrow.png'

// 组件的属性类型
type Props = {
  currentPage: number // 当前页码
  total: number // 总页数
  gotoPage?: number // 跳转页码,默认为当前页码加一
  onChange: (page: number, size?: number) => void // 页码改变时的回调函数
  className?: string // 组件额外的class名
  pageSize?: number // 每页显示条目数
  pageSizes?: number[] // 每页显示条目数的可选项
}

/**
 * 分页组件
 * @param currentPage 当前页码
 * @param total 总页数
 * @param gotoPage 跳转页码,默认为当前页码加一
 * @param onChange 页码改变时的回调函数
 * @param className 组件额外的class名
 * @param pageSize 每页显示条目数
 * @param pageSizes 每页显示条目数的可选项
 */
const Pagination: FC<Props> = ({
  currentPage,
  total,
  gotoPage = currentPage === total ? total : currentPage + 1,
  onChange,
  className,
  pageSize,
  pageSizes,
}) => {
  // 判断是否是移动端
  const isMobile = useIsMobile()
  // 获取i18n翻译函数
  const { t } = useTranslation()
  // 创建一个state,用于存储输入框的值
  const [inputVal, setInputVal] = useState(gotoPage)
  // 计算总页数,并确保总页数大于0
  const totalCount = Math.max(total, 1)
  // 计算当前页数,并确保当前页数在范围内
  const current = Math.min(Math.max(currentPage, 1), total)
  // 移动端中间描述文本
  const mobileMiddleDescrip = `${t('pagination.total_page')} ${totalCount} ${t('pagination.end_page')}`
  // PC端中间描述文本
  const pcMiddleDescrip = `${t('pagination.current_page')} ${current} ${t('pagination.of_page')} ${totalCount} ${t(
    'pagination.end_page',
  )}`
  // 页码更新
  const changePage = (page: number) => {
    if (page && page >= 1 && page <= totalCount) {
      setInputVal(Math.min(page + 1, totalCount))
      onChange(page)
    }
  }
  return (
    <PaginationContainer className={className}>
      <PaginationLeft isFirstPage={current === 1} isLastPage={current === totalCount}>
        <Button className="first-button" onClick={() => changePage(1)}>
          {t('pagination.first')}
        </Button>
        <Button className="left-button" onClick={() => changePage(current - 1)}>
          <img src={current === 1 ? DisLeftArrow : LeftArrow} alt="左侧按钮" />
        </Button>

        {!isMobile && <span className="middle-discrip">{pcMiddleDescrip}</span>}
        <Button className="right-button" onClick={() => changePage(current + 1)}>
          <img src={current === totalCount ? DisRightArrow : RightArrow} alt="右侧按钮" />
        </Button>
        {isMobile && <span className="middle-discrip">{mobileMiddleDescrip}</span>}

        <Button className="last-button" onClick={() => changePage(totalCount)}>
          {t('pagination.last')}
        </Button>
      </PaginationLeft>
      <PaginationRight>
        <span className="page-label">{t('pagination.page')}</span>
        <input
          type="text"
          className="page-input"
          pattern="[0-9]*"
          value={inputVal}
          onChange={event => {
            const pageNo = parseInt(event.target.value, 10)
            setInputVal(Number.isNaN(pageNo) ? 0 : Math.min(pageNo, totalCount))
          }}
        />
        <Button className="page-go-to" onClick={() => changePage(inputVal)}>
          {t('pagination.goto')}
        </Button>
      </PaginationRight>
    </PaginationContainer>
  )
}

export default Pagination
--------------------------------------------------------------------------------------------------------------
// @/components/Pagination/styled.tsx
import styled from 'styled-components'
import variables from '../../styles/variables.module.scss'
export const PaginationContainer = styled.div`
  display: flex;
  flex-direction: row;
  padding: 10px 20px;
  background: #fff;
`
export const PaginationLeft = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 3;
  font-size: 14px;
  @media (max-width: ${variables.mobileBreakPoint}) {
    padding-left: 0;
    justify-content: flex-start;
  }
  .first-button,
  .last-button {
    padding: 4px 8px;
    border-radius: 5px;
    background: #f5f5f5;
    cursor: pointer;
    color: ${({ isFirstPage }: { isFirstPage: boolean }) => (isFirstPage ? '#969696' : '#000000')};
    cursor: ${({ isFirstPage }: { isFirstPage: boolean }) => (isFirstPage ? 'pointer' : 'auto')};
    &:hover {
      background: #999;
    }

    @media (max-width: ${variables.mobileBreakPoint}) {
      display: none;
    }
  }
  .left-button,
  .right-button {
    margin-left: 20px;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 5px;
    background: #f5f5f5;
    cursor: ${({ isFirstPage }: { isFirstPage: boolean }) => (isFirstPage ? 'pointer' : 'auto')};
    &:hover {
      background: #999;
    }
    img {
      width: 8px;
    }
    @media (max-width: ${variables.mobileBreakPoint}) {
      margin-left: 5px;
    }
  }
  .right-button {
    cursor: ${({ isLastPage }: { isLastPage: boolean }) => (isLastPage ? 'pointer' : 'auto')};
  }

  .last-button {
    margin-left: 20px;
    color: ${({ isLastPage }: { isLastPage: boolean }) => (isLastPage ? '#969696' : '#000000')};
    cursor: ${(props: { isFirstPage: boolean; isLastPage: boolean }) => (props.isLastPage ? 'none' : 'auto')};
  }
  .middle-discrip {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 30px;
    background: #f5f5f5;
    border-radius: 5px;
    font-size: 12px;
    padding: 0 12px;
    margin-left: 20px;
    white-space: nowrap;

    @media (max-width: ${variables.mobileBreakPoint}) {
      background: #fff;
      border-radius: 0;
      margin: 0 5px;
      padding: 0;
    }
  }
`

export const PaginationRight = styled.div`
  display: flex;
  align-items: center;
  flex: 2;
  font-size: 14px;

  @media (max-width: ${variables.mobileBreakPoint}) {
    justify-content: flex-end;
  }

  .page-input {
    width: 120px;
    height: 30px;
    border-radius: 5px;
    background-color: rgb(245, 245, 245);
    color: rgb(204, 204, 204);
    margin-right: 40px;
    outline: none;
    border: none;
    padding-left: 10px;

    @media (max-width: ${variables.mobileBreakPoint}) {
      width: 60px;
      margin-right: 20px;
      font-size: 12px;
    }
  }

  .page-label {
    margin-right: 20px;

    @media (max-width: ${variables.mobileBreakPoint}) {
      display: none;
    }
  }

  .page-go-to {
    height: 30px;
    line-height: 30px;
    padding: 0 10px;
    background: #f5f5f5;
    border-radius: 6px;
    border: none;
    outline: none;
    cursor: pointer;

    &:hover {
      background: #999;
    }

    @media (max-width: ${variables.mobileBreakPoint}) {
      font-size: 12px;
    }
  }
`

3. 使用方式

// 引入组件
import Pagination from "@/components/Pagination";
// 使用
<Pagination
  currentPage={1}
  PageSize={10}
  PageSizes={[10, 20, 30]}
  total={data?.total ?? 1}
  onChange={handlePageChange}
/>
const handlePageChange = (page: number) => {
  console.log('handlePageChange', page)
}

4. 效果展示 [PC端&手机端]

(1)PC端
在这里插入图片描述
(2)手机端
在这里插入图片描述


总结

下一篇讲【开始首页编码教学】。关注本栏目,将实时更新。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1852577.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【pytorch04】创建Tensor

numpy中的数据创建tensor 数据已经在numpy中了&#xff0c;将numpy中的数据转到tensor中来&#xff0c;因为我们将使用tensor在GPU上进行加速运算 从NUMPY导入的FLOAT其实是DOUBLE类型 list中的数据创建tensor FloatTensor()和大写的Tensor()接收的是shape&#xff08;即数据的…

敏捷开发笔记(第7章节)--什么是敏捷设计

目录 1&#xff1a;PDF上传链接 7.1: 软件出了什么错 7.2: 设计的臭味--腐化软件的气味 7.2.1: 什么激化了软件的腐化 7.2.2: 敏捷团体不允许软件腐化 7.3: “copy”程序 1: 初始设计 2: 需求在变化 3: 得寸进尺 4: 期望变化 7.3.1: “copy”程序的敏捷设计 7.3.2:…

【职场人】职场故事:与邀功精的共舞

在我的职业生涯中&#xff0c;我遇到过一位特别引人注目的同事&#xff0c;我们都叫他李经理。他的工作能力并不差&#xff0c;但他有一个习惯&#xff0c;那就是喜欢邀功。他的这种习惯&#xff0c;不仅让我印象深刻&#xff0c;也让我在合作中学会了不少东西。 恶心的四件事 …

MySQL学习笔记-进阶篇-视图和存储过程

四、视图和存储过程 视图 存储过程 基本语法 创建 CREATE PROCEDURE ([参数列表]) BEGIN --SQL END; 调用 CALL 存储过程名&#xff08;[参数列表]&#xff09; 查看 --查看指定数据库的存储过程及状态信息 SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SHCEMA…

AI-智能体

什么是 AI 智能体&#xff1f; 「AI 智能体」这个术语并没有真正被定义&#xff0c;对智能体究竟是什么也存在很多的争议。 AI 智能体可以定义为「一个被赋予行动能力的 LLM&#xff08;通常在 RAG 环境中进行函数调用&#xff09;&#xff0c;以便在环境中对如何执行任务做出…

第一次接触Swing

学习java版的HslCommunication发现使用的是Swing&#xff0c;所以了解了一下~ 了解&#xff1a; Swing是Java的标准库&#xff08;Java Foundation Classes, JFC&#xff09;的一部分&#xff0c;用于构建桌面应用程序的图形用户界面&#xff08;GUI&#xff09;。它是Java AWT…

华为某员工爆料:三年前985本科起薪30万,现在硕士起薪还是30w,感慨互联网行情变化

“曾经的30万年薪&#xff0c;是985本科学历的‘标配’&#xff0c;如今硕士也只值这个价&#xff1f;” 一位华为员工的爆料&#xff0c;揭开了互联网行业薪资变化的冰山一角&#xff0c;也引发了不少人的焦虑&#xff1a;互联网人才“通货膨胀”的时代&#xff0c;真的结束了…

Java-异常:不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获

Java-异常&#xff1a;不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获 Java-异常&#xff1a;不恰当的异常转换、不充分的日志记录、过度或不当的异常捕获一、前期准备二、案例分析1、不恰当的异常转换2、不充分日志记录3、过度或不当的异常捕获 三、正确处理方式1…

2024年6月计算机视觉论文推荐:扩散模型、视觉语言模型、视频生成等

6月还有一周就要结束了&#xff0c;我们今天来总结2024年6月上半月发表的最重要的论文&#xff0c;重点介绍了计算机视觉领域的最新研究和进展。 Diffusion Models 1、Autoregressive Model Beats Diffusion: Llama for Scalable Image Generation LlamaGen&#xff0c;是一个…

C++20中的Feature Test Mocros

C20定义了一组预处理器宏&#xff0c;用于测试各种语言和库的feature。 Feature Test Mocros(特性测试宏)是C20中引入的一种强大机制&#xff0c;用于应对兼容性问题。Feature Test Mocros作为预处理器指令(preprocessor directives)出现&#xff0c;它使你能够在编译过程中仔细…

Edge 浏览器退出后,后台占用问题

Edge 浏览器退出后&#xff0c;后台占用问题 环境 windows 11 Microsoft Edge版本 126.0.2592.68 (正式版本) (64 位)详情 在关闭Edge软件后&#xff0c;查看后台&#xff0c;还占用很多系统资源。实在不明白&#xff0c;关了浏览器还不能全关了&#xff0c;微软也学流氓了。…

计算机网络期末

1、IP 地址为:192.168.0.254,它的子网掩码应该为( ) A.255.255.255.0 B.255.255.254.0 C.255.255.252.0 D.255.255.0.0 2、最容易产生网络可靠性瓶颈问题的拓扑构型是&#xff08; &#xff09;。 A 总线型 B 星型 C 环型 D 网状型 3、HTTP 就是电子邮件阅读协议&#xff0…

Android开发实用必备的几款插件,提高你的开发速度

1.GsonFormat 使用方法&#xff1a;快捷键AltS也可以使用AltInsert选择GsonFormat&#xff0c;作用&#xff1a;速将json字符串转换成一个Java Bean&#xff0c;免去我们根据json字符串手写对应Java Bean的过程。 2.ButterKnife Zelezny 又叫黄油刀 使用方法&#xff1a;CtrlS…

Java程序之动物声音“模拟器”

题目&#xff1a; 设计一个“动物模拟器”&#xff0c;希望模拟器可以模拟许多动物的叫声和行为&#xff0c;要求如下&#xff1a; 编写接口Animal&#xff0c;该接口有两个抽象方法cry()和getAnimalName()&#xff0c;即要求实现该接口的各种具体的动物类给出自己的叫声和种类…

PostgreSQL如何定义缓冲区管理器?

目录 一、PostgreSQL是什么二、缓冲区管理器介绍三、缓冲区管理器的应用场景四、如何定义缓冲区管理器 一、PostgreSQL是什么 PostgreSQL是一种高级的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它以其稳定性、可靠性和高度可扩展性而闻名。它最初由加…

史上最强面试题,拿大厂京东,阿里offer全靠它。程序员必看。

答&#xff1a;对于short s1 1; s1 s1 1;由于1是int类型&#xff0c;因此s11运算结果也是int 型&#xff0c;需要强制转换类型才能赋值给short型。而short s1 1; s1 1;可以正确编译&#xff0c;因为s1 1;相当于s1 (short)(s1 1);其中有隐含的强制类型转换。 #6、Java 有…

Trimesh介绍及基本使用

Trimesh介绍及基本使用 Trimesh是一个纯Python 工具库&#xff08;支持3.7版本以上&#xff09;&#xff0c;用于加载和使用三角形Mesh网格&#xff0c;支持多种常见的三维数据格式&#xff0c;如二进制/文本格式的STL、Wavefront OBJ、二进制/文本格式的PLY、GLTF/GLB 2.0、3…

食谱API

在当今追求健康与美味完美结合的时代&#xff0c;获取准确而丰富的食品和营养信息变得至关重要。无论是热衷于探索世界各地美食的烹饪爱好者&#xff0c;还是对自身饮食营养严格把控的健康追求者&#xff0c;都离不开可靠的资源。幸运的是&#xff0c;现在有诸如 TheMealDB 和 …

测试辅助工具(抓包工具)的使用2 之 抓包工具的基本用法

1.过滤设置: Filters- --- 勾选use Filters- --- 下拉选择show only the following hosts ---- 输入域名或者ip地址(多个地址用;隔开) --- 点击action(Run filterset now) 2.删除数据 方式一:点击Remove all 方式二: 黑窗口输入cls,回车 删除一条数据:选中数据---右键选择Rem…

LabVIEW电路板故障诊断系统

基于LabVIEW软件开发的电路板故障诊断系统&#xff0c;涵盖功能测试、性能测试和通讯测试等多个方面。系统集成了多种硬件设备&#xff0c;包括NI PXI-1033机箱、NI PXI-4071数字万用表、NI PXI-4130电源模块、NI PXI-8512 CAN模块等&#xff0c;通过模块化设计实现了对电路板的…