Paper_Trading / frontend /src /components /AuthModal.tsx
superxuu
feat: implement user auth, daily limits, and built-in Vmq payment gateway
57864b1
'use client';
/**
* 登录/注册弹窗组件
*/
import { useState } from 'react';
import { X, User, Lock, Eye, EyeOff } from 'lucide-react';
import { useAuthStore } from '@/store/authStore';
interface AuthModalProps {
onClose: () => void;
initialMode?: 'login' | 'register';
}
export default function AuthModal({ onClose, initialMode = 'login' }: AuthModalProps) {
const [mode, setMode] = useState<'login' | 'register'>(initialMode);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [error, setError] = useState('');
const { login, register, isLoading } = useAuthStore();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
// 前端验证
if (username.trim().length < 3) {
setError('用户名至少3个字符');
return;
}
if (password.length < 6) {
setError('密码至少6位');
return;
}
if (mode === 'register' && password !== confirmPassword) {
setError('两次密码输入不一致');
return;
}
try {
if (mode === 'login') {
await login(username.trim(), password);
} else {
await register(username.trim(), password);
}
onClose();
} catch (err: any) {
setError(err.message || (mode === 'login' ? '登录失败' : '注册失败'));
}
};
const switchMode = () => {
setMode(mode === 'login' ? 'register' : 'login');
setError('');
setConfirmPassword('');
};
return (
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4" onClick={onClose}>
<div
className="bg-[#1E212B] rounded-2xl w-full max-w-sm shadow-2xl border border-[#2A2D3C] overflow-hidden"
onClick={e => e.stopPropagation()}
>
{/* Header */}
<div className="px-5 py-4 border-b border-[#2A2D3C] flex items-center justify-between">
<h3 className="text-lg font-bold text-white">
{mode === 'login' ? '登录' : '注册'}
</h3>
<button
onClick={onClose}
className="w-8 h-8 flex items-center justify-center rounded-lg bg-[#2A2D3C] hover:bg-[#35394B] text-gray-400 hover:text-white transition-colors"
>
<X size={16} />
</button>
</div>
{/* Form */}
<form onSubmit={handleSubmit} className="p-5 space-y-4">
{/* Error */}
{error && (
<div className="bg-red-500/10 border border-red-500/30 text-red-400 text-sm px-4 py-2.5 rounded-lg">
{error}
</div>
)}
{/* Username */}
<div>
<label className="text-gray-400 text-xs mb-1.5 block">用户名</label>
<div className="relative">
<User size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" />
<input
type="text"
value={username}
onChange={e => setUsername(e.target.value)}
placeholder="请输入用户名(至少3位)"
className="w-full bg-[#12141C] border border-gray-700/50 rounded-xl pl-10 pr-4 py-3 text-white text-sm placeholder-gray-600 focus:outline-none focus:border-blue-500 transition-colors"
autoComplete="username"
autoFocus
/>
</div>
</div>
{/* Password */}
<div>
<label className="text-gray-400 text-xs mb-1.5 block">密码</label>
<div className="relative">
<Lock size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" />
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={e => setPassword(e.target.value)}
placeholder="请输入密码(至少6位)"
className="w-full bg-[#12141C] border border-gray-700/50 rounded-xl pl-10 pr-10 py-3 text-white text-sm placeholder-gray-600 focus:outline-none focus:border-blue-500 transition-colors"
autoComplete={mode === 'login' ? 'current-password' : 'new-password'}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-300"
>
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
</button>
</div>
</div>
{/* Confirm Password (register only) */}
{mode === 'register' && (
<div>
<label className="text-gray-400 text-xs mb-1.5 block">确认密码</label>
<div className="relative">
<Lock size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500" />
<input
type={showPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
placeholder="请再次输入密码"
className="w-full bg-[#12141C] border border-gray-700/50 rounded-xl pl-10 pr-4 py-3 text-white text-sm placeholder-gray-600 focus:outline-none focus:border-blue-500 transition-colors"
autoComplete="new-password"
/>
</div>
</div>
)}
{/* Submit */}
<button
type="submit"
disabled={isLoading}
className="w-full bg-gradient-to-r from-blue-600 to-blue-500 hover:from-blue-500 hover:to-blue-400 text-white font-bold py-3 rounded-xl transition-all duration-200 disabled:opacity-50 shadow-lg text-sm"
>
{isLoading ? '处理中...' : (mode === 'login' ? '登录' : '注册')}
</button>
{/* Switch mode */}
<div className="text-center text-sm text-gray-500">
{mode === 'login' ? (
<>还没有账号?<button type="button" onClick={switchMode} className="text-blue-400 hover:text-blue-300 ml-1">立即注册</button></>
) : (
<>已有账号?<button type="button" onClick={switchMode} className="text-blue-400 hover:text-blue-300 ml-1">立即登录</button></>
)}
</div>
</form>
</div>
</div>
);
}