feat(ui): 更新车间和产品页面UI设计并添加新图片资源

更新多个页面的UI设计,包括产品卡片、车间详情页和列表页,采用现代化卡片设计和动画效果
添加新的车间图片资源并更新相关页面的图片引用路径
优化响应式布局和交互体验,增强视觉吸引力
This commit is contained in:
砂糖
2026-02-03 14:39:36 +08:00
parent 8600a01ce4
commit 86c20e5ef7
20 changed files with 179 additions and 141 deletions

View File

@@ -20,9 +20,6 @@ export async function generateMetadata({
}: MetadataProps): Promise<Metadata> {
const { locale, slug } = await params;
const workShop = await getWorkShop(locale, slug);
console.log(workShop);
console.log(workShop?.slug);
if (!workShop) {
return constructMetadata({
@@ -46,7 +43,6 @@ export async function generateMetadata({
// 页面主组件 - 仅保留字段展示+修复 Hydration 错误
export default async function WorkshopDetailPage({ params }: { params: Params }) {
// 🔴 核心修复1移除不必要的 awaitparams 是同步对象)
const { locale, slug } = await params;
const workShop = await getWorkShop(locale, slug);
@@ -58,32 +54,29 @@ export default async function WorkshopDetailPage({ params }: { params: Params })
const workshopDesc = workShop.desc || "暂无车间描述";
return (
<div className="min-h-screen bg-gray-50 py-10 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
{/* 封面图区域 - 🔴 修复布局属性不一致 */}
<div className="relative rounded-2xl overflow-hidden shadow-lg h-[400px] sm:h-[500px] mb-8">
<div className="min-h-screen bg-gray-50 py-16 px-6 sm:px-8 lg:px-10">
<div className="max-w-7xl mx-auto space-y-12">
{/* 封面图区域 - 强化背景效果 */}
<div className="relative rounded-xl overflow-hidden shadow-lg h-[500px] sm:h-[600px] mb-12 group">
<img
src={coverSrc}
alt={coverAlt}
className="absolute inset-0 w-full h-full object-cover transition-transform hover:scale-105 duration-700"
// 显式设置属性,确保服务端/客户端一致
className="absolute inset-0 w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
loading="lazy"
decoding="async"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black opacity-40"></div>
</div>
{/* 标题+描述区域 */}
<div className="bg-white rounded-xl p-8 shadow-sm mb-10">
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-6">
{workShop.title}
</h1>
<p className="text-lg text-gray-700 leading-relaxed">
{workshopDesc}
</p>
<div className="bg-white rounded-xl p-8 shadow-xl">
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-6">{workShop.title}</h1>
<p className="text-lg sm:text-xl text-gray-700 leading-relaxed mb-6">{workshopDesc}</p>
</div>
{/* 图片展示区域 */}
{workShop.images?.length > 0 && (
<div className="bg-white rounded-xl p-8 shadow-sm">
<div className="bg-white rounded-xl p-8 shadow-xl mt-12">
<h2 className="text-2xl font-semibold text-gray-800 mb-6 border-b pb-3 border-gray-200">
</h2>
@@ -94,12 +87,12 @@ export default async function WorkshopDetailPage({ params }: { params: Params })
return (
<div
key={stableKey}
className="relative rounded-lg overflow-hidden shadow-md h-64 hover:shadow-xl transition-all duration-300"
className="relative rounded-xl overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300"
>
<img
src={img}
alt={imgAlt}
className="absolute inset-0 w-full h-full object-cover transition-transform hover:scale-110 duration-500"
className="w-full h-full object-cover transition-transform duration-500 hover:scale-105"
loading="lazy"
decoding="async"
/>
@@ -126,7 +119,7 @@ export async function generateStaticParams() {
}))
);
} catch (error) {
console.error("生成产品静态参数失败:", error);
console.error("生成车间静态参数失败:", error);
return [];
}
}
}

View File

@@ -2,13 +2,14 @@ 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 类型(静态生成的参数)
// 明确 Params 类型(静态生成的参数)
type Params = { locale: string };
type MetadataProps = {
@@ -16,9 +17,7 @@ type MetadataProps = {
};
// 生成页面元数据(确保服务端/客户端翻译一致)
export async function generateMetadata({
params,
}: MetadataProps): Promise<Metadata> {
export async function generateMetadata({ params }: MetadataProps): Promise<Metadata> {
const { locale } = params;
const t = await getTranslations({ locale, namespace: "Workshop" });
@@ -44,35 +43,27 @@ function EmptyState() {
return <div className="py-10 text-center text-gray-500"></div>;
}
// 单个车间卡片组件Client Component 标记,避免 Hydration 冲突)
// 'use client'; // 仅当需要添加交互时启用,当前纯展示可不用
function WorkshopCard({ workshop }: { workshop: WorkShop }) {
// 为 Image 组件添加默认值,避免属性缺失导致不匹配
// 为 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-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow">
{/* 封面图:优化属性,确保服务端/客户端渲染一致 */}
<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}
sizes="100vw" // 简化 sizes避免解析差异
className="object-cover"
// 显式设置属性,避免隐式差异
className="object-cover w-full h-full"
loading="lazy"
/>
</div>
{/* 车间信息 */}
<div className="p-4">
<h2 className="text-xl font-bold mb-2">{workshop.title || "未命名车间"}</h2>
<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>
@@ -81,11 +72,7 @@ function WorkshopCard({ workshop }: { workshop: WorkShop }) {
}
// 页面主组件Server Component
export default async function Page({
params,
}: {
params: Params;
}) {
export default async function Page({ params }: { params: Params }) {
const { locale } = await params;
// 获取翻译(确保服务端/客户端一致)
const t = await getTranslations({ locale, namespace: "Workshop" });
@@ -97,22 +84,21 @@ export default async function Page({
const pageTitle = t("pageTitle", { defaultValue: "车间展示" });
return (
<main className="container mx-auto py-8 px-4">
{/* 页面标题(纯静态渲染,无动态属性) */}
<h1 className="text-3xl font-bold mb-8 text-center">{pageTitle}</h1>
<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>
{/* 移除不必要的 SuspenseServer Component 顶层 await 无需 Suspense */}
{workShops.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{workShops.map((workshop) => {
// 用稳定的 key优先用唯一标识如 id无 id 则用 title+locale 避免 index 导致的问题)
{/* 展示车间卡片 */}
<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} />;
})}
</div>
) : (
<EmptyState />
)}
})
) : (
<EmptyState />
)}
</div>
</main>
);
}
}