247 lines
7.2 KiB
JavaScript
247 lines
7.2 KiB
JavaScript
"use client";
|
||
|
||
import { useEffect, useState, Suspense } from "react";
|
||
import { useRouter, useSearchParams } from "next/navigation";
|
||
import Image from "next/image";
|
||
import PaymentModal from "@/components/PaymentModal";
|
||
|
||
function CheckoutContent() {
|
||
const router = useRouter();
|
||
const searchParams = useSearchParams();
|
||
const [products, setProducts] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
const [error, setError] = useState(null);
|
||
const [address, setAddress] = useState(null);
|
||
const [showPaymentModal, setShowPaymentModal] = useState(false);
|
||
|
||
// 获取地址的函数
|
||
const fetchAddress = async (token) => {
|
||
const addressRes = await fetch("/api/address", {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
});
|
||
|
||
if (addressRes.ok) {
|
||
const addresses = await addressRes.json();
|
||
const selectedAddress = localStorage.getItem("selectedAddress");
|
||
|
||
if (selectedAddress) {
|
||
const parsedSelectedAddress = JSON.parse(selectedAddress);
|
||
// 在服务器返回的地址列表中查找选中的地址
|
||
const matchedAddress = addresses.find(
|
||
(addr) => addr._id === parsedSelectedAddress._id
|
||
);
|
||
if (matchedAddress) {
|
||
setAddress(matchedAddress);
|
||
localStorage.removeItem("selectedAddress");
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 如果没有匹配到选中的地址,使用默认地址
|
||
const defaultAddress = addresses.find((addr) => addr.isDefault);
|
||
if (defaultAddress) {
|
||
setAddress(defaultAddress);
|
||
} else if (addresses.length > 0) {
|
||
setAddress(addresses[0]);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 获取商品信息的函数
|
||
const fetchProducts = async () => {
|
||
const items = searchParams.get("items");
|
||
if (items) {
|
||
const productPromises = items.split(",").map(async (item) => {
|
||
const [productId, quantity] = item.split(":");
|
||
const res = await fetch(`/api/products/${productId}`);
|
||
if (!res.ok) {
|
||
throw new Error("获取商品信息失败");
|
||
}
|
||
const product = await res.json();
|
||
return { ...product, quantity: parseInt(quantity) };
|
||
});
|
||
|
||
const productsData = await Promise.all(productPromises);
|
||
setProducts(productsData);
|
||
return;
|
||
}
|
||
|
||
const productId = searchParams.get("products");
|
||
const quantity = searchParams.get("quantity");
|
||
|
||
if (!productId || !quantity) {
|
||
throw new Error("商品信息不完整");
|
||
}
|
||
|
||
const res = await fetch(`/api/products/${productId}`);
|
||
if (!res.ok) {
|
||
throw new Error("获取商品信息失败");
|
||
}
|
||
|
||
const product = await res.json();
|
||
setProducts([{ ...product, quantity: parseInt(quantity) }]);
|
||
};
|
||
|
||
useEffect(() => {
|
||
const token = localStorage.getItem("token");
|
||
if (!token) {
|
||
router.push("/login");
|
||
return;
|
||
}
|
||
|
||
const fetchData = async () => {
|
||
try {
|
||
await fetchAddress(token);
|
||
await fetchProducts();
|
||
} catch (err) {
|
||
setError(err.message);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
fetchData();
|
||
}, []);
|
||
|
||
if (loading) return <div className="p-4">加载中...</div>;
|
||
if (error) return <div className="p-4 text-red-500">错误: {error}</div>;
|
||
|
||
const total = products.reduce(
|
||
(sum, item) => sum + item.price * item.quantity,
|
||
0
|
||
);
|
||
|
||
const getReturnUrl = () => {
|
||
const items = searchParams.get("items");
|
||
const products = searchParams.get("products");
|
||
const quantity = searchParams.get("quantity");
|
||
|
||
let params = new URLSearchParams();
|
||
if (items) {
|
||
params.append("items", items);
|
||
} else if (products && quantity) {
|
||
params.append("products", products);
|
||
params.append("quantity", quantity);
|
||
}
|
||
|
||
return `/address?returnUrl=${encodeURIComponent(
|
||
"/checkout"
|
||
)}&${params.toString()}`;
|
||
};
|
||
|
||
const handleSubmitOrder = () => {
|
||
setShowPaymentModal(true);
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50 pb-20">
|
||
<div className="p-4">
|
||
<h1 className="text-xl font-bold mb-4">确认订单</h1>
|
||
|
||
{/* 收货地址 */}
|
||
<button
|
||
onClick={() => router.replace(getReturnUrl())}
|
||
className="block w-full bg-white rounded-lg p-4 mb-4 text-left"
|
||
>
|
||
{address ? (
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<div className="flex items-center gap-2">
|
||
<span className="font-medium">{address.name}</span>
|
||
<span>{address.phone}</span>
|
||
</div>
|
||
<div className="text-gray-500 text-sm mt-1">
|
||
{address.address}
|
||
</div>
|
||
</div>
|
||
<svg
|
||
className="w-4 h-4 text-gray-400"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M9 5l7 7-7 7"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
) : (
|
||
<div className="flex justify-between items-center text-gray-500">
|
||
<span>请选择收货地址</span>
|
||
<svg
|
||
className="w-4 h-4"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
viewBox="0 0 24 24"
|
||
>
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M9 5l7 7-7 7"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
)}
|
||
</button>
|
||
|
||
{/* 商品列表 */}
|
||
<div className="bg-white rounded-lg p-4 mb-4">
|
||
{products.map((product) => (
|
||
<div key={product._id} className="flex items-center">
|
||
<Image
|
||
src={product.imageUrl}
|
||
alt={product.title}
|
||
width={80}
|
||
height={80}
|
||
className="rounded"
|
||
/>
|
||
<div className="ml-4 flex-1">
|
||
<h3 className="font-medium">{product.title}</h3>
|
||
<div className="flex justify-between mt-2">
|
||
<span className="text-red-500">¥{product.price}</span>
|
||
<span>x{product.quantity}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 订单总计 */}
|
||
<div className="fixed bottom-0 left-0 right-0 bg-white p-4 border-t">
|
||
<div className="flex justify-between items-center mb-4">
|
||
<span>总计:</span>
|
||
<span className="text-xl text-red-500 font-bold">¥{total}</span>
|
||
</div>
|
||
<button
|
||
className="w-full bg-red-500 text-white py-3 rounded-full"
|
||
disabled={!address}
|
||
onClick={handleSubmitOrder}
|
||
>
|
||
提交订单
|
||
</button>
|
||
</div>
|
||
|
||
<PaymentModal
|
||
isOpen={showPaymentModal}
|
||
onClose={() => setShowPaymentModal(false)}
|
||
amount={total}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default function Checkout() {
|
||
return (
|
||
<Suspense fallback={<div className="p-4">加载中...</div>}>
|
||
<CheckoutContent />
|
||
</Suspense>
|
||
);
|
||
}
|