134 lines
3.7 KiB
TypeScript
Raw Normal View History

2026-06-22 14:43:46 +08:00
import type { Metadata } from 'next';
import { publicApi } from '@/lib/services';
import { ProductCard } from '@/components/front/ProductCard';
export const metadata: Metadata = {
title: '产品中心',
description: '查看我们的全部产品与解决方案。',
};
export const revalidate = 60;
interface PageProps {
searchParams: { categoryId?: string; keyword?: string; page?: string };
}
export default async function ProductsPage({ searchParams }: PageProps) {
const page = Number(searchParams.page ?? 1);
const categoryId = searchParams.categoryId ? Number(searchParams.categoryId) : undefined;
const [categories, productsRes] = await Promise.all([
publicApi.getProductCategories().catch(() => []),
publicApi
.getProducts({
page,
pageSize: 12,
categoryId,
keyword: searchParams.keyword,
})
.catch(() => ({ list: [], total: 0, page, pageSize: 12 })),
]);
return (
<div className="container-page py-12">
<h1 className="text-3xl font-bold text-gray-900"></h1>
<p className="mt-2 text-sm text-gray-500">
{productsRes.total}
</p>
{/* 分类筛选 */}
<div className="mt-6 flex flex-wrap gap-2">
<a
href="/products"
className={`rounded-full border px-3 py-1 text-sm ${
!categoryId
? 'border-brand-600 bg-brand-50 text-brand-700'
: 'border-gray-200 text-gray-600 hover:border-brand-300'
}`}
>
</a>
{categories.map((c) => (
<a
key={c.id}
href={`/products?categoryId=${c.id}`}
className={`rounded-full border px-3 py-1 text-sm ${
categoryId === c.id
? 'border-brand-600 bg-brand-50 text-brand-700'
: 'border-gray-200 text-gray-600 hover:border-brand-300'
}`}
>
{c.name}
</a>
))}
</div>
{/* 列表 */}
<div className="mt-8 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{productsRes.list.map((p) => (
<ProductCard key={p.id} product={p} />
))}
</div>
{productsRes.list.length === 0 && (
<div className="mt-8 flex h-32 items-center justify-center rounded-md border border-dashed text-sm text-gray-400">
</div>
)}
{/* 分页 */}
{productsRes.total > 12 && (
<Pagination
page={page}
total={productsRes.total}
pageSize={12}
baseQuery={{ categoryId: searchParams.categoryId, keyword: searchParams.keyword }}
/>
)}
</div>
);
}
function Pagination({
page,
total,
pageSize,
baseQuery,
}: {
page: number;
total: number;
pageSize: number;
baseQuery: Record<string, string | undefined>;
}) {
const totalPages = Math.ceil(total / pageSize);
const buildHref = (p: number) => {
const qs = new URLSearchParams({
...(baseQuery as Record<string, string>),
page: String(p),
}).toString();
return `/products?${qs}`;
};
return (
<div className="mt-8 flex items-center justify-center gap-2">
<a
href={buildHref(Math.max(1, page - 1))}
className={`rounded border px-3 py-1 text-sm ${
page <= 1 ? 'pointer-events-none opacity-50' : 'hover:bg-gray-50'
}`}
>
</a>
<span className="text-sm text-gray-600">
{page} / {totalPages}
</span>
<a
href={buildHref(Math.min(totalPages, page + 1))}
className={`rounded border px-3 py-1 text-sm ${
page >= totalPages ? 'pointer-events-none opacity-50' : 'hover:bg-gray-50'
}`}
>
</a>
</div>
);
}