Initial commit from Create vite

This commit is contained in:
Eamon
2026-07-24 18:00:22 +08:00
commit 0361853732
79 changed files with 4803 additions and 0 deletions

133
src/home/product/index.tsx Normal file
View File

@@ -0,0 +1,133 @@
import { useState } from "react"
import { useSearchParams } from "react-router-dom"
import Wrap from "@/components/wrap"
import { PRODUCT_DATA } from "@/lib/models/product"
import { cn } from "@/lib/utils"
import ProductGrid from "./productGrid"
import ProductSidebar from "./productSidebar"
import ProductTabs from "./productTabs"
import PurchaseInfo from "./purchaseInfo"
export default function ProductPage() {
const [searchParams, setSearchParams] = useSearchParams()
const [activeVersionId, setActiveVersionId] = useState<string>("v1")
const activeTabId = searchParams.get("tab") || "dynamic"
const activeBrandId = searchParams.get("brand") || ""
const currentTab =
PRODUCT_DATA.find(t => t.id === activeTabId) || PRODUCT_DATA[0]
const currentBrand =
currentTab.brands.find(b => b.id === activeBrandId) || currentTab.brands[0]
const handleTabChange = (tabId: string) => {
const targetTab = PRODUCT_DATA.find(t => t.id === tabId)
const firstBrand = targetTab?.brands[0]
setSearchParams({ tab: tabId, brand: firstBrand?.id || "" })
setActiveVersionId("v1")
}
const handleBrandSelect = (brandId: string) => {
setSearchParams({ tab: activeTabId, brand: brandId })
setActiveVersionId("v1")
}
const getDisplayProducts = () => {
if (!currentBrand) return []
if (currentBrand.versions && currentBrand.versions.length > 0) {
return currentBrand.products.filter(p => p.version === activeVersionId)
}
return currentBrand.products
}
const displayProducts = getDisplayProducts()
const isPurchaseInfo = currentBrand?.id === "info"
return (
<Wrap className="flex flex-col gap-16 pt-30 bg-white">
<div className="flex flex-col">
<ProductTabs
tabs={PRODUCT_DATA}
activeId={activeTabId}
onTabChange={handleTabChange}
/>
<div className="flex mt-8 mb-8 mx-auto w-full">
<div className="shrink-0 border-r border-gray-200">
<ProductSidebar
brands={currentTab.brands}
activeId={currentBrand?.id || ""}
onSelect={handleBrandSelect}
/>
</div>
<div className="flex-1 flex flex-col">
{!isPurchaseInfo ? (
<>
{" "}
<div className="grid grid-cols-2 pl-3 text-sm gap-2 text-gray-600 leading-relaxed">
<ul className="space-y-2">
<li className="flex items-start gap-2">
<span className="w-2 h-2 mt-1.5 bg-emerald-400 rounded-full shrink-0" />
线
</li>
<li className="flex items-start gap-2">
<span className="w-2 h-2 mt-1.5 bg-emerald-400 rounded-full shrink-0" />
</li>
</ul>
<ul className="space-y-2">
<li className="flex items-start gap-2">
<span className="w-2 h-2 mt-1.5 bg-emerald-400 rounded-full shrink-0" />
IP池超大且纯净线
</li>
<li className="flex items-start gap-2">
<span className="w-2 h-2 mt-1.5 bg-emerald-400 rounded-full shrink-0" />
230+500+线
</li>
</ul>
</div>
<div className="bg-orange-50 border gap-3 border-orange-100 text-orange-400 p-3 text-center font-bold mt-3 mb-3 text-sm w-full">
5
</div>
{currentBrand?.versions && currentBrand.versions.length > 0 && (
<div className="w-full mt-2 pl-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{currentBrand.versions.map(v => (
<button
key={v.id}
onClick={() => setActiveVersionId(v.id)}
className={cn(
"w-full py-2 rounded-full text-sm font-medium transition-all cursor-pointer text-center",
activeVersionId === v.id
? "bg-linear-to-r from-blue-500 to-cyan-400 text-white shadow-sm"
: "bg-gray-100 text-gray-600 hover:bg-gray-200",
)}
>
{v.label}
</button>
))}
</div>
</div>
)}
<main className="pl-6">
{displayProducts.length > 0 ? (
<ProductGrid products={displayProducts} />
) : (
<div className="text-center py-20 text-gray-400">
</div>
)}
</main>
</>
) : (
<div className="pl-6 pr-6">
<PurchaseInfo />
</div>
)}
</div>
</div>
</div>
</Wrap>
)
}