Hair Keeper v1.0.0:一个高度集成、深度定制、约定优于配置的全栈Web应用模板,旨在保持灵活性的同时提供一套基于成熟架构的开发底座,自带身份认证、权限控制、丰富前端组件、文件上传、后台任务、智能体开发等丰富功能,提供AI开发辅助,免于纠结功能如何实现,可快速上手专注于业务逻辑
This commit is contained in:
134
src/components/layout/header.tsx
Normal file
134
src/components/layout/header.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { User, LogOut, KeyRound } from 'lucide-react'
|
||||
import { DevPanel } from '@/app/(main)/dev/panel'
|
||||
import { ChangePasswordDialog } from '@/components/layout/change-password-dialog'
|
||||
import { UserProfileDialog } from '@/components/layout/user-profile-dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Separator } from '@/components/ui/separator'
|
||||
import { SidebarTrigger } from '@/components/ui/sidebar'
|
||||
import { signOut } from 'next-auth/react'
|
||||
import { useRouter, usePathname } from 'next/navigation'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { ThemeToggleButton, useThemeTransition } from '@/components/common/theme-toggle-button'
|
||||
import { getMenuTitle } from '@/constants/menu'
|
||||
import type { User as AppUser } from '@/types/user'
|
||||
|
||||
interface HeaderProps {
|
||||
user?: AppUser
|
||||
}
|
||||
|
||||
export function Header({ user }: HeaderProps) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const { theme, setTheme } = useTheme()
|
||||
const { startTransition } = useThemeTransition()
|
||||
const [isChangePasswordOpen, setIsChangePasswordOpen] = useState(false)
|
||||
const [isUserProfileOpen, setIsUserProfileOpen] = useState(false)
|
||||
|
||||
const pageTitle = getMenuTitle(pathname, 2) // 只匹配到第二级菜单
|
||||
|
||||
const handleThemeToggle = () => {
|
||||
startTransition(() => {
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark')
|
||||
})
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
await signOut({ redirect: false })
|
||||
router.push('/login')
|
||||
}
|
||||
|
||||
// 如果没有用户信息,不显示Header(应该被中间件重定向)
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<header className="flex h-16 shrink-0 items-center gap-2 border-b px-4">
|
||||
<SidebarTrigger />
|
||||
<Separator orientation="vertical" className="mr-2 h-4" />
|
||||
<div className="flex items-center justify-between flex-1">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{pageTitle}
|
||||
</h2>
|
||||
<div className="flex items-center space-x-4">
|
||||
{/* 主题切换按钮 */}
|
||||
<ThemeToggleButton
|
||||
theme={theme === 'dark' ? 'dark' : 'light'}
|
||||
variant="polygon"
|
||||
onClick={handleThemeToggle}
|
||||
className="border-0 shadow-none"
|
||||
/>
|
||||
|
||||
{/* 开发者工具按钮 - 仅开发环境 */}
|
||||
{process.env.NODE_ENV === 'development' && user.isSuperAdmin && <DevPanel />}
|
||||
|
||||
{/* 用户菜单 */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="flex items-center space-x-2 px-3">
|
||||
<div className="w-8 h-8 bg-primary/10 rounded-full flex items-center justify-center">
|
||||
<User className="h-4 w-4 text-primary" />
|
||||
</div>
|
||||
<div className="text-left hidden sm:block">
|
||||
<p className="text-sm font-medium">{user.name}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{user.isSuperAdmin ? '超级管理员' : (Array.isArray(user.roles) ? user.roles.join('、') : user.roles)}
|
||||
</p>
|
||||
</div>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuLabel>我的账户</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={() => setIsUserProfileOpen(true)}>
|
||||
<User className="mr-2 h-4 w-4" />
|
||||
个人资料
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setIsChangePasswordOpen(true)}>
|
||||
<KeyRound className="mr-2 h-4 w-4" />
|
||||
修改密码
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel>角色</DropdownMenuLabel>
|
||||
<DropdownMenuItem disabled>
|
||||
{user.isSuperAdmin ? '超级管理员' : (Array.isArray(user.roles) ? user.roles.join('、') : user.roles)}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-red-600 cursor-pointer"
|
||||
onClick={handleLogout}
|
||||
>
|
||||
<LogOut className="mr-2 h-4 w-4" />
|
||||
退出登录
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 修改密码对话框 */}
|
||||
<ChangePasswordDialog
|
||||
isOpen={isChangePasswordOpen}
|
||||
onClose={() => setIsChangePasswordOpen(false)}
|
||||
/>
|
||||
|
||||
{/* 用户资料对话框 */}
|
||||
<UserProfileDialog
|
||||
isOpen={isUserProfileOpen}
|
||||
onClose={() => setIsUserProfileOpen(false)}
|
||||
/>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user