Files
sage-home/app/[locale]/workshop/page.tsx
砂糖 86c20e5ef7 feat(ui): 更新车间和产品页面UI设计并添加新图片资源
更新多个页面的UI设计,包括产品卡片、车间详情页和列表页,采用现代化卡片设计和动画效果
添加新的车间图片资源并更新相关页面的图片引用路径
优化响应式布局和交互体验,增强视觉吸引力
2026-02-03 14:39:36 +08:00

105 lines
3.5 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 { Link as I18nLink, LOCALES } from "@/i18n/routing";
import { constructMetadata } from "@/lib/metadata";
import { getWorkShops } from "@/lib/workshop";
import { WorkShop } from "@/types/workShop";
import clsx from "clsx";
import { Metadata } from "next";
import { getTranslations } from "next-intl/server";
// 强制静态生成
export const dynamic = "force-static";
// 明确 Params 类型(静态生成的参数)
type Params = { locale: string };
type MetadataProps = {
params: Params;
};
// 生成页面元数据(确保服务端/客户端翻译一致)
export async function generateMetadata({ params }: MetadataProps): Promise<Metadata> {
const { locale } = params;
const t = await getTranslations({ locale, namespace: "Workshop" });
return constructMetadata({
page: "Workshop",
title: t("pageTitle", { defaultValue: "车间展示" }), // 使用 next-intl 官方的 defaultValue
description: t("pageDesc", { defaultValue: "公司车间展示" }),
locale: locale,
path: `/workshop`,
canonicalUrl: `/workshop`,
});
}
// 生成静态路由参数(多语言)
export async function generateStaticParams() {
return LOCALES.map((locale) => ({
locale,
}));
}
// 空数据组件(纯静态,无动态属性)
function EmptyState() {
return <div className="py-10 text-center text-gray-500"></div>;
}
function WorkshopCard({ workshop }: { workshop: WorkShop }) {
// 为 img 标签添加默认值,避免属性缺失导致不匹配
const coverSrc = workshop.cover || "/default-workshop-cover.png"; // 兜底封面图
const coverAlt = workshop.title || "车间封面";
return (
<I18nLink href={`/workshop/${workshop.slug}`} locale={workshop.locale}>
<div className="border rounded-xl overflow-hidden shadow-lg hover:shadow-2xl transition-shadow">
{/* 封面图:使用原生 img 标签,确保图片尺寸一致 */}
<div className="relative h-64 w-full">
<img
src={coverSrc}
alt={coverAlt}
className="object-cover w-full h-full"
loading="lazy"
/>
</div>
{/* 车间信息 */}
<div className="p-6 bg-white">
<h2 className="text-2xl font-semibold mb-2 text-gray-800">{workshop.title || "未命名车间"}</h2>
<p className="text-gray-600 mb-4">{workshop.desc || "暂无描述"}</p>
</div>
</div>
</I18nLink>
);
}
// 页面主组件Server Component
export default async function Page({ params }: { params: Params }) {
const { locale } = await params;
// 获取翻译(确保服务端/客户端一致)
const t = await getTranslations({ locale, namespace: "Workshop" });
// 获取车间数据(顶层 awaitServer Component 原生支持)
const workShops: WorkShop[] = await getWorkShops(locale);
// 页面标题翻译兜底
const pageTitle = t("pageTitle", { defaultValue: "车间展示" });
return (
<main className="container mx-auto py-16 px-6 max-w-screen-xl">
{/* 页面标题 */}
<h1 className="text-4xl font-extrabold text-center text-gray-900 mb-12">{pageTitle}</h1>
{/* 展示车间卡片 */}
<div className={clsx("grid gap-8", workShops.length ? "grid-cols-1 md:grid-cols-2 lg:grid-cols-3" : "")}>
{workShops.length > 0 ? (
workShops.map((workshop) => {
const stableKey = `${workshop.title}-${workshop.locale}`;
return <WorkshopCard key={stableKey} workshop={workshop} />;
})
) : (
<EmptyState />
)}
</div>
</main>
);
}