文章目录
- 前言
- 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)// 计算总页数,并确保总页数大于0const 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><inputtype="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";
// 使用
<PaginationcurrentPage={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)手机端
总结
下一篇讲【开始首页编码教学】。关注本栏目,将实时更新。