"use client" import Link from "next/link" import { BookOpen, Search, Settings, LogOut, User, ChevronDown, Plus, Trash2, Network, LayoutGrid, Calculator, HelpCircle, Calendar, Menu, X, Home, Users as UsersIcon, Clock, TreePine } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet" import { useFamily } from "@/context/family-context" import { useState, useRef, useEffect, useCallback } from "react" import { useRouter, usePathname, useSearchParams } from "next/navigation" import { useSession, signOut } from "next-auth/react" import { useDialog } from "@/components/ui/alert-dialog-custom" import { permissions } from "@/lib/permissions" import { solar2lunar } from "@/lib/lunar-calendar" import { CalendarDialog } from "@/components/calendar-dialog" import { MemberNameWithStatus } from "./member-name-with-status" interface FamilyTree { id: string name: string description: string | null ownerId: string currentUserRole?: "OWNER" | "EDITOR" | "VIEWER" } export function SiteHeader() { const { searchMembers, searchResults, currentTree: familyCurrentTree } = useFamily() const { data: session, status } = useSession() const { showAlert, showConfirm, showPrompt } = useDialog() const pathname = usePathname() const searchParams = useSearchParams() const [searchQuery, setSearchQuery] = useState("") const [showResults, setShowResults] = useState(false) const [familyTrees, setFamilyTrees] = useState([]) const [deletingTreeId, setDeletingTreeId] = useState(null) const [treeDropdownOpen, setTreeDropdownOpen] = useState(false) const [currentDate, setCurrentDate] = useState(new Date()) const [calendarDialogOpen, setCalendarDialogOpen] = useState(false) const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const searchRef = useRef(null) const router = useRouter() // 使用 family context 的 currentTree const currentTree = familyCurrentTree // 获取当前视图模式 const currentViewMode = searchParams.get('view') || 'traditional' const isTreePage = pathname === '/tree' // 更新日期 useEffect(() => { const timer = setInterval(() => { setCurrentDate(new Date()) }, 60000) // 每分钟更新一次 return () => clearInterval(timer) }, []) // 获取农历信息 const lunarInfo = solar2lunar(currentDate) const solarDateStr = `${currentDate.getFullYear()}年${currentDate.getMonth() + 1}月${currentDate.getDate()}日` const lunarDateStr = lunarInfo ? lunarInfo.toString() : '农历信息不可用' // 生成带有 treeId 的 URL const getUrlWithTreeId = (path: string) => { if (currentTree?.id) { return `${path}?treeId=${currentTree.id}` } return path } // 切换视图模式 const handleViewModeChange = (mode: string) => { const params = new URLSearchParams(searchParams.toString()) params.set('view', mode) if (currentTree?.id) { params.set('treeId', currentTree.id) } router.push(`${pathname}?${params.toString()}`) } const getRoleBadge = (role?: string) => { switch (role) { case "OWNER": return 所有者 case "EDITOR": return 编辑 case "VIEWER": return 查看 default: return null } } const handleSignOut = async () => { await signOut({ callbackUrl: "/auth/signin" }) } // 获取用户名首字母作为头像 const getUserInitial = () => { if (session?.user?.name) { return session.user.name.charAt(0).toUpperCase() } if (session?.user?.email) { return session.user.email.charAt(0).toUpperCase() } return "U" } // 删除家族树 const handleDeleteTree = async (tree: FamilyTree, e: React.MouseEvent) => { e.stopPropagation() // 阻止触发选择事件 // 检查权限 if (tree.ownerId !== session?.user?.id) { await showAlert('只有家族树的创建者才能删除', '权限不足') return } const confirmText = `确定要删除家族树"${tree.name}"吗?\n\n此操作将永久删除所有成员、活动日志和协作者数据,且不可撤销!` const confirmed = await showConfirm(confirmText, '删除家族树', 'destructive') if (!confirmed) { return } // 二次确认 const finalConfirm = await showPrompt( `请输入家族树名称"${tree.name}"以确认删除:`, '', '确认删除' ) if (finalConfirm !== tree.name) { await showAlert('名称不匹配,删除已取消', '操作已取消') return } setDeletingTreeId(tree.id) try { const response = await fetch(`/api/trees/${tree.id}`, { method: 'DELETE', }) if (!response.ok) { const error = await response.json() throw new Error(error.error || '删除失败') } // 刷新家族树列表 await loadFamilyTrees() // 如果删除的是当前树,跳转到首页 if (currentTree?.id === tree.id) { window.location.href = '/' } } catch (error) { console.error('删除家族树失败:', error) await showAlert('删除失败:' + (error as Error).message, '错误') } finally { setDeletingTreeId(null) } } // 获取家族树列表 const loadFamilyTrees = useCallback(() => { if (!session?.user?.id || status !== 'authenticated') return const controller = new AbortController() fetch('/api/trees', { signal: controller.signal }) .then(res => { if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`) return res.json() }) .then(data => { if (data.trees) { setFamilyTrees(data.trees) } }) .catch(err => { // 忽略 abort 错误和 fetch 错误(可能是页面切换导致的) if (err.name !== 'AbortError' && err.message !== 'Failed to fetch') { console.error('获取家族树列表失败:', err) } }) return () => controller.abort() }, [session?.user?.id, status]) useEffect(() => { loadFamilyTrees() }, [loadFamilyTrees]) // 监听路由变化,刷新家族树列表 useEffect(() => { const handleRouteChange = () => { loadFamilyTrees() } window.addEventListener('focus', handleRouteChange) return () => window.removeEventListener('focus', handleRouteChange) }, [loadFamilyTrees]) // 点击外部关闭搜索结果 useEffect(() => { function handleClickOutside(event: MouseEvent) { if (searchRef.current && !searchRef.current.contains(event.target as Node)) { setShowResults(false) } } document.addEventListener("mousedown", handleClickOutside) return () => document.removeEventListener("mousedown", handleClickOutside) }, []) const handleSearch = (query: string) => { setSearchQuery(query) searchMembers(query) setShowResults(query.length > 0) } const handleSelectMember = (memberId: string) => { const params = new URLSearchParams(window.location.search) router.push(`/members/${memberId}?${params.toString()}`) setSearchQuery("") setShowResults(false) } // 导航菜单项配置 const navItems = [ { href: "/", label: "总览", icon: Home }, { href: "/tree", label: "族谱", icon: Network }, { href: "/members", label: "成员", icon: UsersIcon }, { href: "/timeline", label: "大事记", icon: Clock }, ] // 判断当前路由是否激活 const isActive = (href: string) => { if (href === "/") return pathname === "/" return pathname.startsWith(href) } return ( <>
{/* Logo */}
华夏家谱
{/* Desktop Navigation */} {/* Mobile Menu Button */} 华夏家谱
{/* Mobile Navigation Links */}
{navItems.map((item) => { const Icon = item.icon const active = isActive(item.href) return ( setMobileMenuOpen(false)} className={` flex items-center gap-3 px-4 py-3 rounded-lg font-serif transition-all duration-200 ${active ? 'text-primary bg-primary/10 font-medium' : 'text-muted-foreground hover:text-foreground hover:bg-muted' } `} > {item.label} ) })}
{/* Mobile Utility Links */}
setMobileMenuOpen(false)} className="flex items-center gap-3 px-4 py-3 rounded-lg font-serif text-muted-foreground hover:text-foreground hover:bg-muted transition-all" > 关系计算 setMobileMenuOpen(false)} className="flex items-center gap-3 px-4 py-3 rounded-lg font-serif text-muted-foreground hover:text-foreground hover:bg-muted transition-all" > 系统设置 setMobileMenuOpen(false)} className="flex items-center gap-3 px-4 py-3 rounded-lg font-serif text-muted-foreground hover:text-foreground hover:bg-muted transition-all" > 帮助中心
{/* Mobile User Section */} {session && (
{session.user?.name || "用户"}
{session.user?.email}
)}
{/* 家族树选择器 */} {session && familyTrees.length > 0 && (
我的家族树 {familyTrees.map((tree) => ( { e.preventDefault() setTreeDropdownOpen(false) // 切换家族后跳转到总览页面,family-context 会自动加载新的树 router.push(`/?treeId=${tree.id}`) }} className="cursor-pointer flex items-center justify-between group py-3" >
{tree.name} {getRoleBadge(tree.currentUserRole)} {currentTree?.id === tree.id && ( )}
{permissions.canDeleteTree(tree.currentUserRole) && ( )}
))} {session && ( router.push('/trees/new')} className="gap-2" > 创建新家族树 )}
)}
{/* Search Bar */}
handleSearch(e.target.value)} className="h-9 w-56 xl:w-64 rounded-lg bg-muted/50 pl-9 text-sm border-0 focus-visible:ring-1 focus-visible:ring-primary/50" /> {/* 搜索结果下拉框 */} {showResults && searchResults.length > 0 && (
{searchResults.map((member) => ( ))}
)} {/* 无结果提示 */} {showResults && searchQuery && searchResults.length === 0 && (
未找到匹配的成员
)}
{/* 日期显示 */} {/* Utility Buttons - Desktop Only */}
{/* User Menu */} {status === "loading" ? (
) : session ? (

{session.user?.name || "用户"}

{session.user?.email}

系统设置 帮助中心 退出登录
) : ( )}
{/* 日历查询对话框 - 放在 header 外部避免定位冲突 */} ) }