Spaces:
Sleeping
Sleeping
| import React, { useState, useEffect } from 'react'; | |
| import { useNavigate, useSearchParams } from 'react-router-dom'; | |
| import { useStore } from '@/store/useStore'; | |
| import { useTranslation } from 'react-i18next'; | |
| import { motion } from 'framer-motion'; | |
| import { Mail, Lock, User, Loader2, LogIn, ArrowLeft } from 'lucide-react'; | |
| export default function AuthPage() { | |
| const { t } = useTranslation(); | |
| const navigate = useNavigate(); | |
| const [searchParams] = useSearchParams(); | |
| const { setUser, setToken, token } = useStore(); | |
| const initialMode = searchParams.get('mode') === 'register' ? false : true; | |
| const [isLogin, setIsLogin] = useState(initialMode); | |
| const [isLoading, setIsLoading] = useState(false); | |
| const [error, setError] = useState(''); | |
| const [formData, setFormData] = useState({ | |
| email: '', | |
| password: '', | |
| name: '' | |
| }); | |
| // 如果已经登录,直接跳转到 dashboard 或指定的 redirect | |
| useEffect(() => { | |
| if (token) { | |
| const redirect = searchParams.get('redirect') || '/dashboard'; | |
| const openPaymentModal = searchParams.get('openPaymentModal'); | |
| let targetUrl = redirect; | |
| if (openPaymentModal) { | |
| const url = new URL(redirect, window.location.origin); | |
| url.searchParams.set('openPaymentModal', openPaymentModal); | |
| targetUrl = url.pathname + url.search; | |
| } | |
| navigate(targetUrl, { replace: true }); | |
| } | |
| }, [token, navigate, searchParams]); | |
| // 监听 URL 参数变化 | |
| useEffect(() => { | |
| const mode = searchParams.get('mode'); | |
| if (mode === 'register') setIsLogin(false); | |
| else if (mode === 'login') setIsLogin(true); | |
| }, [searchParams]); | |
| const handleSubmit = async (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| setIsLoading(true); | |
| setError(''); | |
| const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register'; | |
| try { | |
| const response = await fetch(endpoint, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(formData), | |
| }); | |
| const data = await response.json(); | |
| if (data.success) { | |
| if (isLogin) { | |
| setUser(data.user); | |
| setToken(data.token); | |
| const redirect = searchParams.get('redirect') || '/dashboard'; | |
| const openPaymentModal = searchParams.get('openPaymentModal'); | |
| let targetUrl = redirect; | |
| if (openPaymentModal) { | |
| const url = new URL(redirect, window.location.origin); | |
| url.searchParams.set('openPaymentModal', openPaymentModal); | |
| targetUrl = url.pathname + url.search; | |
| } | |
| navigate(targetUrl); | |
| } else { | |
| setIsLogin(true); | |
| setFormData({ ...formData, password: '' }); | |
| alert('注册成功,请登录'); | |
| } | |
| } else { | |
| setError(data.error || '认证失败'); | |
| } | |
| } catch (err) { | |
| setError('网络错误,请稍后再试'); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| return ( | |
| <div className="min-h-screen bg-zinc-50 flex flex-col items-center justify-center p-4"> | |
| <motion.button | |
| initial={{ opacity: 0, x: -20 }} | |
| animate={{ opacity: 1, x: 0 }} | |
| onClick={() => navigate('/')} | |
| className="mb-8 flex items-center gap-2 text-zinc-500 hover:text-zinc-900 transition-colors group" | |
| > | |
| <ArrowLeft size={18} className="group-hover:-translate-x-1 transition-transform" /> | |
| 返回首页 | |
| </motion.button> | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="w-full max-w-md bg-white rounded-2xl shadow-xl border border-zinc-200 p-8" | |
| > | |
| <div className="flex justify-center mb-8"> | |
| <div className="w-12 h-12 bg-blue-600 rounded-xl flex items-center justify-center text-white shadow-lg shadow-blue-200"> | |
| <LogIn size={24} /> | |
| </div> | |
| </div> | |
| <h2 className="text-2xl font-bold text-center text-zinc-900 mb-2"> | |
| {isLogin ? '欢迎回来' : '开启 AI 之旅'} | |
| </h2> | |
| <p className="text-zinc-500 text-center mb-8 text-sm"> | |
| {isLogin ? '请登录您的账号以访问工作台' : '只需几秒钟即可创建您的账号'} | |
| </p> | |
| <form onSubmit={handleSubmit} className="space-y-4"> | |
| {!isLogin && ( | |
| <div className="relative"> | |
| <User className="absolute left-3 top-3 text-zinc-400" size={18} /> | |
| <input | |
| type="text" | |
| placeholder="您的姓名" | |
| required | |
| className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all" | |
| value={formData.name} | |
| onChange={(e) => setFormData({ ...formData, name: e.target.value })} | |
| /> | |
| </div> | |
| )} | |
| <div className="relative"> | |
| <Mail className="absolute left-3 top-3 text-zinc-400" size={18} /> | |
| <input | |
| type="email" | |
| placeholder="电子邮箱" | |
| required | |
| className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all" | |
| value={formData.email} | |
| onChange={(e) => setFormData({ ...formData, email: e.target.value })} | |
| /> | |
| </div> | |
| <div className="relative"> | |
| <Lock className="absolute left-3 top-3 text-zinc-400" size={18} /> | |
| <input | |
| type="password" | |
| placeholder="密码" | |
| required | |
| className="w-full pl-10 pr-4 py-2.5 bg-zinc-50 border border-zinc-200 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none transition-all" | |
| value={formData.password} | |
| onChange={(e) => setFormData({ ...formData, password: e.target.value })} | |
| /> | |
| </div> | |
| {error && ( | |
| <p className="text-red-500 text-xs mt-1 text-center">{error}</p> | |
| )} | |
| <button | |
| type="submit" | |
| disabled={isLoading} | |
| className="w-full py-3 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition-colors shadow-lg shadow-blue-200 disabled:opacity-50 flex items-center justify-center gap-2" | |
| > | |
| {isLoading ? <Loader2 size={18} className="animate-spin" /> : (isLogin ? '登录' : '注册')} | |
| </button> | |
| </form> | |
| <div className="mt-6 text-center"> | |
| <button | |
| onClick={() => setIsLogin(!isLogin)} | |
| className="text-sm text-zinc-500 hover:text-blue-600 transition-colors" | |
| > | |
| {isLogin ? '还没有账号?立即注册' : '已有账号?返回登录'} | |
| </button> | |
| </div> | |
| </motion.div> | |
| </div> | |
| ); | |
| } | |