From bb3dbde1c757add7acf1e725b1ff0529dfece911 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Wed, 1 Apr 2026 19:19:26 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=9B=E5=BB=BA=20Shell=20=E5=B8=83?= =?UTF-8?q?=E5=B1=80=E7=BB=84=E4=BB=B6=EF=BC=88=E4=BE=A7=E8=BE=B9=E6=A0=8F?= =?UTF-8?q?=20+=20=E5=BA=95=E9=83=A8=E5=AF=BC=E8=88=AA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- src/components/Shell.tsx | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 src/components/Shell.tsx diff --git a/src/components/Shell.tsx b/src/components/Shell.tsx new file mode 100644 index 0000000..5951931 --- /dev/null +++ b/src/components/Shell.tsx @@ -0,0 +1,84 @@ +import { useState, useEffect, type ComponentType } from 'react'; + +export interface ModuleConfig { + id: string; + label: string; + icon: ComponentType<{ size?: number; className?: string }>; + component: ComponentType; +} + +function getHashModule(modules: ModuleConfig[]): string { + const hash = window.location.hash.slice(1); + return modules.some((m) => m.id === hash) ? hash : modules[0]?.id ?? ''; +} + +export function Shell({ modules }: { modules: ModuleConfig[] }) { + const [activeModule, setActiveModule] = useState(() => getHashModule(modules)); + + useEffect(() => { + const onHashChange = () => setActiveModule(getHashModule(modules)); + window.addEventListener('hashchange', onHashChange); + return () => window.removeEventListener('hashchange', onHashChange); + }, [modules]); + + useEffect(() => { + if (!window.location.hash) { + window.location.hash = modules[0]?.id ?? ''; + } + }, [modules]); + + const switchModule = (id: string) => { + window.location.hash = id; + }; + + const ActiveComponent = modules.find((m) => m.id === activeModule)?.component ?? modules[0]?.component; + + return ( +
+ {/* Web 侧边栏 (md 及以上) */} + + + {/* 内容区 */} +
+ {ActiveComponent && } +
+ + {/* 移动端底部导航 (md 以下) */} + +
+ ); +}