mirror of
https://github.com/snowykami/neo-blog.git
synced 2025-09-26 11:06:23 +00:00
Merge pull request #36 from snowykami/fix/30
feat: 重构侧边栏组件,合并 NavMain 和 NavUserCenter 为 NavGroup,添加动态激活状态
This commit is contained in:
@ -4,7 +4,7 @@ import {
|
||||
IconInnerShadowTop,
|
||||
} from "@tabler/icons-react"
|
||||
|
||||
import { NavMain } from "@/components/console/nav-main"
|
||||
import { NavGroup } from "@/components/console/nav-group"
|
||||
import { NavUser } from "@/components/console/nav-user"
|
||||
import {
|
||||
Sidebar,
|
||||
@ -17,13 +17,16 @@ import {
|
||||
} from "@/components/ui/sidebar"
|
||||
import config from "@/config"
|
||||
import Link from "next/link"
|
||||
import { NavUserCenter } from "./nav-ucenter"
|
||||
import { sidebarData } from "./data"
|
||||
import { ThemeModeToggle } from "../common/theme-toggle"
|
||||
import { useState } from "react"
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
|
||||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const [activeId, setActiveId] = useState("dashboard")
|
||||
const consoleT = useTranslations("Console")
|
||||
return (
|
||||
<Sidebar collapsible="offcanvas" {...props}>
|
||||
<SidebarHeader>
|
||||
@ -42,8 +45,8 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
</SidebarMenu>
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={sidebarData.navMain} />
|
||||
<NavUserCenter items={sidebarData.navUserCenter} />
|
||||
<NavGroup title={consoleT("general")} activeId={activeId} setActiveId={setActiveId} items={sidebarData.navMain.map((item) => ({...item, title: consoleT(item.title)}))} />
|
||||
<NavGroup title={consoleT("personal")} activeId={activeId} setActiveId={setActiveId} items={sidebarData.navUserCenter.map((item) => ({...item, title: consoleT(item.title)}))} />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<div className="mb-2 flex justify-center">
|
||||
|
@ -6,6 +6,7 @@ import { Folder, Gauge, MessageCircle, Newspaper, Palette, Settings, ShieldCheck
|
||||
|
||||
|
||||
export interface SidebarItem {
|
||||
id: string;
|
||||
title: string;
|
||||
url: string;
|
||||
icon: IconType;
|
||||
@ -15,36 +16,42 @@ export interface SidebarItem {
|
||||
export const sidebarData: { navMain: SidebarItem[]; navUserCenter: SidebarItem[] } = {
|
||||
navMain: [
|
||||
{
|
||||
id: "dashboard",
|
||||
title: "dashboard.title",
|
||||
url: consolePath.dashboard,
|
||||
icon: Gauge,
|
||||
permission: isAdmin
|
||||
},
|
||||
{
|
||||
id: "post",
|
||||
title: "post.title",
|
||||
url: consolePath.post,
|
||||
icon: Newspaper,
|
||||
permission: isEditor
|
||||
},
|
||||
{
|
||||
id: "comment",
|
||||
title: "comment.title",
|
||||
url: consolePath.comment,
|
||||
icon: MessageCircle,
|
||||
permission: isEditor
|
||||
},
|
||||
{
|
||||
id: "file",
|
||||
title: "file.title",
|
||||
url: consolePath.file,
|
||||
icon: Folder,
|
||||
permission: () => true
|
||||
},
|
||||
{
|
||||
id: "user",
|
||||
title: "user.title",
|
||||
url: consolePath.user,
|
||||
icon: Users,
|
||||
permission: isAdmin
|
||||
},
|
||||
{
|
||||
id: "global",
|
||||
title: "global.title",
|
||||
url: consolePath.global,
|
||||
icon: Settings,
|
||||
@ -53,19 +60,22 @@ export const sidebarData: { navMain: SidebarItem[]; navUserCenter: SidebarItem[]
|
||||
],
|
||||
navUserCenter: [
|
||||
{
|
||||
id: "user_profile",
|
||||
title: "user_profile.title",
|
||||
url: consolePath.userProfile,
|
||||
icon: UserPen,
|
||||
permission: () => true
|
||||
},
|
||||
{
|
||||
id: "user_security",
|
||||
title: "user_security.title",
|
||||
url: consolePath.userSecurity,
|
||||
icon: ShieldCheck,
|
||||
permission: () => true
|
||||
},
|
||||
{
|
||||
title: "user-preference.title",
|
||||
id: "user_preference",
|
||||
title: "user_preference.title",
|
||||
url: consolePath.userPreference,
|
||||
icon: Palette,
|
||||
permission: () => true
|
||||
|
@ -10,39 +10,34 @@ import {
|
||||
} from "@/components/ui/sidebar"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation";
|
||||
import { User } from "@/models/user";
|
||||
import { useAuth } from "@/contexts/auth-context";
|
||||
import { IconType } from "@/types/icon";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { consolePath } from "@/hooks/use-route";
|
||||
import { SidebarItem } from "./data";
|
||||
|
||||
export function NavMain({
|
||||
export function NavGroup({
|
||||
items,
|
||||
title,
|
||||
activeId,
|
||||
setActiveId
|
||||
}: {
|
||||
items: {
|
||||
title: string
|
||||
url: string
|
||||
icon?: IconType;
|
||||
permission: ({ user }: { user: User }) => boolean
|
||||
}[]
|
||||
items: SidebarItem[],
|
||||
title: string,
|
||||
activeId: string,
|
||||
setActiveId? :(id: string) => void
|
||||
}) {
|
||||
const t = useTranslations("Console")
|
||||
const { user } = useAuth();
|
||||
const pathname = usePathname() ?? "/"
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent className="flex flex-col gap-2">
|
||||
<SidebarGroupLabel>{t("general")}</SidebarGroupLabel>
|
||||
<SidebarGroupLabel>{title}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
item.permission({ user }) && <SidebarMenuItem key={item.title}>
|
||||
<Link href={item.url}>
|
||||
<SidebarMenuButton tooltip={item.title} isActive={item.url != consolePath.dashboard && pathname.startsWith(item.url) || item.url === pathname}>
|
||||
<Link href={item.url} onClick={() => setActiveId && setActiveId(item.id)}>
|
||||
<SidebarMenuButton tooltip={item.title} isActive={activeId === item.id}>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{t(item.title)}</span>
|
||||
<span>{item.title}</span>
|
||||
</SidebarMenuButton>
|
||||
</Link>
|
||||
</SidebarMenuItem>
|
@ -1,50 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupLabel,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@/components/ui/sidebar"
|
||||
import { User } from "@/models/user"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { useAuth } from "@/contexts/auth-context"
|
||||
import { IconType } from "@/types/icon"
|
||||
import { useTranslations } from "next-intl"
|
||||
|
||||
export function NavUserCenter({
|
||||
items,
|
||||
}: {
|
||||
items: {
|
||||
title: string
|
||||
url: string
|
||||
icon?: IconType;
|
||||
permission: ({ user }: { user: User }) => boolean
|
||||
}[]
|
||||
}) {
|
||||
const t = useTranslations("Console")
|
||||
const { user } = useAuth();
|
||||
const pathname = usePathname() ?? "/"
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
||||
<SidebarGroupLabel>{t("personal")}</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
item.permission({ user }) && <SidebarMenuItem key={item.title}>
|
||||
<Link href={item.url}>
|
||||
<SidebarMenuButton tooltip={item.title} isActive={pathname === item.url}>
|
||||
{item.icon && <item.icon />}
|
||||
<span>{t(item.title)}</span>
|
||||
</SidebarMenuButton>
|
||||
</Link>
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
</SidebarMenu>
|
||||
</SidebarGroup>
|
||||
)
|
||||
}
|
@ -133,7 +133,7 @@
|
||||
"update_password_success": "密码已更新",
|
||||
"verify_code": "验证码"
|
||||
},
|
||||
"user-preference": {
|
||||
"user_preference": {
|
||||
"title": "个性化"
|
||||
}
|
||||
},
|
||||
|
Reference in New Issue
Block a user