Files
sage-home/app/[locale]/blog/page.tsx
砂糖 70f337bb92 init
2026-01-24 16:54:44 +08:00

187 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import ParallaxHero from "@/app/[locale]/blog/ParallaxHero";
import TablerEyeFilled from "@/components/icons/eye";
import { Link as I18nLink, Locale, LOCALES } from "@/i18n/routing";
import { getPosts } from "@/lib/getBlogs";
import { constructMetadata } from "@/lib/metadata";
import dayjs from "dayjs";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
type Params = Promise<{ locale: string }>;
type MetadataProps = {
params: Params;
};
export async function generateMetadata({
params,
}: MetadataProps): Promise<Metadata> {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Blog" });
return constructMetadata({
page: "Blog",
title: t("title"),
description: t("description"),
locale: locale as Locale,
path: `/blog`,
canonicalUrl: `/blog`,
});
}
export default async function Page({
params,
// searchParams,
}: {
params: Params;
}) {
const { locale } = await params;
// const resolvedSearchParams = await searchParams;
// const category = resolvedSearchParams.category as string || "";
let { posts } = await getPosts(locale);
const t = await getTranslations("Blog");
// const pageRaw = resolvedSearchParams.page as string || "1";
// const page = Math.max(1, parseInt(10, 10));
const page = 1;
const pageSize = 10;
const totalPages = Math.max(1, Math.ceil(posts.length / pageSize));
const start = (page - 1) * pageSize;
const end = start + pageSize;
const pagePosts = posts.slice(start, end);
return (
<div className="w-full">
<ParallaxHero />
{/* 顶部操作区:标签 + 面包屑 */}
<div className="container mx-auto px-4">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mt-4">
{/* <div className="flex gap-3">
<button className="px-4 py-2 rounded-md border bg-white text-gray-700 shadow-sm hover:bg-gray-50">
投资者保护
</button>
<button className="px-4 py-2 rounded-md border bg-white text-gray-700 shadow-sm hover:bg-gray-50">
公司公告
</button>
</div> */}
<div className="flex items-center gap-2 text-sm text-gray-500">
<I18nLink href="/" prefetch={false} className="hover:underline">
</I18nLink>
<span>/</span>
<I18nLink href="/blog" prefetch={false} className="hover:underline">
</I18nLink>
</div>
</div>
{/* 列表 */}
<div className="mt-4">
{pagePosts.map((post) => {
const year = dayjs(post.date).format("YYYY");
const monthDay = dayjs(post.date).format("MM-DD");
const views = (post.metadata?.views as number) || 0;
return (
<I18nLink
key={post.slug}
href={`/blog/${post.slug}`}
prefetch={false}
className="block rounded-md mb-4 bg-gray-100 px-6 py-5 transition-all duration-200 shadow hover:bg-gray-200 hover:translate-y-[1px] hover:shadow-inner active:translate-y-[2px] active:shadow-inner focus:outline-none focus:ring-1 focus:ring-gray-300"
>
<div className="flex items-center">
<div className="flex-1 pr-4">
<div className="text-base md:text-lg font-medium text-gray-800">
{post.title}
</div>
<div className="mt-2 text-xs text-gray-600 flex items-center gap-1">
<TablerEyeFilled className="w-4 h-4" />
访{views}
</div>
</div>
<div className="w-24 md:w-28 text-right">
<div className="text-xs text-gray-500">{year}</div>
<div className="text-2xl md:text-3xl font-bold text-gray-500">{monthDay}</div>
</div>
</div>
</I18nLink>
);
})}
</div>
<div className="my-6 flex items-center justify-center gap-2">
<I18nLink
href={`/blog?page=${Math.max(1, page - 1)}`}
prefetch={false}
className={`px-2.5 py-1 border rounded-sm text-sm ${page === 1 ? "bg-gray-100 text-gray-400" : "bg-white hover:bg-gray-50"
}`}
>
{"<"}
</I18nLink>
{(() => {
const items: (number | string)[] = [];
const showTotal = totalPages;
const maxNumbers = 4;
const startNum = 1;
const endNum = Math.min(showTotal, maxNumbers);
for (let n = startNum; n <= endNum; n++) items.push(n);
if (showTotal > maxNumbers + 1) items.push("...");
if (showTotal > maxNumbers) items.push(showTotal);
return items.map((it, idx) => {
if (typeof it === "string")
return (
<span key={`dot-${idx}`} className="px-2.5 py-1 text-sm text-gray-500">
{it}
</span>
);
const isActive = it === page;
return (
<I18nLink
key={it}
href={`/blog?page=${it}`}
prefetch={false}
className={`px-2.5 py-1 border rounded-sm text-sm ${isActive
? "bg-red-600 text-white border-red-600"
: "bg-white hover:bg-gray-50"
}`}
>
{it}
</I18nLink>
);
});
})()}
<I18nLink
href={`/blog?page=${Math.min(totalPages, page + 1)}`}
prefetch={false}
className={`px-2.5 py-1 border rounded-sm text-sm ${page === totalPages ? "bg-gray-100 text-gray-400" : "bg-white hover:bg-gray-50"
}`}
>
{">"}
</I18nLink>
<span className="ml-2 text-sm text-gray-600"></span>
<form action="/blog" method="get" className="flex items-center gap-1">
<input
type="number"
name="page"
min={1}
max={totalPages}
defaultValue={page}
className="w-14 h-7 border rounded-sm px-2 text-sm"
/>
<button className="px-2.5 h-7 border rounded-sm text-sm bg-white hover:bg-gray-50" type="submit">
</button>
<span className="text-sm text-gray-600"></span>
</form>
</div>
</div>
</div>
);
}
export async function generateStaticParams() {
return LOCALES.map((locale) => ({ locale }));
}