gongxiangp2/components/department-dashboard/DepartmentAccountMenu.tsx
2026-08-19 12:08:38 +08:00

95 lines
3.8 KiB
TypeScript

'use client'
import Link from 'next/link'
import { signOut, useSession } from 'next-auth/react'
import { ChevronDown, LogOut, UserRound } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
type DepartmentAccountMenuProps = {
collapsed: boolean
onNavigate?: () => void
}
export default function DepartmentAccountMenu({ collapsed, onNavigate }: DepartmentAccountMenuProps) {
const { data: session } = useSession()
const [open, setOpen] = useState(false)
const menuRef = useRef<HTMLDivElement>(null)
const triggerRef = useRef<HTMLButtonElement>(null)
const userName = session?.user?.name || session?.user?.email || '当前用户'
const initials = userName.slice(0, 1).toUpperCase()
useEffect(() => {
const closeMenu = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) setOpen(false)
}
const closeWithEscape = (event: KeyboardEvent) => {
if (event.key !== 'Escape' || !open) return
event.stopPropagation()
setOpen(false)
triggerRef.current?.focus()
}
document.addEventListener('mousedown', closeMenu)
document.addEventListener('keydown', closeWithEscape)
return () => {
document.removeEventListener('mousedown', closeMenu)
document.removeEventListener('keydown', closeWithEscape)
}
}, [open])
return (
<div ref={menuRef} className="relative">
{open && (
<div
role="menu"
className={`absolute bottom-full z-50 mb-2 w-56 rounded-lg bg-white p-2 text-[#344054] shadow-[0_14px_38px_rgba(15,23,42,0.22)] ring-1 ring-[#e2e8f0] ${collapsed ? 'left-0' : 'inset-x-0 w-auto'}`}
>
<Link
href="/profile"
role="menuitem"
onClick={() => {
setOpen(false)
onNavigate?.()
}}
className="flex min-h-11 items-center gap-3 rounded-lg px-3 text-sm font-medium hover:bg-[#f2f5f8] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#1677ff]"
>
<UserRound className="h-4 w-4" aria-hidden="true" />
个人资料
</Link>
<button
type="button"
role="menuitem"
onClick={() => signOut({ callbackUrl: '/login' })}
className="flex min-h-11 w-full items-center gap-3 rounded-lg px-3 text-left text-sm font-medium text-[#b42318] hover:bg-[#fff1f0] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f97066]"
>
<LogOut className="h-4 w-4" aria-hidden="true" />
退出登录
</button>
</div>
)}
<button
ref={triggerRef}
type="button"
aria-expanded={open}
aria-haspopup="menu"
aria-label="打开账户菜单"
title={collapsed ? userName : undefined}
onClick={() => setOpen((current) => !current)}
className={`flex min-h-12 w-full items-center rounded-lg text-left text-[#dbe4ed] transition-colors hover:bg-white/[0.08] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#69a9ff] ${collapsed ? 'justify-center px-2' : 'gap-3 px-2'}`}
>
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[#1677ff] text-sm font-bold text-white">{initials}</span>
{!collapsed && (
<>
<span className="min-w-0 flex-1">
<span className="block truncate text-sm font-semibold">{userName}</span>
<span className="mt-0.5 block text-xs text-[#7f93a8]">账户与退出</span>
</span>
<ChevronDown className={`h-4 w-4 shrink-0 text-[#7f93a8] transition-transform ${open ? 'rotate-180' : ''}`} aria-hidden="true" />
</>
)}
</button>
</div>
)
}