"use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter, usePathname } from "next/navigation"; import Link from "next/link"; import { getToken, getUser, logout } from "@/lib/auth"; interface ViewportItem { key: string; label: string; route: string; icon: string | null; sortOrder: string; requiredPermission: string | null; } export default function AppShell({ children }: { children: React.ReactNode }) { const router = useRouter(); const pathname = usePathname(); const [viewports, setViewports] = useState([]); const [loading, setLoading] = useState(true); const [mounted, setMounted] = useState(false); const fetchViewports = useCallback(async () => { const token = getToken(); if (!token) { router.replace("/login"); return; } try { const res = await fetch("/api/v1/teacher/viewports", { headers: { Authorization: `Bearer ${token}` }, }); if (res.status === 401) { logout(); return; } const json = await res.json(); if (json.success && json.data) { setViewports(json.data); } } catch { // 网络错误,保持空视口 } finally { setLoading(false); } }, [router]); useEffect(() => { setMounted(true); const token = getToken(); if (!token) { router.replace("/login"); return; } fetchViewports(); }, [router, fetchViewports]); // 防止 SSR 闪烁 if (!mounted) return null; const user = getUser(); return (
{/* 左侧栏:导航树 */} {/* 中间:内容区(纸面) */}
{children}
); }