new change
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
// src/components/auth/AuthForm.tsx
|
||||
// 认证表单组件 - 包含登录和注册功能,同时提供测试账号信息
|
||||
'use client';
|
||||
import { useState } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { signIn } from 'next-auth/react';
|
||||
import { register } from '@/lib/api/actions';
|
||||
import { register, getVerificationCode } from '@/lib/api/actions';
|
||||
// 由于找不到 '@/components/ui/alert' 模块,推测可能路径有误,这里使用实际路径代替,需根据项目实际情况调整
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Eye, EyeOff } from 'lucide-react';
|
||||
|
||||
@@ -23,6 +24,10 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [success, setSuccess] = useState<string | null>(null);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [verificationCode, setVerificationCode] = useState('');
|
||||
const [isSendingCode, setIsSendingCode] = useState(false);
|
||||
const [countdown, setCountdown] = useState(0);
|
||||
const [codeSuccessMessage, setCodeSuccessMessage] = useState<string | null>(null);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -44,11 +49,22 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查测试账号情况
|
||||
const isTestAccount = usernameField === 'test' && password === 'test';
|
||||
|
||||
// 对于非测试账号,验证验证码是否输入
|
||||
if (!isTestAccount && !verificationCode) {
|
||||
setError('请输入验证码');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用next-auth的signIn功能进行登录,设置redirect为false以便处理错误
|
||||
const result = await signIn('credentials', {
|
||||
username: usernameField, // 使用username或email作为用户名字段
|
||||
email: email, // 传递email字段
|
||||
password,
|
||||
verificationCode, // 传递验证码
|
||||
redirect: false, // 不自动重定向,以便处理错误
|
||||
callbackUrl: '/user-home' // 指定重定向目标
|
||||
});
|
||||
@@ -70,13 +86,21 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
setError('登录过程中发生未知错误');
|
||||
}
|
||||
} else {
|
||||
// 调用注册API
|
||||
const result = await register({
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
minecraftUsername
|
||||
});
|
||||
// 验证验证码是否输入
|
||||
if (!verificationCode) {
|
||||
setError('请输入验证码');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用注册API
|
||||
const result = await register({
|
||||
username,
|
||||
password,
|
||||
email,
|
||||
minecraftUsername,
|
||||
verificationCode
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
setSuccess('注册成功!即将跳转到登录页面...');
|
||||
@@ -100,8 +124,59 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
const resetMessages = () => {
|
||||
setError(null);
|
||||
setSuccess(null);
|
||||
setCodeSuccessMessage(null);
|
||||
};
|
||||
|
||||
// 处理获取验证码
|
||||
const handleGetVerificationCode = async () => {
|
||||
// 检查是否是测试环境使用测试账号
|
||||
const isTestAccount = type === 'login' && username === 'test' && password === 'test';
|
||||
if (isTestAccount) {
|
||||
setCodeSuccessMessage('测试账号无需验证码');
|
||||
return;
|
||||
}
|
||||
|
||||
const targetEmail = type === 'login' ? (email || username) : email;
|
||||
|
||||
if (!targetEmail) {
|
||||
setError(type === 'login' ? '请输入用户名/邮箱' : '请输入邮箱');
|
||||
return;
|
||||
}
|
||||
|
||||
// 简单的邮箱格式验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (type === 'register' || (type === 'login' && targetEmail.includes('@'))) {
|
||||
if (!emailRegex.test(targetEmail)) {
|
||||
setError('请输入有效的邮箱地址');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setIsSendingCode(true);
|
||||
try {
|
||||
const result = await getVerificationCode(targetEmail);
|
||||
if (result.success) {
|
||||
setCodeSuccessMessage('验证码已发送,请注意查收');
|
||||
setCountdown(60); // 设置60秒倒计时
|
||||
} else {
|
||||
setError(result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取验证码失败:', error);
|
||||
setError('获取验证码失败,请稍后再试');
|
||||
} finally {
|
||||
setIsSendingCode(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 倒计时效果
|
||||
useEffect(() => {
|
||||
if (countdown > 0) {
|
||||
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [countdown]);
|
||||
|
||||
// 监听输入变化,清除错误消息
|
||||
const handleInputChange = (setter: React.Dispatch<React.SetStateAction<string>>) => {
|
||||
return (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -125,6 +200,14 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
<AlertDescription>{success}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{/* 验证码发送成功消息 */}
|
||||
{codeSuccessMessage && (
|
||||
<Alert className="bg-blue-50 border-blue-200 text-blue-800">
|
||||
<AlertTitle>提示</AlertTitle>
|
||||
<AlertDescription>{codeSuccessMessage}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="username">用户名</Label>
|
||||
<Input
|
||||
@@ -167,6 +250,33 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 验证码输入区域 */}
|
||||
{type === 'login' && (username !== 'test' || password !== 'test') && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="verificationCode">验证码</Label>
|
||||
<div className="flex space-x-2">
|
||||
<Input
|
||||
id="verificationCode"
|
||||
type="text"
|
||||
value={verificationCode}
|
||||
onChange={handleInputChange(setVerificationCode)}
|
||||
placeholder="请输入验证码"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleGetVerificationCode}
|
||||
disabled={isSendingCode || countdown > 0 || !email && !username}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{isSendingCode ? '发送中...' :
|
||||
countdown > 0 ? `${countdown}s后重新发送` :
|
||||
'获取验证码'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === 'register' && (
|
||||
<>
|
||||
@@ -190,8 +300,33 @@ export default function AuthForm({ type }: AuthFormProps) {
|
||||
onChange={handleInputChange(setMinecraftUsername)}
|
||||
placeholder="您的Minecraft游戏内用户名"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 注册表单的验证码输入区域 */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="verificationCode">验证码</Label>
|
||||
<div className="flex space-x-2">
|
||||
<Input
|
||||
id="verificationCode"
|
||||
type="text"
|
||||
value={verificationCode}
|
||||
onChange={handleInputChange(setVerificationCode)}
|
||||
placeholder="请输入验证码"
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
onClick={handleGetVerificationCode}
|
||||
disabled={isSendingCode || countdown > 0 || !email}
|
||||
className="whitespace-nowrap"
|
||||
>
|
||||
{isSendingCode ? '发送中...' :
|
||||
countdown > 0 ? `${countdown}s后重新发送` :
|
||||
'获取验证码'}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
|
||||
@@ -4,6 +4,7 @@ import axios from 'axios';
|
||||
|
||||
// 配置axios实例,统一处理API请求
|
||||
const apiClient = axios.create({
|
||||
//将baseURL改为实际服务器地址
|
||||
baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',
|
||||
timeout: 10000, // 设置10秒超时
|
||||
headers: {
|
||||
@@ -95,7 +96,8 @@ export const serverSignOut = async () => {
|
||||
export const login = async (credentials: {
|
||||
username?: string;
|
||||
email?: string;
|
||||
password: string
|
||||
password: string;
|
||||
verificationCode: string;
|
||||
}) => {
|
||||
try {
|
||||
// 对于测试环境,可以直接验证测试账号
|
||||
@@ -129,6 +131,18 @@ export const login = async (credentials: {
|
||||
}
|
||||
|
||||
// 实际环境中调用API
|
||||
// 验证码验证 - 测试环境下使用测试账号时可以跳过验证
|
||||
if (!(process.env.NODE_ENV !== 'production' &&
|
||||
usernameField === TEST_USERNAME &&
|
||||
credentials.password === TEST_PASSWORD)) {
|
||||
|
||||
// 非测试账号需要验证验证码
|
||||
const verifyResult = await verifyCode(usernameField, credentials.verificationCode);
|
||||
if (!verifyResult.success) {
|
||||
return verifyResult;
|
||||
}
|
||||
}
|
||||
|
||||
const response = await apiClient.post('/auth/login', credentials);
|
||||
return { success: true, ...response.data };
|
||||
} catch (error) {
|
||||
@@ -146,12 +160,67 @@ export const login = async (credentials: {
|
||||
}
|
||||
};
|
||||
|
||||
// 导出获取验证码函数
|
||||
export const getVerificationCode = async (email: string) => {
|
||||
try {
|
||||
// 验证邮箱格式
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
return { success: false, error: '请输入有效的邮箱地址' };
|
||||
}
|
||||
|
||||
// 调用API获取验证码
|
||||
const response = await apiClient.post('/auth/get-verification-code', { email });
|
||||
return { success: true, ...response.data };
|
||||
} catch (error) {
|
||||
console.error('获取验证码失败:', error);
|
||||
if (error instanceof axios.AxiosError) {
|
||||
if (error.response?.status === 400) {
|
||||
return { success: false, error: error.response.data?.error || '获取验证码失败,请检查邮箱格式' };
|
||||
}
|
||||
if (error.request) {
|
||||
return { success: false, error: '网络连接失败,请检查您的网络设置' };
|
||||
}
|
||||
}
|
||||
return { success: false, error: '获取验证码失败,请稍后再试' };
|
||||
}
|
||||
};
|
||||
|
||||
// 导出验证验证码函数
|
||||
export const verifyCode = async (email: string, code: string) => {
|
||||
try {
|
||||
// 验证输入
|
||||
if (!email?.trim()) {
|
||||
return { success: false, error: '邮箱不能为空' };
|
||||
}
|
||||
if (!code?.trim()) {
|
||||
return { success: false, error: '验证码不能为空' };
|
||||
}
|
||||
|
||||
// 调用API验证验证码
|
||||
const response = await apiClient.post('/auth/verify-code', { email, code });
|
||||
return { success: true, ...response.data };
|
||||
} catch (error) {
|
||||
console.error('验证码验证失败:', error);
|
||||
if (error instanceof axios.AxiosError) {
|
||||
if (error.response?.status === 400) {
|
||||
return { success: false, error: error.response.data?.error || '验证码错误或已过期' };
|
||||
}
|
||||
if (error.request) {
|
||||
return { success: false, error: '网络连接失败,请检查您的网络设置' };
|
||||
}
|
||||
}
|
||||
return { success: false, error: '验证码验证失败,请稍后再试' };
|
||||
}
|
||||
};
|
||||
|
||||
// 导出注册函数
|
||||
export const register = async (userData: {
|
||||
username: string;
|
||||
password: string;
|
||||
email: string;
|
||||
minecraftUsername: string;
|
||||
verificationCode: string;
|
||||
}) => {
|
||||
try {
|
||||
// 基本验证
|
||||
@@ -179,6 +248,12 @@ export const register = async (userData: {
|
||||
return { success: false, error: '请输入有效的邮箱地址' };
|
||||
}
|
||||
|
||||
// 验证码验证
|
||||
const verifyResult = await verifyCode(userData.email, userData.verificationCode);
|
||||
if (!verifyResult.success) {
|
||||
return verifyResult;
|
||||
}
|
||||
|
||||
// 实际环境中调用API
|
||||
const response = await apiClient.post('/auth/register', userData);
|
||||
return { success: true, ...response.data };
|
||||
|
||||
Reference in New Issue
Block a user