diff --git a/next.config.ts b/next.config.ts index e9ffa30..5f003e5 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,14 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ + rewrites: async () => { + return [ + { + source: '/api/v1/:path*', + destination: 'http://localhost:8080/api/v1/:path*', + }, + ]; + }, }; export default nextConfig; diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index 72b6cdd..6ff48cc 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -662,6 +662,8 @@ export default function AuthPage() { )} + + {/* Submit Button */} = ({ onVerify, onClose const response = await axios.get(`${API_BASE_URL}/captcha/generate`, { withCredentials: true // 关键:允许跨域携带凭证 }); - const { code, msg: resMsg, captcha_id, mBase64, tBase64, y } = response.data; + const { code, msg: resMsg, data } = response.data; + const { masterImage, tileImage, captchaId, y } = data; // 后端返回成功状态(code=200) if (code === 200) { // 设置背景图 - setBackgroundImage(mBase64); + setBackgroundImage(masterImage); // 设置拼图图片 - setPuzzleImage(tBase64); + setPuzzleImage(tileImage); // 设置拼图y坐标(从后端获取,以背景图左上角为原点) setPuzzleY(y); // 设置进程ID(用于后续验证) - setProcessId(captcha_id); + setProcessId(captchaId); // 随机生成拼图x坐标(确保拼图在背景图内) // setPuzzlePosition(Math.random() * (CANVAS_WIDTH - 50 - 50) + 50); // 保存后端返回的提示信息 @@ -163,47 +164,59 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose try { // 向后端发送验证请求,参数为滑块位置(x坐标)和进程ID + // 使用sliderPosition作为dx值,这是拼图块左上角的位置 const response = await axios.post(`${API_BASE_URL}/captcha/verify`, { dx: sliderPosition, // 滑块位置(拼图左上角x坐标,以背景图左上角为原点) - captcha_id: processId // 验证码进程ID + captchaId: processId // 验证码进程ID },{ withCredentials: true }); const { code, msg: resMsg, data } = response.data; // 保存后端返回的提示信息 setMsg(resMsg); - // 后端返回成功 (code=200) + // 根据后端返回的code判断验证结果 + // 验证成功:code=200 if (code === 200) { - // 验证成功(data=true) - if (data === true) { - setIsVerified(true); - setVerifyResult(true); - // 延迟1.2秒后调用验证成功回调 - setTimeout(() => onVerify(true), 1200); - } - // 验证失败(data=false) - else { - setVerifyResult(false); - setShowError(true); - // 增加尝试次数 - setAttempts(prev => prev + 1); - // 1.5秒后重置滑块位置并隐藏错误提示 - setTimeout(() => { - setSliderPosition(0); - setShowError(false); - }, 1500); - } - } - // 后端返回参数错误(400)或系统错误(500) - else if (code === 400 || code === 500) { - setVerifyResult('error'); - setShowError(true); // 增加尝试次数 setAttempts(prev => prev + 1); - // 1.5秒后重置滑块位置并隐藏错误提示 + // 重置所有状态,确保验证成功状态的纯净性 + setShowError(false); + setVerifyResult(false); + // 直接设置验证成功状态,不使用异步更新 + setIsVerified(true); + // 延迟1.2秒后调用验证成功回调 + setTimeout(() => onVerify(true), 1200); + } + // 验证失败:code=400 + else if (code === 400) { + // 确保错误状态的正确性:验证失败显示红色 + setVerifyResult(false); + setShowError(true); + setIsVerified(false); + // 增加尝试次数 + setAttempts(prev => prev + 1); + // 1.5秒后重置滑块位置、隐藏错误提示并重置验证结果 setTimeout(() => { setSliderPosition(0); setShowError(false); + setVerifyResult(false); + setIsVerified(false); + }, 1500); + } + // 后端返回系统错误(500) + else if (code === 500) { + // 系统错误显示橙色 + setVerifyResult('error'); + setShowError(true); + setIsVerified(false); + // 增加尝试次数 + setAttempts(prev => prev + 1); + // 1.5秒后重置滑块位置、隐藏错误提示并重置验证结果 + setTimeout(() => { + setSliderPosition(0); + setShowError(false); + setVerifyResult(false); + setIsVerified(false); }, 1500); } @@ -318,12 +331,12 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose // 加载中显示旋转动画 return
; } + // 验证成功时,无论其他状态如何,都显示对勾图标 if (isVerified) { - // 验证成功显示对勾图标 return ; } - if (showError) { - // 验证失败显示叉号图标 + // 验证失败或错误时显示叉号图标 + if (showError || verifyResult === 'error') { return ; } // 默认显示蓝色圆点 @@ -332,8 +345,12 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose const getStatusText = () => { - if (verifyResult === 'error' || showError || isVerified) { - // 错误、验证失败或成功时显示后端返回的消息 + if (isVerified) { + // 验证成功时优先显示成功消息 + return msg; + } + if (verifyResult === 'error' || showError) { + // 错误或验证失败时显示后端返回的消息 return msg; } // 默认显示拖拽提示 @@ -342,17 +359,21 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose const getStatusColor = () => { - if (verifyResult === 'error') return 'text-orange-700'; if (isVerified) return 'text-green-700'; + if (verifyResult === 'error') return 'text-orange-700'; if (showError) return 'text-red-700'; return 'text-gray-600'; }; const getProgressColor = () => { - if (verifyResult === 'error') return 'bg-gradient-to-r from-orange-400 to-orange-500'; + // 验证成功时,无论其他状态如何,都显示绿色渐变 if (isVerified) return 'bg-gradient-to-r from-green-400 to-green-500'; - if (showError) return 'bg-gradient-to-r from-red-400 to-red-500'; + // 系统错误(后端返回400/500)显示橙色渐变 + if (verifyResult === 'error') return 'bg-gradient-to-r from-orange-400 to-orange-500'; + // 验证失败(后端返回200但data=false)显示红色渐变 + if (showError && verifyResult !== 'error') return 'bg-gradient-to-r from-red-400 to-red-500'; + // 默认显示蓝色渐变 return 'bg-gradient-to-r from-blue-400 to-blue-500'; }; @@ -386,7 +407,7 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose /> )} {/* 可移动拼图块 */} - {puzzleImage && !isVerified && ( + {puzzleImage && (
= ({ onVerify, onClose alt="拼图块" className={`${isVerified ? 'opacity-100' : 'opacity-90'}`} style={{ - filter: isVerified ? 'drop-shadow(0 0 10px rgba(34, 197, 94, 0.5))' : 'drop-shadow(0 2px 4px rgba(0,0,0,0.3))' }} /> @@ -450,7 +470,7 @@ export const SliderCaptcha: React.FC = ({ onVerify, onClose {/* 底部信息区域 */}
- 尝试次数: {attempts + 1}/5 + 尝试次数: {attempts} 安全验证 diff --git a/src/lib/api.ts b/src/lib/api.ts index 17d62b5..0007298 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,4 @@ -export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8080/api/v1'; +export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || '/api/v1'; export interface Texture { id: number;