fake_shop/app/good/[id]/page.jsx

256 lines
7.6 KiB
JavaScript
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.

"use client";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";
import Image from "next/image";
import { useRouter } from "next/navigation";
import toast from "react-hot-toast";
import AddToCartModal from "@/components/AddToCartModal";
import Link from "next/link";
import { AiOutlineHome, AiOutlineShoppingCart } from "react-icons/ai";
import { FiUser } from "react-icons/fi";
export default function ProductDetail() {
const params = useParams();
const [product, setProduct] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [quantity, setQuantity] = useState(1);
const router = useRouter();
const [showModal, setShowModal] = useState(false);
const [recommendedProducts, setRecommendedProducts] = useState([]);
useEffect(() => {
const fetchProduct = async () => {
try {
const res = await fetch(`/api/products/${params.id}`);
if (!res.ok) {
throw new Error("获取商品详情失败");
}
const data = await res.json();
setProduct(data);
fetchRecommendedProducts(); // 直接获取推荐商品
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
const fetchRecommendedProducts = async () => {
try {
const res = await fetch(`/api/products`); // 获取所有商品
if (!res.ok) {
throw new Error("获取推荐商品失败");
}
const data = await res.json();
setRecommendedProducts(data.slice(0, 4)); // 取前四个商品
} catch (err) {
console.error(err.message);
}
};
fetchProduct();
}, [params.id]);
const handleAddToCart = async () => {
try {
const token = localStorage.getItem("token");
if (!token) {
router.push("/login");
return;
}
const res = await fetch("/api/cart", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
productId: params.id,
quantity: quantity,
}),
});
if (!res.ok) {
throw new Error("添加购物车失败");
}
toast.success("添加成功");
handleModalClose();
} catch (error) {
toast.error(error.message);
}
};
const handleBuyNow = () => {
const token = localStorage.getItem("token");
if (!token) {
router.push("/login");
return;
}
router.push(`/checkout?products=${params.id}&quantity=${quantity}`);
};
const handleAddToCartClick = () => {
const token = localStorage.getItem("token");
if (!token) {
router.push("/login");
return;
}
setShowModal(true);
};
const handleModalClose = () => {
setShowModal(false);
};
if (loading) {
return <div className="p-4">加载中...</div>;
}
if (error) {
return <div className="p-4 text-red-500">错误: {error}</div>;
}
if (!product) {
return <div className="p-4">商品不存在</div>;
}
return (
<div className="flex flex-col min-h-screen pb-20 bg-gray-50">
{/* 商品图片 */}
<div className="relative aspect-square">
<Image
src={product.imageUrl}
alt={product.title}
fill
className="object-cover"
/>
</div>
{/* 商品信息 */}
<div className="flex-1 p-4 bg-white">
<div className="flex items-center">
<span className="text-2xl text-red-500 font-bold">
¥{product.price}
</span>
{product.isVip && (
<span className="ml-2 px-2 py-0.5 text-xs text-yellow-600 border border-yellow-600 rounded">
VIP
</span>
)}
</div>
<h1 className="mt-2 text-lg font-medium">{product.title}</h1>
<div className="mt-2 text-sm text-gray-500">
<span>库存: {product.stock}</span>
<span className="ml-4">销量: {product.sales}</span>
</div>
{/* 配送信息 */}
<div className="mt-4 p-3 bg-gray-50 rounded-lg">
<div className="flex justify-between items-center">
<span className="text-gray-500">配送</span>
<span>{product.delivery}</span>
</div>
<div className="mt-2 flex justify-between items-center">
<span className="text-gray-500">服务</span>
<div className="flex gap-2">
{product.services.map((service, index) => (
<span key={index} className="text-sm text-gray-600">
{service}
</span>
))}
</div>
</div>
</div>
{/* 商品描述 */}
<div className="mt-6">
<h2 className="text-lg font-bold mb-2">商品详情</h2>
<p className="text-gray-600">{product.description}</p>
<h3 className="mt-4 font-bold">产品特点</h3>
<ul className="mt-2 space-y-2">
{product.features.map((feature, index) => (
<li key={index} className="flex items-center text-gray-600">
<span className="mr-2"></span>
{feature}
</li>
))}
</ul>
</div>
</div>
{/* 推荐商品模块 */}
<div className="p-4 bg-white mt-4">
<h2 className="text-lg font-bold mb-2">猜你喜欢</h2>
<div className="grid grid-cols-2 gap-4">
{recommendedProducts.map((item) => (
<Link key={item._id} href={`/good/${item._id}`}>
<div className="bg-gray-100 rounded-lg p-2 overflow-hidden">
<Image
src={item.imageUrl}
alt={item.title}
width={200}
height={200}
className="object-cover rounded h-32 w-full"
/>
<h3 className="text-sm font-medium mt-2">{item.title}</h3>
<p className="text-red-500">¥{item.price}</p>
</div>
</Link>
))}
</div>
</div>
{/* 底部购买栏 */}
<div className="fixed gap-4 bottom-0 left-0 right-0 flex items-center p-4 bg-white border-t">
<Link href="/tab">
<button className="flex-1 text-gray-500 flex flex-col items-center">
<AiOutlineHome className="mb-1 w-6 h-6" />
<span className="text-xs">首页</span>
</button>
</Link>
<Link href="/contact">
<button className="flex-1 text-gray-500 flex flex-col items-center">
<FiUser className="mb-1 w-6 h-6" />
<span className="text-xs">客服</span>
</button>
</Link>
<Link href="/tab/cart">
<button className="flex-1 text-gray-500 flex flex-col items-center">
<AiOutlineShoppingCart className="mb-1 w-6 h-6" />
<span className="text-xs">购物车</span>
</button>
</Link>
<button
className="flex-1 bg-yellow-400 text-white py-2 rounded-full"
onClick={handleAddToCartClick}
>
加入购物车
</button>
<button
className="flex-1 bg-red-500 text-white py-2 rounded-full"
onClick={handleBuyNow}
>
立即购买
</button>
</div>
<AddToCartModal
isOpen={showModal}
onClose={handleModalClose}
product={product}
quantity={quantity}
setQuantity={setQuantity}
onAddToCart={handleAddToCart}
/>
</div>
);
}