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

184 lines
6.0 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 ProductTabsClient from "@/components/ProductTabsClient";
import { Locale, LOCALES } from "@/i18n/routing";
import { getProducts } from "@/lib/getProducts";
import { constructMetadata } from "@/lib/metadata";
import { Product } from "@/types/product";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
import { notFound } from "next/navigation";
type Params = {
locale: string;
slug: string;
};
type MetadataProps = {
params: Promise<Params>;
};
export async function generateMetadata({
params,
}: MetadataProps): Promise<Metadata> {
const { locale, slug } = await params;
try {
const { products }: { products: Product[] } = await getProducts(locale);
const product = products.find((product) => product.slug === slug);
if (!product) {
return constructMetadata({
title: "404 - 产品不存在",
description: "请求的产品页面未找到",
noIndex: true,
locale: locale as Locale,
path: `/products/${slug}`,
canonicalUrl: `/products/${slug}`,
});
}
return constructMetadata({
page: "products",
title: product.title,
description: `${product.title} - ${product.model} - ${product.place}`,
images: product.images.length ? [product.images[0]] : [],
locale: locale as Locale,
path: `/products/${slug}`,
canonicalUrl: `/products/${slug}`,
});
} catch (error) {
console.error("生成产品元数据失败:", error);
return constructMetadata({
title: "产品详情",
description: "产品详情页面",
locale: (await params).locale as Locale,
path: `/products/${(await params).slug}`,
});
}
}
export default async function ProductPage({ params }: { params: Promise<Params> }) {
const { locale, slug } = await params;
const { products }: { products: Product[] } = await getProducts(locale);
const product = products.find((item) => item.slug == slug);
if (!product) return notFound();
const t = await getTranslations({ locale, namespace: "Product" });
// 获取所有产品图片
const allImages = product.images || [];
const formattedPublishTime = (() => {
if (!product.publishedTime) return "暂无";
const publishDate = typeof product.publishedTime === 'string'
? new Date(product.publishedTime)
: product.publishedTime;
return isNaN(publishDate.getTime())
? "暂无"
: publishDate.toLocaleDateString("zh-CN", {
year: "numeric",
month: "2-digit",
day: "2-digit",
});
})();
return (
<div>
<img src="http://kelunpuzhonggong.com/upload/img/20251015111553.jpg" className="w-screen h-auto" alt="" />
<div className="w-full max-w-6xl mx-auto px-4 md:px-8 py-8">
<div className="text-center mb-6">
<h1 className="text-purple-600 text-xl font-bold">{t("detailTitle")}</h1>
</div>
{/* 调整后:左侧大图+下方缩略图 + 右侧信息 */}
<div className="border rounded p-4 mb-6">
<div className="flex flex-col md:flex-row">
{/* 左侧图片区域(大图+缩略图) */}
<div className="md:w-1/3 mb-4 md:mb-0">
{/* 第一张大图 */}
{allImages.length > 0 ? (
<div className="relative w-full h-auto max-h-[500px]">
<img
src={allImages[0]}
alt={`${product.title || "产品"}-主图`}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 66vw, 66vw"
className="object-contain"
/>
</div>
) : (
<div className="w-full h-60 bg-gray-100 flex items-center justify-center text-gray-500">
</div>
)}
</div>
{/* 右侧产品信息区域 */}
<div className="md:w-2/3 md:ml-4">
<h1 className="text-xl font-bold mb-3">{product.title || "未命名产品"}</h1>
<div className="space-y-2 mb-6">
<div>
<span className="font-medium">{t("productModel")}</span>
<span>{product.model || "暂无"}</span>
</div>
<div>
<span className="font-medium">{t("productPlace")}</span>
<span>{product.place || "暂无"}</span>
</div>
<div>
<span className="font-medium">{t("productPublishedTime")}</span>
<span>{formattedPublishTime}</span>
</div>
</div>
</div>
</div>
{/* 所有图片缩略图(横向滚动) */}
{allImages.length > 0 && (
<div className="flex flex-nowrap gap-2 mt-4 overflow-x-auto pb-2">
{allImages.map((img, index) => (
<div
key={index}
className="relative w-20 h-20 cursor-pointer border border-gray-200 rounded"
>
<img
src={img}
alt={`${product.title || "产品"}-图${index + 1}`}
className="object-cover"
sizes="80px"
/>
</div>
))}
</div>
)}
</div>
<ProductTabsClient
locale={locale}
detail={product.detail}
spec={product.spec}
packaging={product.packaging}
/>
</div>
</div>
);
}
export async function generateStaticParams() {
try {
const defaultLocale = LOCALES[0];
const { products }: { products: Product[] } = await getProducts(defaultLocale);
const validProducts = products.filter((product) => product.slug.toString() && product.title);
return LOCALES.flatMap((locale) =>
validProducts.map((product) => ({
locale,
slug: product.slug.toString(),
}))
);
} catch (error) {
console.error("生成产品静态参数失败:", error);
return [];
}
}