Files
hair-keeper/src/components/layout/header.tsx

134 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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>
)
}