108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
import { useState } from 'react';
|
|
import { User, LogOut, LayoutDashboard, LogIn } from 'lucide-react';
|
|
import { API_BASE_URL } from '../utils/request';
|
|
|
|
interface UserAuthProps {
|
|
user: any;
|
|
onOpenAuth: () => void;
|
|
onLogout: () => void;
|
|
onNavigateToAdmin: () => void;
|
|
}
|
|
|
|
export function UserAuth({ user, onOpenAuth, onLogout, onNavigateToAdmin }: UserAuthProps) {
|
|
const [showUserMenu, setShowUserMenu] = useState(false);
|
|
|
|
if (!user) {
|
|
return (
|
|
<button
|
|
onClick={onOpenAuth}
|
|
className="flex items-center gap-2 px-4 py-2 rounded-full bg-white/10 hover:bg-white/20 backdrop-blur-md border border-white/10 text-white font-medium text-sm transition-all duration-200 shadow-lg hover:shadow-blue-500/20 pointer-events-auto"
|
|
>
|
|
<LogIn size={16} />
|
|
<span>登录 / 注册</span>
|
|
</button>
|
|
);
|
|
}
|
|
|
|
// Helper to get full avatar URL
|
|
const getAvatarUrl = () => {
|
|
if (!user?.avatar_url) return null;
|
|
return `/upload/${user.avatar_url}`;
|
|
};
|
|
|
|
return (
|
|
<div className="relative pointer-events-auto">
|
|
{/* Overlay for closing menu */}
|
|
{showUserMenu && (
|
|
<div
|
|
className="fixed inset-0 z-40"
|
|
onClick={() => setShowUserMenu(false)}
|
|
/>
|
|
)}
|
|
|
|
<button
|
|
onClick={() => setShowUserMenu(!showUserMenu)}
|
|
className="flex items-center gap-3 pl-2 pr-4 py-1.5 rounded-full bg-black/40 hover:bg-black/60 backdrop-blur-md border border-white/10 text-white transition-all duration-200 relative z-50"
|
|
>
|
|
<div className="w-8 h-8 rounded-full overflow-hidden bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center shadow-inner">
|
|
{user.avatar_url ? (
|
|
<img src={getAvatarUrl()} alt="avatar" className="w-full h-full object-cover" />
|
|
) : (
|
|
<User size={16} />
|
|
)}
|
|
</div>
|
|
<div className="flex flex-col items-start text-xs">
|
|
<span className="text-gray-400 leading-none mb-0.5">已登录</span>
|
|
<span className="font-bold leading-none max-w-[80px] truncate">{user.username}</span>
|
|
</div>
|
|
</button>
|
|
|
|
{/* Dropdown Menu */}
|
|
{showUserMenu && (
|
|
<div className="absolute top-full right-0 mt-2 w-48 bg-gray-900/95 backdrop-blur-xl border border-white/10 rounded-xl shadow-2xl overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200 z-50">
|
|
<div className="p-4 border-b border-white/10 bg-white/5 flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-full overflow-hidden bg-gradient-to-br from-blue-500 to-purple-600 flex-shrink-0 flex items-center justify-center">
|
|
{user.avatar_url ? (
|
|
<img src={getAvatarUrl()} alt="avatar" className="w-full h-full object-cover" />
|
|
) : (
|
|
<User size={20} className="text-white" />
|
|
)}
|
|
</div>
|
|
<div className="overflow-hidden">
|
|
<p className="text-sm font-bold text-white truncate">{user.full_name || user.username}</p>
|
|
<p className="text-[10px] text-gray-500 truncate">{user.email}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-1">
|
|
<button
|
|
onClick={() => {
|
|
// Open admin in new tab
|
|
window.open('/admin', '_blank');
|
|
setShowUserMenu(false);
|
|
}}
|
|
className="w-full text-left px-3 py-2.5 text-sm text-gray-200 hover:bg-white/10 hover:text-white flex items-center gap-3 transition-colors rounded-lg"
|
|
>
|
|
<LayoutDashboard size={16} className="text-blue-400" />
|
|
进入后台管理
|
|
</button>
|
|
|
|
<div className="h-px bg-white/10 my-1 mx-2"></div>
|
|
|
|
<button
|
|
onClick={() => {
|
|
onLogout();
|
|
setShowUserMenu(false);
|
|
}}
|
|
className="w-full text-left px-3 py-2.5 text-sm text-red-400 hover:bg-red-500/10 hover:text-red-300 flex items-center gap-3 transition-colors rounded-lg"
|
|
>
|
|
<LogOut size={16} />
|
|
退出登录
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|