加了一堆模型和一堆功能

This commit is contained in:
2026-04-16 00:36:21 +08:00
parent 47c0863bab
commit 40de992516
34 changed files with 3111 additions and 718 deletions

View File

@@ -5,10 +5,29 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { useApp } from "@/lib/app-context";
import { useAuth } from "@/lib/auth-context";
import { ProfileModal } from "@/components/profile-modal";
const NAV_ITEMS = [
{ href: "/", label: "对话" },
{ href: "/gallery", label: "资源库" },
{
href: "/",
label: "对话",
icon: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z" />
</svg>
),
},
{
href: "/gallery",
label: "资源库",
icon: (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
<circle cx="8.5" cy="8.5" r="1.5" />
<polyline points="21 15 16 10 5 21" />
</svg>
),
},
] as const;
export function TopNav() {
@@ -16,6 +35,7 @@ export function TopNav() {
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
const { user, logout } = useAuth();
const [menuOpen, setMenuOpen] = useState(false);
const [profileOpen, setProfileOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@@ -30,12 +50,13 @@ export function TopNav() {
}, [menuOpen]);
return (
<header className="flex-shrink-0 h-12 border-b border-[var(--border)] bg-[var(--bg-secondary)] flex items-center px-3 md:px-4 gap-3 md:gap-6">
<>
<header className="relative z-10 flex-shrink-0 h-14 border-b border-[var(--border)] bg-[var(--bg-secondary)]/80 backdrop-blur-xl flex items-center px-4 md:px-5 gap-4 md:gap-6">
{/* 移动端汉堡菜单 */}
<button
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
className="md:hidden flex-shrink-0 p-1.5 rounded-md
text-[var(--text-secondary)] hover:text-[var(--text-primary)]
text-[var(--text-secondary)] hover:text-[var(--accent)]
transition-colors cursor-pointer"
title="菜单"
>
@@ -45,33 +66,37 @@ export function TopNav() {
</button>
{/* Logo */}
<Link href="/" className="flex items-center gap-2 flex-shrink-0">
<svg width="22" height="22" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" rx="7" fill="var(--accent)" />
<path d="M8 22L12.5 10h2.2L19 22h-2.3l-1.1-3h-4.2l-1.1 3H8Zm3.7-5h3.1l-1.5-4.6h-.1L11.7 17Z" fill="white" />
<circle cx="23" cy="12" r="3.5" stroke="white" strokeWidth="1.8" fill="none" />
<path d="M23 15.5v5" stroke="white" strokeWidth="1.8" strokeLinecap="round" />
<circle cx="23" cy="22.5" r="1" fill="white" />
</svg>
<Link href="/" className="flex items-center gap-2.5 flex-shrink-0 group">
<div className="w-8 h-8 rounded-lg bg-[var(--accent)] flex items-center justify-center
shadow-[0_0_12px_rgba(77,184,164,0.25)]
group-hover:shadow-[0_0_20px_rgba(77,184,164,0.4)] transition-shadow">
<svg width="18" height="18" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 22L12.5 10h2.2L19 22h-2.3l-1.1-3h-4.2l-1.1 3H8Zm3.7-5h3.1l-1.5-4.6h-.1L11.7 17Z" fill="#0C1210" />
<circle cx="23" cy="12" r="3.5" stroke="#0C1210" strokeWidth="1.8" fill="none" />
<path d="M23 15.5v5" stroke="#0C1210" strokeWidth="1.8" strokeLinecap="round" />
<circle cx="23" cy="22.5" r="1" fill="#0C1210" />
</svg>
</div>
<span className="text-base font-bold tracking-tight text-[var(--text-primary)]">
EPEEKit
</span>
</Link>
{/* 导航 Tab */}
<nav className="flex items-center gap-1">
{NAV_ITEMS.map(({ href, label }) => {
<nav className="flex items-center gap-1.5">
{NAV_ITEMS.map(({ href, label, icon }) => {
const active = href === "/" ? pathname === "/" : pathname.startsWith(href);
return (
<Link
key={href}
href={href}
className={`px-2.5 md:px-3 py-1.5 text-sm rounded-md transition-colors ${
className={`relative flex items-center gap-1.5 px-3 md:px-4 py-2 text-sm rounded-lg ${
active
? "bg-[var(--bg-tertiary)] text-[var(--text-primary)] font-medium"
: "text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
? "glow-border text-[var(--accent)] font-medium !bg-[var(--bg-secondary)]"
: "text-[var(--text-secondary)] hover:text-[var(--text-primary)] border border-transparent hover:border-[var(--border-glow)] transition-all"
}`}
>
{icon}
{label}
</Link>
);
@@ -85,29 +110,39 @@ export function TopNav() {
<div className="relative" ref={menuRef}>
<button
onClick={() => setMenuOpen(!menuOpen)}
className="flex items-center gap-2 px-2 md:px-3 py-1.5 rounded-md text-sm
className="flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm
text-[var(--text-secondary)] hover:text-[var(--text-primary)]
hover:bg-[var(--bg-tertiary)] transition-colors cursor-pointer"
hover:bg-[var(--bg-tertiary)] border border-transparent
hover:border-[var(--border)] transition-all cursor-pointer"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
<div className="w-7 h-7 rounded-full bg-[var(--accent)]/15 border border-[var(--accent)]/30
flex items-center justify-center text-[var(--accent)] text-xs font-semibold">
{(user.display_name || user.username).charAt(0).toUpperCase()}
</div>
<span className="hidden sm:inline max-w-[100px] truncate">
{user.display_name || user.username}
</span>
</button>
{menuOpen && (
<div className="absolute right-0 top-full mt-1 w-44 rounded-lg
bg-[var(--bg-secondary)] border border-[var(--border)]
shadow-xl py-1 z-50">
<div className="px-3 py-2 text-xs text-[var(--text-secondary)] border-b border-[var(--border)]">
{user.username}
{user.is_admin && <span className="ml-1 text-[var(--accent)]">()</span>}
</div>
<div className="absolute right-0 top-full mt-1.5 w-48 rounded-xl
glass-panel shadow-xl shadow-black/30 py-1.5 z-50">
<button
onClick={() => { setMenuOpen(false); setProfileOpen(true); }}
className="w-full text-left px-3.5 py-2 text-xs text-[var(--text-secondary)]
hover:text-[var(--accent)] hover:bg-[var(--bg-tertiary)]
border-b border-[var(--border)] transition-colors cursor-pointer
flex items-center gap-1.5"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" />
<circle cx="12" cy="7" r="4" />
</svg>
<span>{user.username}</span>
{user.is_admin && <span className="text-[var(--accent)]">()</span>}
</button>
<button
onClick={() => { setMenuOpen(false); logout(); }}
className="w-full text-left px-3 py-2 text-sm text-[var(--text-secondary)]
className="w-full text-left px-3.5 py-2 text-sm text-[var(--text-secondary)]
hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]
transition-colors cursor-pointer"
>
@@ -118,5 +153,8 @@ export function TopNav() {
</div>
)}
</header>
{profileOpen && <ProfileModal onClose={() => setProfileOpen(false)} />}
</>
);
}