Files
st-react/web-app/src/components/Navbar.tsx
Echo f4e166c5ee 🎉 初始化项目
Signed-off-by: Echo <1711788888@qq.com>
2026-02-27 21:52:00 +08:00

139 lines
5.3 KiB
TypeScript

import {useEffect, useState} from 'react'
import {Link} from 'react-router-dom'
import {LogOut, Menu, Shield, Sparkles, User} from 'lucide-react'
import {authApi, type User as UserType} from '../api/auth'
export default function Navbar() {
const [user, setUser] = useState<UserType | null>(null)
const [showUserMenu, setShowUserMenu] = useState(false)
useEffect(() => {
loadUser()
}, [])
const loadUser = async () => {
const token = localStorage.getItem('token')
if (!token) return
try {
const response = await authApi.getUserInfo()
setUser(response.data)
} catch (err) {
console.error('获取用户信息失败:', err)
localStorage.removeItem('token')
localStorage.removeItem('refreshToken')
}
}
const handleLogout = () => {
localStorage.removeItem('token')
localStorage.removeItem('refreshToken')
setUser(null)
window.location.href = '/'
}
return (
<nav className="fixed top-4 left-4 right-4 z-50">
<div className="max-w-7xl mx-auto glass rounded-2xl px-6 py-4">
<div className="flex items-center justify-between">
<Link to="/" className="flex items-center gap-2">
<div className="w-8 h-8 bg-gradient-to-br from-primary to-secondary rounded-lg flex items-center justify-center">
<Sparkles className="w-5 h-5" />
</div>
<span className="text-xl font-semibold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
</span>
</Link>
<div className="hidden md:flex items-center gap-6">
<Link to="/" className="text-sm hover:text-primary transition-colors cursor-pointer">
</Link>
<Link to="/market" className="text-sm hover:text-primary transition-colors cursor-pointer">
广
</Link>
{user && (
<>
<Link to="/characters" className="text-sm hover:text-primary transition-colors cursor-pointer">
</Link>
<Link to="/presets" className="text-sm hover:text-primary transition-colors cursor-pointer">
</Link>
<Link to="/chat" className="text-sm hover:text-primary transition-colors cursor-pointer">
</Link>
{user.isAdmin && (
<Link to="/admin" className="text-sm hover:text-primary transition-colors cursor-pointer flex items-center gap-1">
<Shield className="w-4 h-4" />
</Link>
)}
</>
)}
</div>
<div className="flex items-center gap-3">
{user ? (
<div className="relative">
<button
onClick={() => setShowUserMenu(!showUserMenu)}
className="flex items-center gap-2 px-4 py-2 glass-hover rounded-lg text-sm cursor-pointer"
>
{user.avatar ? (
<img src={user.avatar} alt={user.nickName} className="w-6 h-6 rounded-full" />
) : (
<User className="w-5 h-5" />
)}
<span>{user.nickName || user.username}</span>
</button>
{showUserMenu && (
<div className="absolute right-0 mt-2 w-48 glass rounded-xl overflow-hidden">
<Link
to="/profile"
className="block px-4 py-3 text-sm hover:bg-white/5 cursor-pointer"
onClick={() => setShowUserMenu(false)}
>
</Link>
{user.isAdmin && (
<Link
to="/admin"
className="block px-4 py-3 text-sm hover:bg-white/5 cursor-pointer flex items-center gap-2"
onClick={() => setShowUserMenu(false)}
>
<Shield className="w-4 h-4" />
</Link>
)}
<button
onClick={handleLogout}
className="w-full text-left px-4 py-3 text-sm hover:bg-white/5 cursor-pointer flex items-center gap-2 text-red-400"
>
<LogOut className="w-4 h-4" />
退
</button>
</div>
)}
</div>
) : (
<>
<Link to="/login" className="hidden md:block px-4 py-2 glass-hover rounded-lg text-sm cursor-pointer">
</Link>
<Link to="/register" className="px-4 py-2 bg-gradient-to-r from-primary to-secondary rounded-lg text-sm hover:opacity-90 transition-opacity cursor-pointer">
使
</Link>
</>
)}
<button className="md:hidden p-2 glass-hover rounded-lg cursor-pointer">
<Menu className="w-5 h-5" />
</button>
</div>
</div>
</div>
</nav>
)
}