157 lines
4.6 KiB
JavaScript
157 lines
4.6 KiB
JavaScript
"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>
|
|
);
|
|
}
|