Administrator
发布于 2024-10-19 / 10 阅读
0
0

Next.js 零基础教程13 表格分页技术实现方法

书接上回,上一篇说到我们实现了咱内搜索,由于一次搜索的内容可能太多,因此这章将会将如何将内容进行分页。添加分页功能可让用户浏览不同的页面以查看所有发票。让我们看看如何使用 URL 参数实现分页功能,就像搜索一样。

添加分页

首先需要创建一个分页组件,用于承接这个功能,然后在发票页面上面进行引用
编辑/app/invoices/page.tsx页面,将分页注释删除掉

import Pagination from "@/app/ui/invoices/pagination";
import Search from "@/app/ui/search";
import Table from "@/app/ui/invoices/table";
//import { CreateInvoice } from "@/app/ui/invoices/buttons";
import { lusitana } from "@/app/ui/fonts";
import { InvoicesTableSkeleton } from "@/app/ui/skeletons";
import { Suspense } from "react";
import { fetchInvoicesPages } from "@/app/lib/data";

export default async function Page({
  searchParams,
}: {
  searchParams?: {
    query?: string;
    page?: string;
  };
}) {
  const query = searchParams?.query || "";
  const currentPage = Number(searchParams?.page) || 1;
  const totalPages = await fetchInvoicesPages(query);

  return (
    <div className="w-full">
      <div className="flex w-full items-center justify-between">
        <h1 className={`${lusitana.className} text-2xl`}>最近发票</h1>
      </div>
      <div className="mt-4 flex items-center justify-between gap-2 md:mt-8">
        <Search placeholder="搜索发票内容......" />
        {/* <CreateInvoice /> */}
      </div>
      <Suspense key={query + currentPage} fallback={<InvoicesTableSkeleton />}>
        <Table query={query} currentPage={currentPage} />
      </Suspense>
      <div className="mt-5 flex w-full justify-center">
        <Pagination totalPages={totalPages} />
      </div>
    </div>
  );
}

分页
修改之后会报错,因为我们还没有实现/app/ui/invoices/pagination.tsx组件,接下来需要实现分页的组件。

创建文件/app/ui/invoices/pagination.tsx ,编辑

"use client";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import clsx from "clsx";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";

export default function Pagination({ totalPages }: { totalPages: number }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const currentPage = Number(searchParams.get("page")) || 1;

  console.log(pathname);
  console.log(searchParams);
  console.log(currentPage);

  return <div>Pagination</div>;
}

分页
上面这段逻辑是用于获取路径、参数和当前页面数的,接下来,在组件中创建一个名为的新函数createPageURL。与搜索类似,您将使用它URLSearchParams来设置新页码并pathName创建 URL 字符串。

createPageURL创建当前搜索参数的实例。然后,它将“page”参数更新为提供的页码。最后,它使用路径名和更新的搜索参数构建完整的URL。组件的其余部分<Pagination>处理样式和不同状态(第一个、最后一个、活动、禁用等)。我们不会在本课程中详细介绍,但您可以随意查看代码以了解createPageURL调用的位置。

最后,当用户输入新的搜索查询时,您需要将页码重置为1。您可以通过更新组件handleSearch中的函数来执行此操作<Search>,编辑 /app/ui/search.tsx 页面

完成这一切之后,我们切换到/app/ui/invoices/pagination.tsx/app/lib/utils.ts 完善页面的UI功能
编辑/app/lib/utils.ts,添加下面的代码

export const generatePagination = (currentPage: number, totalPages: number) => {
  // If the total number of pages is 7 or less,
  // display all pages without any ellipsis.
  if (totalPages <= 7) {
    return Array.from({ length: totalPages }, (_, i) => i + 1);
  }

  // If the current page is among the first 3 pages,
  // show the first 3, an ellipsis, and the last 2 pages.
  if (currentPage <= 3) {
    return [1, 2, 3, "...", totalPages - 1, totalPages];
  }

  // If the current page is among the last 3 pages,
  // show the first 2, an ellipsis, and the last 3 pages.
  if (currentPage >= totalPages - 2) {
    return [1, 2, "...", totalPages - 2, totalPages - 1, totalPages];
  }

  // If the current page is somewhere in the middle,
  // show the first page, an ellipsis, the current page and its neighbors,
  // another ellipsis, and the last page.
  return [
    1,
    "...",
    currentPage - 1,
    currentPage,
    currentPage + 1,
    "...",
    totalPages,
  ];
};

这个 generatePagination函数实现了一个智能的分页显示逻辑,旨在创建一个用户友好的分页导航系统。它的主要功能是根据当前页码和总页数生成一个页码数组,用于显示在分页控件中。让我们详细分析一下它的功能:

  1. 处理少量页面的情况

    • 如果总页数小于或等于7,函数会返回所有页码的数组,不使用省略号,例如:[1, 2, 3, 4, 5, 6, 7]

  2. 处理当前页在前三页的情况

    • 如果当前页是前三页之一,函数会显示前三页,然后是省略号,最后是最后两页。例如:[1, 2, 3, "...", 99, 100]

  3. 处理当前页在后三页的情况

    • 如果当前页是后三页之一,函数会显示前两页,然后是省略号,最后是最后三页。例如:[1, 2, "...", 98, 99, 100]

  4. 处理当前页在中间的情况:

    • 如果当前页在中间位置,函数会显示第一页,省略号,当前页及其前后各一页,另一个省略号,最后是最后一页。例如:[1, "...", 49, 50, 51, "...", 100]

编辑/app/lib/pagination.tsx,修改为下面的代码,实现分页的左右按钮

"use client";
import { ArrowLeftIcon, ArrowRightIcon } from "@heroicons/react/24/outline";
import clsx from "clsx";
import Link from "next/link";
import { usePathname, useSearchParams } from "next/navigation";
import { generatePagination } from "@/app/lib/utils";

export default function Pagination({ totalPages }: { totalPages: number }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const currentPage = Number(searchParams.get("page")) || 1;

  const createPageURL = (pageNumber: number | string) => {
    const params = new URLSearchParams(searchParams);
    params.set("page", pageNumber.toString());
    return `${pathname}?${params.toString()}`;
  };
  const allPages = generatePagination(currentPage, totalPages);

  return (
    <>
      {/*  NOTE: Uncomment this code in Chapter 11 */}

      <div className="inline-flex">
        <PaginationArrow
          direction="left"
          href={createPageURL(currentPage - 1)}
          isDisabled={currentPage <= 1}
        />

        <div className="flex -space-x-px">
          {allPages.map((page, index) => {
            let position: "first" | "last" | "single" | "middle" | undefined;

            if (index === 0) position = "first";
            if (index === allPages.length - 1) position = "last";
            if (allPages.length === 1) position = "single";
            if (page === "...") position = "middle";

            return (
              <PaginationNumber
                key={page}
                href={createPageURL(page)}
                page={page}
                position={position}
                isActive={currentPage === page}
              />
            );
          })}
        </div>

        <PaginationArrow
          direction="right"
          href={createPageURL(currentPage + 1)}
          isDisabled={currentPage >= totalPages}
        />
      </div>
    </>
  );
}

function PaginationNumber({
  page,
  href,
  isActive,
  position,
}: {
  page: number | string;
  href: string;
  position?: "first" | "last" | "middle" | "single";
  isActive: boolean;
}) {
  const className = clsx(
    "flex h-10 w-10 items-center justify-center text-sm border",
    {
      "rounded-l-md": position === "first" || position === "single",
      "rounded-r-md": position === "last" || position === "single",
      "z-10 bg-blue-600 border-blue-600 text-white": isActive,
      "hover:bg-gray-100": !isActive && position !== "middle",
      "text-gray-300": position === "middle",
    }
  );

  return isActive || position === "middle" ? (
    <div className={className}>{page}</div>
  ) : (
    <Link href={href} className={className}>
      {page}
    </Link>
  );
}

function PaginationArrow({
  href,
  direction,
  isDisabled,
}: {
  href: string;
  direction: "left" | "right";
  isDisabled?: boolean;
}) {
  const className = clsx(
    "flex h-10 w-10 items-center justify-center rounded-md border",
    {
      "pointer-events-none text-gray-300": isDisabled,
      "hover:bg-gray-100": !isDisabled,
      "mr-2 md:mr-4": direction === "left",
      "ml-2 md:ml-4": direction === "right",
    }
  );

  const icon =
    direction === "left" ? (
      <ArrowLeftIcon className="w-4" />
    ) : (
      <ArrowRightIcon className="w-4" />
    );

  return isDisabled ? (
    <div className={className}>{icon}</div>
  ) : (
    <Link className={className} href={href}>
      {icon}
    </Link>
  );
}

表格

以上内容为分页的详细技术实现步骤,如果你想看更多内容或者能够看到技术更新的内容,请百度搜索:曲速引擎 warp drive csdn 在首页找到我的地址访问即可,一线更新内容将会在我的个人博客上面更新,谢谢大家。

更详细内容查看

独立博客 https://www.dataeast.cn/
CSDN博客 https://blog.csdn.net/siberiaWarpDrive
B站视频空间 https://space.bilibili.com/25871614?spm_id_from=333.1007.0.0
关注 “曲速引擎 Warp Drive” 微信公众号
公众号二维码


评论