This commit is contained in:
root
2026-06-07 15:57:47 +08:00
parent cd885f5fcd
commit 9de2465793
50 changed files with 4942 additions and 169 deletions

View File

@@ -3,18 +3,26 @@
import { useState, useMemo, useSyncExternalStore } from "react";
import { useTranslation } from "@/app/digital/i18n/navigation";
import type { ResourceData } from "@/app/digital/lib/theme-data";
import DigitalThemeIcon, { type IconName } from "./DigitalThemeIcon";
const PER_PAGE_MOBILE = 4;
const PER_PAGE_PC = 8;
const BREAKPOINT = 768;
const panCategoryIconNames: Record<string, IconName> = {
docs: "file",
templates: "check",
firmware: "spark",
storage: "cloud",
};
function useIsPC() {
return useSyncExternalStore(
(callback) => {
if (typeof window === "undefined") return () => {};
const mq = window.matchMedia(`(min-width: ${BREAKPOINT}px)`);
const mq = window.matchMedia(`(min-width: ${BREAKPOINT}px)`);
const fn = () => callback();
mq.addEventListener("change", fn);
mq.addEventListener("change", fn);
return () => mq.removeEventListener("change", fn);
},
() =>
@@ -68,11 +76,11 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
}, [data.products, safeProductsPage, productsPerPage]);
return (
<div className="mt-12 space-y-16">
<div className="dn-resources mt-12 space-y-16">
{/* 图书 - 封面+书名,数量少则大封面,多则分页 */}
<div>
<h3 className="mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<span>📚</span>
<h3 className="dn-subsection-title mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<DigitalThemeIcon name="book" emoji="📚" className="h-5 w-5" />
{t("resourceSections.books")}
{data.books.length > 0 && (
<span className="text-sm font-normal text-slate-500 dark:text-slate-400">
@@ -88,7 +96,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
key={`book-${booksPage}-${i}`}
type="button"
onClick={showUpdating}
className="group flex cursor-not-allowed flex-col text-left"
className="dn-resource-tile group flex cursor-not-allowed flex-col text-left"
>
<div
className="overflow-hidden rounded-lg border border-slate-200 bg-slate-100 shadow-sm transition-all group-hover:shadow-md dark:border-slate-700 dark:bg-slate-800"
@@ -107,12 +115,12 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
))}
</div>
{booksTotalPages > 1 && (
<div className="mt-6 flex items-center justify-center gap-2">
<div className="dn-pagination mt-6 flex items-center justify-center gap-2">
<button
type="button"
onClick={() => setBooksPage((p) => Math.max(0, p - 1))}
disabled={safeBooksPage === 0}
className="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
className="dn-icon-button flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
aria-label="上一页"
>
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -126,7 +134,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
type="button"
onClick={() => setBooksPage((p) => Math.min(booksTotalPages - 1, p + 1))}
disabled={safeBooksPage === booksTotalPages - 1}
className="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
className="dn-icon-button flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
aria-label="下一页"
>
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -139,8 +147,8 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
{/* 商业数码 - 商品图+跳转电商 */}
<div>
<h3 className="mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<span>🛒</span>
<h3 className="dn-subsection-title mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<DigitalThemeIcon name="shopping" emoji="🛒" className="h-5 w-5" />
{t("resourceSections.products")}
{data.products.length > 0 && (
<span className="text-sm font-normal text-slate-500 dark:text-slate-400">
@@ -156,7 +164,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
key={`product-${productsPage}-${i}`}
type="button"
onClick={showUpdating}
className="group flex cursor-not-allowed flex-col text-left"
className="dn-resource-tile group flex cursor-not-allowed flex-col text-left"
>
<div
className="overflow-hidden rounded-xl border border-slate-200 bg-white p-3 shadow-sm transition-all group-hover:shadow-md dark:border-slate-700 dark:bg-slate-900"
@@ -175,12 +183,12 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
))}
</div>
{productsTotalPages > 1 && (
<div className="mt-6 flex items-center justify-center gap-2">
<div className="dn-pagination mt-6 flex items-center justify-center gap-2">
<button
type="button"
onClick={() => setProductsPage((p) => Math.max(0, p - 1))}
disabled={safeProductsPage === 0}
className="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
className="dn-icon-button flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
aria-label="上一页"
>
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -194,7 +202,7 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
type="button"
onClick={() => setProductsPage((p) => Math.min(productsTotalPages - 1, p + 1))}
disabled={safeProductsPage === productsTotalPages - 1}
className="flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
className="dn-icon-button flex h-10 w-10 items-center justify-center rounded-lg border border-slate-200 bg-white text-slate-600 transition-colors hover:bg-slate-50 disabled:opacity-40 disabled:cursor-not-allowed dark:border-slate-700 dark:bg-slate-800 dark:text-slate-400 dark:hover:bg-slate-700"
aria-label="下一页"
>
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
@@ -207,24 +215,28 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
{/* 网盘 - 分类 Tab */}
<div>
<h3 className="mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<span></span>
<h3 className="dn-subsection-title mb-4 flex items-center gap-2 text-xl font-bold text-slate-900 dark:text-slate-100">
<DigitalThemeIcon name="cloud" emoji="☁️" className="h-5 w-5" />
{t("resourceSections.pan")}
</h3>
<div className="rounded-2xl border border-slate-200 bg-white dark:border-slate-700 dark:bg-slate-900">
<div className="flex flex-wrap gap-1 border-b border-slate-200 p-2 dark:border-slate-700">
<div className="dn-card dn-pan rounded-2xl border border-slate-200 bg-white dark:border-slate-700 dark:bg-slate-900">
<div className="dn-tabs flex flex-wrap gap-1 border-b border-slate-200 p-2 dark:border-slate-700">
{data.pan.categories.map((cat) => (
<button
key={cat.id}
type="button"
onClick={() => setActivePanCategory(cat.id)}
className={`rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
className={`dn-tab rounded-lg px-4 py-2 text-sm font-medium transition-colors ${
activePanCategory === cat.id
? "bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-300"
? "dn-tab-active bg-sky-100 text-sky-700 dark:bg-sky-900/50 dark:text-sky-300"
: "text-slate-600 hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
}`}
>
<span className="mr-1.5">{cat.icon}</span>
<DigitalThemeIcon
name={panCategoryIconNames[cat.id] ?? "file"}
emoji={cat.icon}
className="mr-1.5 inline h-4 w-4"
/>
{cat.title}
</button>
))}
@@ -243,10 +255,14 @@ export default function ResourceNavigation({ data }: ResourceNavigationProps) {
href={item.href}
target="_blank"
rel="noopener noreferrer sponsored"
className="flex items-center gap-3 rounded-xl border border-slate-100 p-3 transition-colors hover:border-sky-200 hover:bg-sky-50/50 dark:border-slate-700 dark:hover:border-sky-800 dark:hover:bg-sky-900/20"
className="dn-resource-link flex items-center gap-3 rounded-xl border border-slate-100 p-3 transition-colors hover:border-sky-200 hover:bg-sky-50/50 dark:border-slate-700 dark:hover:border-sky-800 dark:hover:bg-sky-900/20"
>
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-100 text-lg dark:bg-slate-800">
{activeCategory.icon}
<div className="dn-icon-box flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-slate-100 text-lg dark:bg-slate-800">
<DigitalThemeIcon
name={panCategoryIconNames[activeCategory.id] ?? "file"}
emoji={activeCategory.icon}
className="h-5 w-5"
/>
</div>
<div className="min-w-0 flex-1">
<div className="font-medium text-slate-900 dark:text-slate-100">{item.name}</div>