This commit is contained in:
2025-06-22 11:34:32 +08:00
parent 142992a6c9
commit 7d139b22db
55 changed files with 11035 additions and 105 deletions
+255
View File
@@ -0,0 +1,255 @@
import { notFound } from 'next/navigation'
import { prisma } from '@/lib/prisma'
import { AddToCartButton } from '@/components/AddToCartButton'
import { ArrowLeft, Package, Star, Shield, Truck, RotateCcw } from 'lucide-react'
import Link from 'next/link'
interface ComponentDetailPageProps {
params: Promise<{
id: string
}>
}
export default async function ComponentDetailPage({ params }: ComponentDetailPageProps) {
const component = await prisma.component.findUnique({
where: { id: (await params).id },
include: {
componentType: true
}
})
if (!component) {
notFound()
}
const specifications = component.specifications ? JSON.parse(component.specifications) : {}
// 获取同类型的其他产品推荐
const relatedComponents = await prisma.component.findMany({
where: {
componentTypeId: component.componentTypeId,
id: { not: component.id }
},
take: 4,
orderBy: {
createdAt: 'desc'
}
})
return (
<div className="min-h-screen bg-gray-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Breadcrumb */}
<nav className="flex items-center space-x-2 text-sm text-gray-500 mb-8">
<Link href="/" className="hover:text-blue-600">首页</Link>
<span>/</span>
<Link href="/components" className="hover:text-blue-600">配件商城</Link>
<span>/</span>
<Link href={`/components?type=${component.componentType.name}`} className="hover:text-blue-600">
{component.componentType.name}
</Link>
<span>/</span>
<span className="text-gray-900">{component.name}</span>
</nav>
{/* Back Button */}
<Link
href="/components"
className="inline-flex items-center text-blue-600 hover:text-blue-800 mb-6"
>
<ArrowLeft className="h-4 w-4 mr-2" />
返回商品列表
</Link>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
{/* Product Image */}
<div className="space-y-4">
<div className="aspect-square bg-white rounded-lg border border-gray-200 flex items-center justify-center">
{component.imageUrl ? (
<img loading='lazy'
src={component.imageUrl}
alt={component.name}
className="max-w-full max-h-full object-contain grow"
/>
) : (
<div className="text-center text-gray-400">
<Package className="h-24 w-24 mx-auto mb-4" />
<p>暂无商品图片</p>
</div>
)}
</div>
</div>
{/* Product Info */}
<div className="space-y-6">
<div>
<div className="flex items-center space-x-2 text-sm text-gray-500 mb-2">
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
{component.componentType.name}
</span>
<span className="bg-gray-100 text-gray-800 px-2 py-1 rounded">
{component.brand}
</span>
</div>
<h1 className="text-3xl font-bold text-gray-900 mb-4">
{component.name}
</h1>
<div className="flex items-center space-x-4 mb-4">
<div className="flex items-center">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`h-5 w-5 ${i < 4 ? 'text-yellow-400 fill-current' : 'text-gray-300'}`}
/>
))}
<span className="ml-2 text-sm text-gray-600">(128 评价)</span>
</div>
</div>
</div>
{/* Price */}
<div className="border-t border-b border-gray-200 py-6">
<div className="flex items-baseline space-x-2">
<span className="text-3xl font-bold text-red-600">
¥{component.price}
</span>
<span className="text-lg text-gray-500 line-through">
¥{Math.round(component.price * 1.2)}
</span>
<span className="bg-red-100 text-red-800 text-sm px-2 py-1 rounded">
8折
</span>
</div>
<p className="text-sm text-gray-600 mt-2">价格含税,全国包邮</p>
</div>
{/* Stock Status */}
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">库存状态:</span>
{component.stock > 0 ? (
<span className="text-green-600 font-medium">
有货 ({component.stock} 件)
</span>
) : (
<span className="text-red-600 font-medium">缺货</span>
)}
</div>
{/* Add to Cart */}
<div className="space-y-4">
<AddToCartButton
componentId={component.id}
disabled={component.stock === 0}
className="w-full"
/>
<button className="w-full border border-gray-300 text-gray-700 py-3 px-6 rounded-lg hover:bg-gray-50 transition-colors">
添加到装机配置
</button>
</div>
{/* Service Promises */}
<div className="grid grid-cols-3 gap-4 pt-6 border-t border-gray-200">
<div className="text-center">
<Truck className="h-8 w-8 text-blue-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">全国包邮</p>
</div>
<div className="text-center">
<Shield className="h-8 w-8 text-green-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">正品保障</p>
</div>
<div className="text-center">
<RotateCcw className="h-8 w-8 text-orange-600 mx-auto mb-2" />
<p className="text-xs text-gray-600">7天无理由退换</p>
</div>
</div>
</div>
</div>
{/* Product Details */}
<div className="mt-12 grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Description */}
<div className="lg:col-span-2">
<h2 className="text-2xl font-bold text-gray-900 mb-6">商品详情</h2>
<div className="bg-white rounded-lg p-6 shadow-sm">
<h3 className="font-semibold text-gray-900 mb-4">产品描述</h3>
<p className="text-gray-700 leading-relaxed mb-6">
{component.description || '暂无详细描述'}
</p>
{Object.keys(specifications).length > 0 && (
<>
<h3 className="font-semibold text-gray-900 mb-4">技术规格</h3>
<div className="space-y-3">
{Object.entries(specifications).map(([key, value]) => (
<div key={key} className="flex justify-between py-2 border-b border-gray-100">
<span className="text-gray-600">{key}:</span>
<span className="font-medium text-gray-900">{String(value)}</span>
</div>
))}
</div>
</>
)}
</div>
</div>
{/* Product Info */}
<div>
<h2 className="text-2xl font-bold text-gray-900 mb-6">产品信息</h2>
<div className="bg-white rounded-lg p-6 shadow-sm space-y-4">
<div className="flex justify-between">
<span className="text-gray-600">品牌:</span>
<span className="font-medium">{component.brand}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">型号:</span>
<span className="font-medium">{component.model}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">分类:</span>
<span className="font-medium">{component.componentType.name}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-600">库存:</span>
<span className="font-medium">{component.stock} 件</span>
</div>
</div>
</div>
</div>
{/* Related Products */}
{relatedComponents.length > 0 && (
<div className="mt-12">
<h2 className="text-2xl font-bold text-gray-900 mb-6">相关推荐</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
{relatedComponents.map((item) => (
<Link
key={item.id}
href={`/components/${item.id}`}
className="bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow"
>
<div className="p-4">
<div className="aspect-square bg-gray-100 rounded-lg mb-4 flex items-center justify-center">
{item.imageUrl ? (
<img loading='lazy'
src={item.imageUrl}
alt={item.name}
className="max-w-full max-h-full object-contain"
/>
) : (
<Package className="h-12 w-12 text-gray-400" />
)}
</div>
<h3 className="font-medium text-gray-900 mb-2 line-clamp-2">
{item.name}
</h3>
<p className="text-lg font-bold text-red-600">¥{item.price}</p>
</div>
</Link>
))}
</div>
</div>
)}
</div>
</div>
)
}