new change

This commit is contained in:
chenZB666
2025-10-10 17:08:29 +08:00
parent b96533827e
commit 960442b1fd
2 changed files with 221 additions and 11 deletions

View File

@@ -1,12 +1,13 @@
// src/components/auth/AuthForm.tsx // src/components/auth/AuthForm.tsx
// 认证表单组件 - 包含登录和注册功能,同时提供测试账号信息 // 认证表单组件 - 包含登录和注册功能,同时提供测试账号信息
'use client'; 'use client';
import { useState } from 'react'; import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { signIn } from 'next-auth/react'; 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 { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Eye, EyeOff } from 'lucide-react'; import { Eye, EyeOff } from 'lucide-react';
@@ -23,6 +24,10 @@ export default function AuthForm({ type }: AuthFormProps) {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null); const [success, setSuccess] = useState<string | null>(null);
const [showPassword, setShowPassword] = useState(false); 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) => { const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
@@ -44,11 +49,22 @@ export default function AuthForm({ type }: AuthFormProps) {
return; return;
} }
// 检查测试账号情况
const isTestAccount = usernameField === 'test' && password === 'test';
// 对于非测试账号,验证验证码是否输入
if (!isTestAccount && !verificationCode) {
setError('请输入验证码');
setIsLoading(false);
return;
}
// 使用next-auth的signIn功能进行登录设置redirect为false以便处理错误 // 使用next-auth的signIn功能进行登录设置redirect为false以便处理错误
const result = await signIn('credentials', { const result = await signIn('credentials', {
username: usernameField, // 使用username或email作为用户名字段 username: usernameField, // 使用username或email作为用户名字段
email: email, // 传递email字段 email: email, // 传递email字段
password, password,
verificationCode, // 传递验证码
redirect: false, // 不自动重定向,以便处理错误 redirect: false, // 不自动重定向,以便处理错误
callbackUrl: '/user-home' // 指定重定向目标 callbackUrl: '/user-home' // 指定重定向目标
}); });
@@ -70,13 +86,21 @@ export default function AuthForm({ type }: AuthFormProps) {
setError('登录过程中发生未知错误'); setError('登录过程中发生未知错误');
} }
} else { } else {
// 调用注册API // 验证验证码是否输入
const result = await register({ if (!verificationCode) {
username, setError('请输入验证码');
password, setIsLoading(false);
email, return;
minecraftUsername }
});
// 调用注册API
const result = await register({
username,
password,
email,
minecraftUsername,
verificationCode
});
if (result.success) { if (result.success) {
setSuccess('注册成功!即将跳转到登录页面...'); setSuccess('注册成功!即将跳转到登录页面...');
@@ -100,8 +124,59 @@ export default function AuthForm({ type }: AuthFormProps) {
const resetMessages = () => { const resetMessages = () => {
setError(null); setError(null);
setSuccess(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>>) => { const handleInputChange = (setter: React.Dispatch<React.SetStateAction<string>>) => {
return (e: React.ChangeEvent<HTMLInputElement>) => { return (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -125,6 +200,14 @@ export default function AuthForm({ type }: AuthFormProps) {
<AlertDescription>{success}</AlertDescription> <AlertDescription>{success}</AlertDescription>
</Alert> </Alert>
)} )}
{/* 验证码发送成功消息 */}
{codeSuccessMessage && (
<Alert className="bg-blue-50 border-blue-200 text-blue-800">
<AlertTitle></AlertTitle>
<AlertDescription>{codeSuccessMessage}</AlertDescription>
</Alert>
)}
<div> <div>
<Label htmlFor="username"></Label> <Label htmlFor="username"></Label>
<Input <Input
@@ -167,6 +250,33 @@ export default function AuthForm({ type }: AuthFormProps) {
</button> </button>
</div> </div>
</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' && ( {type === 'register' && (
<> <>
@@ -190,8 +300,33 @@ export default function AuthForm({ type }: AuthFormProps) {
onChange={handleInputChange(setMinecraftUsername)} onChange={handleInputChange(setMinecraftUsername)}
placeholder="您的Minecraft游戏内用户名" 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>
</> </div>
</>
)} )}
<Button type="submit" className="w-full" disabled={isLoading}> <Button type="submit" className="w-full" disabled={isLoading}>

View File

@@ -4,6 +4,7 @@ import axios from 'axios';
// 配置axios实例统一处理API请求 // 配置axios实例统一处理API请求
const apiClient = axios.create({ const apiClient = axios.create({
//将baseURL改为实际服务器地址
baseURL: process.env.NEXT_PUBLIC_API_URL || '/api', baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',
timeout: 10000, // 设置10秒超时 timeout: 10000, // 设置10秒超时
headers: { headers: {
@@ -95,7 +96,8 @@ export const serverSignOut = async () => {
export const login = async (credentials: { export const login = async (credentials: {
username?: string; username?: string;
email?: string; email?: string;
password: string password: string;
verificationCode: string;
}) => { }) => {
try { try {
// 对于测试环境,可以直接验证测试账号 // 对于测试环境,可以直接验证测试账号
@@ -129,6 +131,18 @@ export const login = async (credentials: {
} }
// 实际环境中调用API // 实际环境中调用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); const response = await apiClient.post('/auth/login', credentials);
return { success: true, ...response.data }; return { success: true, ...response.data };
} catch (error) { } 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: { export const register = async (userData: {
username: string; username: string;
password: string; password: string;
email: string; email: string;
minecraftUsername: string; minecraftUsername: string;
verificationCode: string;
}) => { }) => {
try { try {
// 基本验证 // 基本验证
@@ -179,6 +248,12 @@ export const register = async (userData: {
return { success: false, error: '请输入有效的邮箱地址' }; return { success: false, error: '请输入有效的邮箱地址' };
} }
// 验证码验证
const verifyResult = await verifyCode(userData.email, userData.verificationCode);
if (!verifyResult.success) {
return verifyResult;
}
// 实际环境中调用API // 实际环境中调用API
const response = await apiClient.post('/auth/register', userData); const response = await apiClient.post('/auth/register', userData);
return { success: true, ...response.data }; return { success: true, ...response.data };