用户系统
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useApp } from "@/lib/app-context";
|
||||
import { useAuth } from "@/lib/auth-context";
|
||||
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/", label: "对话" },
|
||||
@@ -12,6 +14,20 @@ const NAV_ITEMS = [
|
||||
export function TopNav() {
|
||||
const pathname = usePathname();
|
||||
const { sidebarCollapsed, setSidebarCollapsed } = useApp();
|
||||
const { user, logout } = useAuth();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const menuRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!menuOpen) return;
|
||||
function handleClick(e: MouseEvent) {
|
||||
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
|
||||
setMenuOpen(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClick);
|
||||
return () => document.removeEventListener("mousedown", handleClick);
|
||||
}, [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">
|
||||
@@ -64,21 +80,43 @@ export function TopNav() {
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
{/* 全局搜索入口 — 移动端只显示图标 */}
|
||||
<button
|
||||
className="flex items-center gap-2 px-2 md:px-3 py-1.5 rounded-md text-sm
|
||||
text-[var(--text-secondary)] border border-[var(--border)]
|
||||
bg-[var(--bg-tertiary)] hover:border-[var(--accent)]
|
||||
transition-colors cursor-pointer"
|
||||
title="搜索"
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="M21 21l-4.35-4.35" />
|
||||
</svg>
|
||||
<span className="hidden sm:inline">搜索</span>
|
||||
<kbd className="hidden md:inline text-xs text-[var(--text-secondary)] opacity-50 ml-2">⌘K</kbd>
|
||||
</button>
|
||||
{/* 用户菜单 */}
|
||||
{user && (
|
||||
<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
|
||||
text-[var(--text-secondary)] hover:text-[var(--text-primary)]
|
||||
hover:bg-[var(--bg-tertiary)] transition-colors 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>
|
||||
<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>
|
||||
<button
|
||||
onClick={() => { setMenuOpen(false); logout(); }}
|
||||
className="w-full text-left px-3 py-2 text-sm text-[var(--text-secondary)]
|
||||
hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]
|
||||
transition-colors cursor-pointer"
|
||||
>
|
||||
退出登录
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user