init: 初始化 AssetX 项目仓库
包含 webapp(Next.js 用户端)、webapp-back(Go 后端)、 antdesign(管理后台)、landingpage(营销落地页)、 数据库 SQL 和配置文件。
This commit is contained in:
75
webapp/app/market/product/[id]/page.tsx
Normal file
75
webapp/app/market/product/[id]/page.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useParams } from "next/navigation";
|
||||
import Sidebar from "@/components/layout/Sidebar";
|
||||
import TopBar from "@/components/layout/TopBar";
|
||||
import ContentSection from "@/components/product/ContentSection";
|
||||
import ProductDetailSkeleton from "@/components/product/ProductDetailSkeleton";
|
||||
import { fetchProductDetail, ProductDetail } from "@/lib/api/fundmarket";
|
||||
|
||||
export default function ProductDetailPage() {
|
||||
const params = useParams();
|
||||
const productId = params.id as string;
|
||||
const [product, setProduct] = useState<ProductDetail | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadProduct() {
|
||||
if (!productId) return;
|
||||
|
||||
setLoading(true);
|
||||
const data = await fetchProductDetail(parseInt(productId));
|
||||
setProduct(data);
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
loadProduct();
|
||||
}, [productId]);
|
||||
|
||||
const breadcrumbItems = [
|
||||
{ label: "ASSETX", href: "/market" },
|
||||
{ label: "Fund Market", href: "/market" },
|
||||
{ label: product?.name || "Loading..." },
|
||||
];
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-subtle dark:bg-gray-900 flex">
|
||||
<Sidebar />
|
||||
<div className="flex-1 min-w-0 flex flex-col md:ml-[240px] pt-14 md:pt-0">
|
||||
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-4 md:px-8 py-3 md:py-6">
|
||||
<TopBar breadcrumbItems={breadcrumbItems} />
|
||||
</div>
|
||||
<div className="flex-1 px-4 py-4 md:px-8 md:py-8 bg-[#F3F4F6] dark:bg-gray-900 overflow-x-hidden">
|
||||
<ProductDetailSkeleton />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!product) {
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-subtle dark:bg-gray-900 flex items-center justify-center">
|
||||
<div className="text-text-primary dark:text-white">Product not found</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-bg-subtle dark:bg-gray-900 flex">
|
||||
<Sidebar />
|
||||
<div className="flex-1 min-w-0 flex flex-col md:ml-[240px] pt-14 md:pt-0">
|
||||
<div className="bg-[#F3F4F6] dark:bg-gray-800 border-b border-border-normal dark:border-gray-700 px-4 md:px-8 py-3 md:py-6">
|
||||
<TopBar breadcrumbItems={breadcrumbItems} />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 px-4 py-4 md:px-8 md:py-8 bg-[#F3F4F6] dark:bg-gray-900 overflow-x-hidden">
|
||||
{/* Tab Navigation and Content */}
|
||||
<ContentSection product={product} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user