2025-06-22 11:34:32 +08:00

256 lines
9.9 KiB
TypeScript
Raw Permalink 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 { 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>
)
}