"use client"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { Settings, ExternalLink, Briefcase, Images, Sparkles, Trophy, Building2, Crown, Medal, Award, Edit } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useTheme } from "next-themes"; import { useState, useEffect, useRef } from "react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { ImageAnnotator } from "@/components/image-annotator"; type RankingItem = { rank: number; userId: string; userName: string; department: string; featuredCount: number; }; type TopNavbarProps = { siteName?: string; siteLogo?: string; siteLogoDark?: string; isAdmin?: boolean; customLinks?: Array<{ id: string; name: string; url: string; order: number; }>; }; export function TopNavbar({ siteName = "AI Chatbot", siteLogo, siteLogoDark, isAdmin = false, customLinks = [], }: TopNavbarProps) { const router = useRouter(); const { resolvedTheme } = useTheme(); const [mounted, setMounted] = useState(false); const [ranking, setRanking] = useState([]); const [rankingOpen, setRankingOpen] = useState(false); const [showImageEditor, setShowImageEditor] = useState(false); const [editorImageUrl, setEditorImageUrl] = useState(""); const fileInputRef = useRef(null); useEffect(() => { setMounted(true); }, []); // 获取精选榜单数据 const fetchRanking = async () => { try { const res = await fetch("/api/featured/ranking"); const data = await res.json(); if (data.ranking) { setRanking(data.ranking); } } catch (error) { console.error("Failed to fetch ranking:", error); } }; // 当下拉菜单打开时获取数据 useEffect(() => { if (rankingOpen) { fetchRanking(); } }, [rankingOpen]); // 获取排名图标 const getRankIcon = (rank: number) => { switch (rank) { case 1: return ; case 2: return ; case 3: return ; default: return {rank}; } }; // 决定显示哪个 LOGO // 导航栏背景固定为黑色,理论上应该始终显示适合深色背景的 LOGO // 但如果有专门配置了 "Dark Mode Logo" (通常用于深色背景),我们优先使用它 // 如果没配置 Dark Mode Logo,则使用默认 Logo // 注意:这里的背景是黑色的,所以通常需要一个浅色的 LOGO(即 Dark Mode 下使用的 LOGO) // 如果用户配置逻辑是:siteLogo(Light Mode/白底用), siteLogoDark(Dark Mode/黑底用) // 那么在黑色导航栏上,应该优先使用 siteLogoDark const displayLogo = (siteLogoDark && siteLogoDark.trim() !== "") ? siteLogoDark : siteLogo; // 处理图片上传 const handleImageUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; // 检查文件类型 if (!file.type.startsWith("image/")) { return; } // 转换为 data URL const reader = new FileReader(); reader.onloadend = () => { const result = reader.result as string; setEditorImageUrl(result); setShowImageEditor(true); }; reader.onerror = () => { console.error("图片读取失败"); }; reader.readAsDataURL(file); // 清空文件输入,以便可以再次选择同一文件 if (fileInputRef.current) { fileInputRef.current.value = ""; } }; // 打开图片编辑器 const handleOpenImageEditor = () => { fileInputRef.current?.click(); }; // 处理图片编辑完成 const handleImageEditorConfirm = (annotatedImageUrl: string) => { // 将编辑后的图片存储到 sessionStorage,以便在聊天页面中使用 try { sessionStorage.setItem("pending-edited-image", annotatedImageUrl); } catch (error) { // sessionStorage不可用(如隐私模式),使用URL参数作为备选方案 console.warn("sessionStorage不可用,尝试使用URL参数:", error); // 如果sessionStorage不可用,我们可以将图片数据编码到URL中 // 但由于数据可能很大,这里只记录警告,让用户手动添加图片 // 或者可以显示提示信息 } // 导航到新聊天页面(无论sessionStorage是否成功) router.push("/"); setShowImageEditor(false); setEditorImageUrl(""); }; // 取消图片编辑 const handleImageEditorCancel = () => { setShowImageEditor(false); setEditorImageUrl(""); }; return ( ); }