完善功能

This commit is contained in:
yezian 2025-02-11 16:50:04 +08:00
parent a568ed012f
commit 608391a9af
11 changed files with 160 additions and 36 deletions

View File

@ -3,6 +3,7 @@
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();
@ -11,6 +12,7 @@ function CheckoutContent() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [address, setAddress] = useState(null);
const [showPaymentModal, setShowPaymentModal] = useState(false);
//
const fetchAddress = async (token) => {
@ -129,6 +131,10 @@ function CheckoutContent() {
)}&${params.toString()}`;
};
const handleSubmitOrder = () => {
setShowPaymentModal(true);
};
return (
<div className="min-h-screen bg-gray-50 pb-20">
<div className="p-4">
@ -215,10 +221,17 @@ function CheckoutContent() {
<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>
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -10,5 +10,4 @@
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}

View File

@ -24,7 +24,6 @@ export default function ProductDetail() {
throw new Error("获取商品详情失败");
}
const data = await res.json();
console.log(data);
setProduct(data);
} catch (err) {
setError(err.message);

View File

@ -1,28 +1,16 @@
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { Toaster } from "react-hot-toast";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "宠物用品商城",
description: "专业的宠物用品购物平台,为您的宠物提供优质商品",
keywords: "宠物用品,宠物商城,宠物食品,宠物玩具",
};
export default function RootLayout({ children }) {
return (
<html lang="en" data-theme="light">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<html lang="zh-CN">
<body>
{children}
<Toaster
toastOptions={{

View File

@ -1,5 +1,20 @@
import Image from "next/image";
"use client";
import { useSearchParams, useRouter } from "next/navigation";
import { useEffect } from "react";
export default function Home() {
const searchParams = useSearchParams();
const router = useRouter();
useEffect(() => {
const url = searchParams.get("url");
if (url) {
router.replace(decodeURIComponent(url));
} else if (!searchParams.toString()) {
router.replace("/tab");
}
}, [searchParams, router]);
return <div></div>;
}

View File

@ -8,6 +8,7 @@ export default function Profile() {
const [isLoggedIn, setIsLoggedIn] = React.useState(false);
const [userData, setUserData] = React.useState(null);
const [points, setPoints] = useState(0);
const [currentImageIndex, setCurrentImageIndex] = useState(0);
useEffect(() => {
//
@ -44,6 +45,14 @@ export default function Profile() {
fetchUserPoints();
}, []);
useEffect(() => {
const timer = setInterval(() => {
setCurrentImageIndex((prevIndex) => (prevIndex + 1) % 3);
}, 3000);
return () => clearInterval(timer);
}, []);
return (
<div className="flex flex-col min-h-screen bg-gray-50">
{/* 顶部用户信息区域 */}
@ -51,7 +60,10 @@ export default function Profile() {
{isLoggedIn && userData ? (
<Link href="/user/profile" className="flex items-center gap-6">
<Image
src={userData.avatar || "https://imageplaceholder.net/150x150"}
src={
userData.avatar ||
"https://s2.loli.net/2025/02/11/miDGcVpHvQUzNwr.jpg"
}
alt="avatar"
width={80}
height={80}
@ -169,6 +181,45 @@ export default function Profile() {
</Link>
</div>
</div>
{/* 宠物相册区域 */}
<div className="p-4">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-bold">推荐商品</h3>
</div>
<div className="relative w-full h-48 rounded-lg overflow-hidden">
{[
"https://s2.loli.net/2025/02/11/QCxNUXcDaPeRohE.png",
"https://s2.loli.net/2025/02/11/FdX5giEp9DjGx4J.png",
"https://s2.loli.net/2025/02/11/9zRiqkcn43BjHvp.png",
].map((url, index) => (
<Link href="/tab" key={index}>
<Image
src={url}
alt={`商品 ${index + 1}`}
fill
className={`object-cover transition-opacity duration-500 ${
index === currentImageIndex ? "opacity-100" : "opacity-0"
}`}
/>
</Link>
))}
<div className="absolute bottom-2 left-0 right-0 flex justify-center gap-1">
{[0, 1, 2].map((index) => (
<div
key={index}
className={`w-1.5 h-1.5 rounded-full ${
index === currentImageIndex
? "bg-yellow-400"
: "bg-white opacity-60"
}`}
/>
))}
</div>
</div>
</div>
</div>
);
}

View File

@ -32,7 +32,10 @@ export default function UserProfile() {
<div className="bg-yellow-400 p-8">
<div className="flex items-center gap-6">
<Image
src={userData.avatar || "https://imageplaceholder.net/150x150"}
src={
userData.avatar ||
"https://s2.loli.net/2025/02/11/miDGcVpHvQUzNwr.jpg"
}
alt="avatar"
width={80}
height={80}

View File

@ -0,0 +1,56 @@
import React from "react";
import toast from "react-hot-toast";
export default function PaymentModal({ isOpen, onClose, amount }) {
if (!isOpen) return null;
const handlePayment = () => {
toast.error("微信支付接口调用失败");
onClose();
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center">
<div className="bg-white w-[90%] max-w-md rounded-lg">
<div className="p-4 border-b">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium">支付方式</h3>
<button onClick={onClose} className="text-gray-400">
</button>
</div>
</div>
<div className="p-4">
<div className="text-center mb-4">
<p>支付金额</p>
<p className="text-xl font-bold">{amount.toFixed(2)}</p>
</div>
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg mb-4">
<div className="flex items-center">
<svg
className="w-6 h-6 mr-3 text-[#07c160]"
viewBox="0 0 28 28"
fill="currentColor"
>
<path d="M9.442 15.027c-0.12 0.060-0.239 0.088-0.359 0.088-0.239 0-0.458-0.109-0.598-0.309l-0.050-0.070-2.157-4.633c-0.020-0.040-0.020-0.070-0.020-0.109 0-0.199 0.159-0.359 0.359-0.359 0.080 0 0.149 0.020 0.219 0.060l2.546 1.796c0.189 0.129 0.408 0.199 0.648 0.199 0.189 0 0.369-0.050 0.527-0.139l12.036-7.284c-2.267-2.616-5.821-4.314-9.843-4.314-7.005 0-12.69 5.247-12.69 11.721 0 3.514 1.796 6.668 4.593 8.743 0.219 0.159 0.359 0.408 0.359 0.697 0 0.080-0.020 0.159-0.040 0.229-0.129 0.478-0.349 1.264-0.359 1.294-0.020 0.070-0.040 0.149-0.040 0.219 0 0.199 0.159 0.359 0.359 0.359 0.070 0 0.139-0.020 0.199-0.070l2.367-1.365c0.189-0.109 0.408-0.169 0.627-0.169 0.109 0 0.229 0.020 0.329 0.050 1.614 0.478 3.347 0.737 5.148 0.737 7.005 0 12.69-5.247 12.69-11.721 0-1.944-0.548-3.769-1.505-5.375l-14.342 8.693z" />
</svg>
<span>微信支付</span>
</div>
<div className="w-6 h-6 rounded-full border-2 border-yellow-400 flex items-center justify-center">
<div className="w-3 h-3 bg-yellow-400 rounded-full"></div>
</div>
</div>
<button
className="w-full bg-yellow-400 text-white py-3 rounded-full"
onClick={handlePayment}
>
确认支付
</button>
</div>
</div>
</div>
);
}

View File

@ -1,13 +0,0 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "imageplaceholder.net",
},
],
},
};
module.exports = nextConfig;

View File

@ -1,4 +1,17 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "imageplaceholder.net",
},
{
protocol: "https",
hostname: "s2.loli.net",
},
],
},
};
export default nextConfig;