添加控制台菜单加载态

This commit is contained in:
2025-06-19 14:55:58 +08:00
parent 43e814fbe7
commit da16bc1dbe
3 changed files with 34 additions and 31 deletions

View File

@@ -14,6 +14,7 @@ import {
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar"
import { UserApi } from "@/lib/api"; import { UserApi } from "@/lib/api";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect, useLayoutEffect } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import useSWR from "swr"; import useSWR from "swr";
@@ -22,41 +23,22 @@ export default function ConsoleMenuLayout({
}: { }: {
children: React.ReactNode children: React.ReactNode
}) { }) {
const isClientSide = typeof window !== 'undefined';
const router = useRouter(); const router = useRouter();
const { data: user, isLoading, error, mutate } = useSWR(
const getInitialData = () => {
if (!window || !window.localStorage) return null;
const cache = localStorage.getItem(UserApi.USER_ME_CACHE_KEY);
if (!cache) return;
try {
const user = JSON.parse(cache);
if (!user || !user.userId) throw new Error();
return user;
} catch (error) {
localStorage.removeItem(UserApi.USER_ME_CACHE_KEY);
}
return undefined;
}
const { data: user, isLoading, error } = useSWR(
'/api/user/me', '/api/user/me',
async () => { async () => UserApi.me(),
const data = await UserApi.me();
localStorage.setItem(UserApi.USER_ME_CACHE_KEY, JSON.stringify(data));
return data;
},
{ {
onError: (error) => { onError: (error) => {
if (error.statusCode === 401) { if (error.statusCode === 401) {
localStorage.removeItem('token'); if (isClientSide) {
localStorage.removeItem(UserApi.USER_ME_CACHE_KEY); localStorage.removeItem('token');
}
toast.info('登录凭证已失效,请重新登录'); toast.info('登录凭证已失效,请重新登录');
router.replace('/console/login'); router.replace('/console/login');
} }
}, },
fallbackData: getInitialData(),
revalidateIfStale: false, revalidateIfStale: false,
revalidateOnFocus: false, revalidateOnFocus: false,
revalidateOnReconnect: false, revalidateOnReconnect: false,

View File

@@ -4,6 +4,7 @@ import * as React from "react"
import { import {
CloudUpload, CloudUpload,
Inbox, Inbox,
LucideIcon,
Mail, Mail,
Server, Server,
SquareTerminal, SquareTerminal,
@@ -34,7 +35,22 @@ export function AppSidebar({ user, isUserLoading, ...props }: React.ComponentPro
email: "m@example.com", email: "m@example.com",
avatar: "/avatars/shadcn.jpg", avatar: "/avatars/shadcn.jpg",
}, },
navMain: [ navMain: null as null | {
title: string
url: string
icon?: LucideIcon
isActive?: boolean
isHidden?: boolean
items?: {
title: string
url: string
isHidden?: boolean
}[]
}[],
}
if (!isUserLoading) {
data.navMain = [
{ {
title: "网站管理", title: "网站管理",
url: "/console/web", url: "/console/web",
@@ -101,7 +117,7 @@ export function AppSidebar({ user, isUserLoading, ...props }: React.ComponentPro
url: "/", url: "/",
icon: Undo2, icon: Undo2,
}, },
], ]
} }
return ( return (

View File

@@ -18,13 +18,12 @@ import {
SidebarMenuSubItem, SidebarMenuSubItem,
} from "@/components/ui/sidebar" } from "@/components/ui/sidebar"
import Link from "next/link" import Link from "next/link"
import { usePathname } from "next/navigation" import { Skeleton } from "./ui/skeleton"
import { useState, useEffect } from "react"
export function NavMain({ export function NavMain({
items, items,
}: { }: {
items: { items: null | {
title: string title: string
url: string url: string
icon?: LucideIcon icon?: LucideIcon
@@ -41,7 +40,13 @@ export function NavMain({
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel></SidebarGroupLabel> <SidebarGroupLabel></SidebarGroupLabel>
<SidebarMenu> <SidebarMenu>
{items.filter(i => !i.isHidden).map((item) => ( {
!items && Array(5).fill(null).map((_, i) => (
<Skeleton key={i} className="w-full h-7 mt-1" />
))
}
{items && items.filter(i => !i.isHidden).map((item) => (
(item.items && item.items.length > 0) (item.items && item.items.length > 0)
? ( ? (
<Collapsible <Collapsible