完善应用
This commit is contained in:
parent
3965330120
commit
5dce6fa08f
|
@ -132,7 +132,7 @@ export default function ProductDetail() {
|
|||
</div>
|
||||
|
||||
{/* 商品信息 */}
|
||||
<div className="flex-1 p-4 bg-white">
|
||||
<div className="flex-1 p-4 bg-white shadow-lg">
|
||||
<div className="flex items-center">
|
||||
<span className="text-2xl text-red-500 font-bold">
|
||||
¥{product.price}
|
||||
|
@ -186,6 +186,60 @@ export default function ProductDetail() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* 店铺模块 */}
|
||||
<div className="p-4 bg-white mt-4 rounded-lg shadow-lg flex flex-col">
|
||||
<Link href="/shop" className="flex flex-row items-center">
|
||||
<Image
|
||||
src="https://s2.loli.net/2025/02/25/G1yo27THWhNC5uf.jpg" // 替换为实际的店铺头像路径
|
||||
alt="店铺头像"
|
||||
width={80}
|
||||
height={80}
|
||||
className="rounded-lg border-2 border-yellow-400 mr-4"
|
||||
/>
|
||||
<div className="flex flex-col flex-1 justify-around items-start">
|
||||
<span className="text-lg font-semibold text-gray-800">
|
||||
宠物用品专卖店
|
||||
</span>
|
||||
<div className="flex items-center">
|
||||
<span className="mr-2 text-gray-600">评分:</span>
|
||||
<div className="flex">
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<svg
|
||||
key={index}
|
||||
className="w-5 h-5 text-yellow-500"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M10 15l-5.878 3.09 1.121-6.535L0 6.545l6.545-.955L10 0l2.455 5.59L20 6.545l-5.243 4.005 1.121 6.535z" />
|
||||
</svg>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-yellow-700 bg-yellow-400 px-1 rounded">
|
||||
官方正品旗舰店{" >"}
|
||||
</p>
|
||||
</div>
|
||||
<button className="bg-red-500 h-12 px-2 rounded-lg text-white">
|
||||
进店看看
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-row mt-2 justify-between">
|
||||
<div className="flex">
|
||||
<span className="text-gray-500">宝贝质量:</span>
|
||||
<span className="font-bold">4.9</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="text-gray-500">物流速度:</span>
|
||||
<span className="font-bold">5.0</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<span className="text-gray-500">服务保障:</span>
|
||||
<span className="font-bold">4.8</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 推荐商品模块 */}
|
||||
<div className="p-4 bg-white mt-4">
|
||||
<h2 className="text-lg font-bold mb-2">猜你喜欢</h2>
|
||||
|
|
|
@ -7,6 +7,13 @@ export const metadata = {
|
|||
keywords: "宠物用品,宠物商城,宠物食品,宠物玩具",
|
||||
};
|
||||
|
||||
export const viewport = {
|
||||
width: "device-width",
|
||||
initialScale: 1,
|
||||
maximumScale: 1,
|
||||
userScalable: 0,
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="zh-CN">
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export default function ShopPage() {
|
||||
const [products, setProducts] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [productsPerPage] = useState(10); // 每页显示的商品数量
|
||||
const [activeTab, setActiveTab] = useState("all"); // 新增的状态
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/products");
|
||||
if (!res.ok) {
|
||||
throw new Error("获取商品列表失败");
|
||||
}
|
||||
const data = await res.json();
|
||||
setProducts(data);
|
||||
} catch (err) {
|
||||
setError(err.message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchProducts();
|
||||
}, []);
|
||||
|
||||
const handleSearch = (e) => {
|
||||
setSearchTerm(e.target.value);
|
||||
};
|
||||
|
||||
const filteredProducts = products.filter((product) =>
|
||||
product.title.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const indexOfLastProduct = currentPage * productsPerPage;
|
||||
const indexOfFirstProduct = indexOfLastProduct - productsPerPage;
|
||||
const currentProducts = filteredProducts.slice(
|
||||
indexOfFirstProduct,
|
||||
indexOfLastProduct
|
||||
);
|
||||
|
||||
const paginate = (pageNumber) => setCurrentPage(pageNumber);
|
||||
|
||||
if (loading) return <div className="p-4">加载中...</div>;
|
||||
if (error) return <div className="p-4 text-red-500">错误: {error}</div>;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="搜索商品"
|
||||
value={searchTerm}
|
||||
onChange={handleSearch}
|
||||
className="mb-4 p-2 border rounded w-full"
|
||||
/>
|
||||
|
||||
{/* 店铺信息 */}
|
||||
<Link href="/shop/profile" className="flex flex-row items-center">
|
||||
<Image
|
||||
src="https://s2.loli.net/2025/02/25/G1yo27THWhNC5uf.jpg" // 替换为实际的店铺头像路径
|
||||
alt="店铺头像"
|
||||
width={60}
|
||||
height={60}
|
||||
className="rounded-lg border-2 border-yellow-400 mr-4"
|
||||
/>
|
||||
<div className="flex flex-col flex-1 justify-around items-start">
|
||||
<span className="text-lg font-semibold text-gray-800">
|
||||
宠物用品专卖店
|
||||
</span>
|
||||
<p className="text-yellow-700 bg-yellow-400 px-1 rounded">
|
||||
官方正品旗舰店{" >"}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
{/* Tab 切换栏 */}
|
||||
<div className="flex border-b mb-4">
|
||||
<button
|
||||
className={`flex-1 py-3 text-center ${
|
||||
activeTab === "all"
|
||||
? "text-yellow-500 border-b-2 border-yellow-500"
|
||||
: "text-gray-500"
|
||||
}`}
|
||||
onClick={() => setActiveTab("all")}
|
||||
>
|
||||
宝贝
|
||||
</button>
|
||||
<button
|
||||
className={`flex-1 py-3 text-center ${
|
||||
activeTab === "new"
|
||||
? "text-yellow-500 border-b-2 border-yellow-500"
|
||||
: "text-gray-500"
|
||||
}`}
|
||||
onClick={() => setActiveTab("new")}
|
||||
>
|
||||
新品
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`grid gap-4 ${
|
||||
activeTab === "all" ? "grid-cols-2" : "grid-cols-1"
|
||||
}`}
|
||||
>
|
||||
{currentProducts.map((product) => (
|
||||
<div
|
||||
key={product._id}
|
||||
className="bg-white rounded-lg shadow-md overflow-hidden"
|
||||
>
|
||||
<Link href={`/good/${product._id}`}>
|
||||
<div className="relative aspect-square">
|
||||
<Image
|
||||
src={product.imageUrl}
|
||||
alt={product.title}
|
||||
fill
|
||||
className="object-cover rounded-t-lg"
|
||||
/>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<h3 className="text-lg truncate font-medium mt-2 line-clamp-2">
|
||||
{product.title}
|
||||
</h3>
|
||||
<p className="text-red-500">¥{product.price}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
{[
|
||||
...Array(Math.ceil(filteredProducts.length / productsPerPage)).keys(),
|
||||
].map((number) => (
|
||||
<button
|
||||
key={number}
|
||||
onClick={() => paginate(number + 1)}
|
||||
className={`px-3 py-1 mx-1 ${
|
||||
currentPage === number + 1 ? "bg-yellow-400" : "bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
{number + 1}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
export default function ShopProfile() {
|
||||
const [showLicenseImage, setShowLicenseImage] = useState(false);
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 p-4">
|
||||
<div className="bg-white p-6 rounded-lg shadow-md">
|
||||
{/* 店铺头像和名称 */}
|
||||
<div className="flex flex-col items-center">
|
||||
<Image
|
||||
src="https://s2.loli.net/2025/02/25/G1yo27THWhNC5uf.jpg" // 替换为实际的店铺头像路径
|
||||
alt="店铺头像"
|
||||
width={100}
|
||||
height={100}
|
||||
className="rounded-lg border-2 border-yellow-400"
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<h1 className="text-2xl font-bold">宠物用品专卖店</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 店铺评分 */}
|
||||
<div className="mb-4 mt-2">
|
||||
<div className="flex flex-row mb-4 items-center justify-center gap-2">
|
||||
<h2 className="text-lg font-semibold">店铺评分</h2>
|
||||
<div className="flex">
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<svg
|
||||
key={index}
|
||||
className="w-5 h-5 text-yellow-500"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M10 15l-5.878 3.09 1.121-6.535L0 6.545l6.545-.955L10 0l2.455 5.59L20 6.545l-5.243 4.005 1.121 6.535z" />
|
||||
</svg>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between space-x-4 mt-2">
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500">宝贝质量</span>
|
||||
<span className="font-bold text-lg">4.9</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500">物流速度</span>
|
||||
<span className="font-bold">5.0</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<span className="text-gray-500">服务保障</span>
|
||||
<span className="font-bold">4.8</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 保证金金额 */}
|
||||
<div className="mb-4">
|
||||
<h2 className="text-lg font-semibold">保证金</h2>
|
||||
<p className="text-gray-700">¥10,000</p>
|
||||
</div>
|
||||
|
||||
{/* 店铺资质 */}
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">店铺资质</h2>
|
||||
<ul className="list-disc list-inside text-gray-700 mt-2">
|
||||
<li
|
||||
onClick={() => setShowLicenseImage(!showLicenseImage)}
|
||||
className="cursor-pointer text-blue-500"
|
||||
>
|
||||
营业执照
|
||||
</li>
|
||||
</ul>
|
||||
{showLicenseImage && (
|
||||
<div className="mt-4 flex justify-center">
|
||||
<Image
|
||||
src="https://s2.loli.net/2025/02/25/UIlGv2VKjhL3oDC.jpg"
|
||||
alt="营业执照"
|
||||
width={500}
|
||||
height={500}
|
||||
className="rounded-lg border-2 border-gray-300"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -20,7 +20,10 @@ export default function TabLayout({ children }) {
|
|||
<div className="flex flex-col h-screen">
|
||||
<div className="flex-1 overflow-auto">{children}</div>
|
||||
|
||||
<nav className="flex items-center justify-around py-2 bg-white border-t border-gray-200">
|
||||
<nav
|
||||
className="flex items-center justify-around py-2 bg-white border-t border-gray-200"
|
||||
style={{ paddingBottom: "env(safe-area-inset-bottom)" }}
|
||||
>
|
||||
<Link href="/tab" className={getLinkStyle("/tab")}>
|
||||
<AiOutlineHome className="w-6 h-6" />
|
||||
<span className="text-xs mt-1">首页</span>
|
||||
|
|
Loading…
Reference in New Issue