/** * # 路由 /shop/ 全部产品列表页 * 原站 /shop/ 形态:左栏属性筛选(复选框,URL search params 驱动)+ 主区 * 「IoT Products / Showing x–y of z results」+ 产品网格 + 分页。 */ import { createFileRoute, useRouter } from "@tanstack/react-router"; import { getLayoutData, Layout } from "~/components/website/Layout"; import { ProductCard } from "~/components/website/ProductCard"; import { Pagination } from "~/components/website/Pagination"; import { Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator } from "~/components/ui/breadcrumb"; import { parseCollectionSearch, searchToFilters, filtersToSearch, type CollectionSearch } from "~/components/website/collection-shared"; import { getFilterConfig, listProducts, type FilterState, type ProductListResult } from "~/server/data"; import { useT } from "~/i18n"; export const Route = createFileRoute("/shop")({ validateSearch: parseCollectionSearch as (search: Record) => CollectionSearch, loaderDeps: ({ search }) => ({ search }), loader: async ({ deps }) => { const [layout, filterConfig] = await Promise.all([ getLayoutData(), getFilterConfig().catch(() => []), ]); const list = await listProducts({ filters: searchToFilters(deps.search), page: deps.search.page, pageSize: 24, }).catch(() => ({ items: [], total: 0, page: 1, pageSize: 24 }) as ProductListResult); return { layout, filterConfig, list }; }, head: () => ({ meta: [ { title: "All Products - DusunIoT" }, { name: "description", content: "Browse all Dusun IoT gateways and modules." }, ], }), component: ShopPage, }); /** # 函数 组件 /shop/ 页面 */ function ShopPage() { const t = useT(); const data = Route.useLoaderData(); const search = Route.useSearch(); const router = useRouter(); const page = data.list.page; const from = (page - 1) * data.list.pageSize + (data.list.items.length ? 1 : 0); const to = (page - 1) * data.list.pageSize + data.list.items.length; /** # 函数 更新筛选:切换某属性值后 navigate(重置到第 1 页) */ function toggleFilter(attrSlug: string, valueSlug: string) { const filters: FilterState = searchToFilters(search); const cur = filters[attrSlug] || []; const next = cur.includes(valueSlug) ? cur.filter((v) => v !== valueSlug) : [...cur, valueSlug]; if (next.length) filters[attrSlug] = next; else delete filters[attrSlug]; router.navigate({ to: "/shop/", search: filtersToSearch(filters, 1) }); } return (
{t("nav_home")} {t("nav_shop")}
{/* 左栏属性筛选(与原站 /shop/ 一致:仅属性过滤器,无分类树) */}

{t("shop_iot_products")}

{data.list.total > 0 ? t("shop_showing", { from, to, total: data.list.total }) : t("shop_no_results")}

{data.list.items.map((p) => ( ))}
{data.list.items.length === 0 ? (

No products found with the selected filters.

) : null} (n > 1 ? `/shop/?page=${n}` : "/shop/")} />
); }