1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
import { postsPerPage } from '@/app/(main)/layout.config';
import { NumberedPagination } from '@/components/numbered-pagination';
import { PostCard } from '@/components/posts/post-card';
import { Section } from '@/components/section';
import { createMetadata } from '@/lib/metadata';
import { getPublishedPosts } from '@/lib/payload-posts';
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound, redirect } from 'next/navigation';
const CurrentPostsCount = ({
startIndex,
endIndex,
totalPosts,
}: {
startIndex: number;
endIndex: number;
totalPosts: number;
}) => {
const start = startIndex + 1;
const end = endIndex < totalPosts ? endIndex : totalPosts;
if (start === end) return <span>({start})</span>;
return (
<span>
({start}-{end})
</span>
);
};
const Pagination = ({
pageIndex,
pageCount,
}: {
pageIndex: number;
pageCount: number;
}) => {
const handlePageChange = async (page: number) => {
'use server';
redirect(`/posts?page=${page}`);
};
return (
<Section className='bg-dashed'>
<NumberedPagination
currentPage={pageIndex + 1}
totalPages={pageCount}
paginationItemsToDisplay={5}
onPageChange={handlePageChange}
/>
</Section>
);
};
export default async function Page(props: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const searchParams = await props.searchParams;
const pageIndex = searchParams.page
? Number.parseInt(
Array.isArray(searchParams.page)
? (searchParams.page[0] ?? '')
: searchParams.page,
10,
) - 1
: 0;
if (Number.isNaN(pageIndex)) notFound();
// 获取文章(带分页)
const { posts, totalDocs, totalPages } = await getPublishedPosts({
limit: postsPerPage,
page: pageIndex + 1,
});
if (pageIndex < 0 || (totalPages > 0 && pageIndex >= totalPages)) notFound();
const startIndex = pageIndex * postsPerPage;
const endIndex = startIndex + posts.length;
return (
<>
<Section className='p-4 lg:p-6'>
<h1 className='font-bold text-3xl leading-tight tracking-tighter md:text-4xl'>
All {totalDocs} Posts{' '}
<CurrentPostsCount
startIndex={startIndex}
endIndex={endIndex}
totalPosts={totalDocs}
/>
</h1>
</Section>
<Section className='h-full' sectionClassName='flex flex-1'>
<div className='grid divide-y divide-dashed divide-border/70 text-left dark:divide-border'>
{posts.map((post) => {
const date = post.date.toDateString();
return (
<PostCard
title={post.title}
description={post.description}
image={post.image}
url={post.url}
date={date}
key={post.id}
author={post.author}
tags={post.tags}
/>
);
})}
</div>
</Section>
{totalPages > 1 && (
<Pagination pageIndex={pageIndex} pageCount={totalPages} />
)}
</>
);
}
type Props = {
params: Promise<{ slug: string[] }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};
export async function generateMetadata(
props: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
const searchParams = await props.searchParams;
const pageIndex = searchParams.page
? Number.parseInt(
Array.isArray(searchParams.page)
? (searchParams.page[0] ?? '')
: searchParams.page,
10,
)
: 1;
const isFirstPage = pageIndex === 1 || !searchParams.page;
const pageTitle = isFirstPage ? 'Posts' : `Posts - Page ${pageIndex}`;
const canonicalUrl = isFirstPage ? '/posts' : `/posts?page=${pageIndex}`;
return createMetadata({
title: pageTitle,
description: `Posts${!isFirstPage ? ` - Page ${pageIndex}` : ''}`,
openGraph: {
url: canonicalUrl,
},
alternates: {
canonical: canonicalUrl,
},
});
}
|