import { useState, useEffect, useMemo, type ComponentType } from 'react'; import { useAuth } from '../auth/useAuth'; import { DemoModeProvider } from './Blur'; import FeedbackFab from './FeedbackFab'; export interface ModuleConfig { id: string; label: string; icon: ComponentType<{ size?: number; className?: string }>; component: ComponentType; } /** hash 一级段(`#` 或 `#/` 都只取 id) */ function getHashHead(): string { return window.location.hash.slice(1).split('/')[0]; } function getInitialModule(modules: ModuleConfig[]): string { const head = getHashHead(); if (modules.some((m) => m.id === head)) return head; return modules[0]?.id ?? ''; } function getHashModule(modules: ModuleConfig[]): string { const head = getHashHead(); return modules.some((m) => m.id === head) ? head : ''; } export function Shell({ modules }: { modules: ModuleConfig[] }) { const [activeModule, setActiveModule] = useState(() => getInitialModule(modules)); useEffect(() => { const onHashChange = () => { const h = getHashModule(modules); if (h) setActiveModule(h); }; window.addEventListener('hashchange', onHashChange); return () => window.removeEventListener('hashchange', onHashChange); }, [modules]); useEffect(() => { // 同步 hash 一段到当前模块:使用 replaceState 避免产生多余的 history 记录, // 否则在小程序/webview 环境下首次进入需要点两次返回才能退出。 // 注意只比对一级段,避免把子模块写入的 `#/` 二级段抹掉。 if (getHashHead() !== activeModule) { const { pathname, search } = window.location; window.history.replaceState(null, '', `${pathname}${search}#${activeModule}`); } }, [activeModule]); const switchModule = (id: string) => { if (getHashHead() === id) return; const { pathname, search } = window.location; window.history.replaceState(null, '', `${pathname}${search}#${id}`); setActiveModule(id); }; const ActiveComponent = modules.find((m) => m.id === activeModule)?.component ?? modules[0]?.component; const { user } = useAuth(); const watermarkText = useMemo(() => { const name = user?.userName || '未登录'; const time = new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-'); return `${name}-${time}`; }, [user]); return (
{/* 全局水印 */}
${watermarkText}`)}")`, backgroundRepeat: 'repeat', }} />
{/* Web 侧边栏 (md 及以上) */} {/* 内容区 */}
{ActiveComponent && }
{/* 移动端底部导航 (md 以下) */}
); }