feat: 手机验证码调试
This commit is contained in:
@@ -219,5 +219,14 @@ module.exports = {
|
|||||||
devMiddleware: {
|
devMiddleware: {
|
||||||
writeToDisk: false,
|
writeToDisk: false,
|
||||||
},
|
},
|
||||||
|
// 代理配置:将 /api 请求代理到后端服务器
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: 'http://49.232.185.254:5001',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
logLevel: 'debug',
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import WechatRegister from './WechatRegister';
|
|||||||
|
|
||||||
// API配置
|
// API配置
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000";
|
const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5001";
|
||||||
|
|
||||||
// 统一配置对象
|
// 统一配置对象
|
||||||
const AUTH_CONFIG = {
|
const AUTH_CONFIG = {
|
||||||
@@ -54,7 +54,7 @@ const AUTH_CONFIG = {
|
|||||||
// API配置
|
// API配置
|
||||||
api: {
|
api: {
|
||||||
endpoint: '/api/auth/register/phone',
|
endpoint: '/api/auth/register/phone',
|
||||||
purpose: 'register',
|
purpose: 'login', // ⚡ 统一使用 'login' 模式
|
||||||
},
|
},
|
||||||
|
|
||||||
// 功能开关
|
// 功能开关
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, useEffect, useRef, useCallback } from "react";
|
import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -15,6 +15,7 @@ import { authService, WECHAT_STATUS, STATUS_MESSAGES } from "../../services/auth
|
|||||||
// 配置常量
|
// 配置常量
|
||||||
const POLL_INTERVAL = 2000; // 轮询间隔:2秒
|
const POLL_INTERVAL = 2000; // 轮询间隔:2秒
|
||||||
const QR_CODE_TIMEOUT = 300000; // 二维码超时:5分钟
|
const QR_CODE_TIMEOUT = 300000; // 二维码超时:5分钟
|
||||||
|
const ENABLE_MOCK = 'true'; // 通过环境变量控制 mock
|
||||||
|
|
||||||
export default function WechatRegister() {
|
export default function WechatRegister() {
|
||||||
// 状态管理
|
// 状态管理
|
||||||
@@ -22,11 +23,13 @@ export default function WechatRegister() {
|
|||||||
const [wechatSessionId, setWechatSessionId] = useState("");
|
const [wechatSessionId, setWechatSessionId] = useState("");
|
||||||
const [wechatStatus, setWechatStatus] = useState(WECHAT_STATUS.NONE);
|
const [wechatStatus, setWechatStatus] = useState(WECHAT_STATUS.NONE);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [scale, setScale] = useState(1); // iframe 缩放比例
|
||||||
|
|
||||||
// 使用 useRef 管理定时器,避免闭包问题和内存泄漏
|
// 使用 useRef 管理定时器,避免闭包问题和内存泄漏
|
||||||
const pollIntervalRef = useRef(null);
|
const pollIntervalRef = useRef(null);
|
||||||
const timeoutRef = useRef(null);
|
const timeoutRef = useRef(null);
|
||||||
const isMountedRef = useRef(true); // 追踪组件挂载状态
|
const isMountedRef = useRef(true); // 追踪组件挂载状态
|
||||||
|
const containerRef = useRef(null); // 容器DOM引用
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -189,9 +192,42 @@ export default function WechatRegister() {
|
|||||||
* 获取微信二维码
|
* 获取微信二维码
|
||||||
*/
|
*/
|
||||||
const getWechatQRCode = async () => {
|
const getWechatQRCode = async () => {
|
||||||
|
debugger
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
// 开发环境:使用 mock 数据
|
||||||
|
if (ENABLE_MOCK) {
|
||||||
|
console.log('🔧 开发模式:使用 Mock 数据');
|
||||||
|
|
||||||
|
// 模拟网络延迟
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
|
// 检查组件是否已卸载
|
||||||
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
|
// Mock 数据 - 使用一个测试页面
|
||||||
|
const mockResponse = {
|
||||||
|
auth_url: 'https://open.weixin.qq.com/connect/qrconnect?appid=mock&redirect_uri=mock&response_type=code&scope=snsapi_login&state=mock#wechat_redirect',
|
||||||
|
session_id: 'mock-session-' + Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
|
setWechatAuthUrl(mockResponse.auth_url);
|
||||||
|
setWechatSessionId(mockResponse.session_id);
|
||||||
|
setWechatStatus(WECHAT_STATUS.WAITING);
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Mock 模式",
|
||||||
|
description: "正在使用测试数据展示",
|
||||||
|
status: "info",
|
||||||
|
duration: 2000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 不启动轮询(避免无效请求)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生产环境:调用真实 API
|
||||||
const response = await authService.getWechatQRCode();
|
const response = await authService.getWechatQRCode();
|
||||||
|
|
||||||
// 检查组件是否已卸载
|
// 检查组件是否已卸载
|
||||||
@@ -236,6 +272,46 @@ export default function WechatRegister() {
|
|||||||
};
|
};
|
||||||
}, [clearTimers]);
|
}, [clearTimers]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测量容器尺寸并计算缩放比例
|
||||||
|
*/
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
// 微信授权页面的原始尺寸
|
||||||
|
const ORIGINAL_WIDTH = 600;
|
||||||
|
const ORIGINAL_HEIGHT = 800;
|
||||||
|
|
||||||
|
const calculateScale = () => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
const { width, height } = containerRef.current.getBoundingClientRect();
|
||||||
|
|
||||||
|
// 计算宽高比例,取较小值确保完全适配
|
||||||
|
const scaleX = width / ORIGINAL_WIDTH;
|
||||||
|
const scaleY = height / ORIGINAL_HEIGHT;
|
||||||
|
const newScale = Math.min(scaleX, scaleY, 1.0); // 最大不超过1.0
|
||||||
|
|
||||||
|
// 设置最小缩放比例为0.3,避免过小
|
||||||
|
setScale(Math.max(newScale, 0.3));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始计算
|
||||||
|
calculateScale();
|
||||||
|
|
||||||
|
// 使用 ResizeObserver 监听容器尺寸变化
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
calculateScale();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (containerRef.current) {
|
||||||
|
resizeObserver.observe(containerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
return () => {
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
}, [wechatStatus]); // 当状态变化时重新计算
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 渲染状态提示文本
|
* 渲染状态提示文本
|
||||||
*/
|
*/
|
||||||
@@ -246,15 +322,47 @@ export default function WechatRegister() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Text fontSize="xs" color="gray.500">
|
<Text fontSize="xs" color="gray.500">
|
||||||
{STATUS_MESSAGES[wechatStatus] || STATUS_MESSAGES[WECHAT_STATUS.WAITING]}
|
{STATUS_MESSAGES[wechatStatus]}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VStack spacing={2}>
|
<VStack spacing={2} display="flex" alignItems="center" justifyContent="center">
|
||||||
|
{wechatStatus === WECHAT_STATUS.WAITING ? (
|
||||||
|
<>
|
||||||
<Text fontSize="lg" fontWeight="bold" color="gray.700" whiteSpace="nowrap">
|
<Text fontSize="lg" fontWeight="bold" color="gray.700" whiteSpace="nowrap">
|
||||||
微信扫一扫
|
微信扫码
|
||||||
|
</Text>
|
||||||
|
<Box
|
||||||
|
ref={containerRef}
|
||||||
|
position="relative"
|
||||||
|
width="150px"
|
||||||
|
height="100px"
|
||||||
|
maxWidth="100%"
|
||||||
|
display="flex"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
src={wechatAuthUrl}
|
||||||
|
width="300"
|
||||||
|
height="350"
|
||||||
|
style={{
|
||||||
|
borderRadius: '8px',
|
||||||
|
border: 'none',
|
||||||
|
transform: `scale(${scale})`,
|
||||||
|
transformOrigin: 'center center'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
{renderStatusText()}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Text fontSize="lg" fontWeight="bold" color="gray.700" whiteSpace="nowrap">
|
||||||
|
微信扫码
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
@@ -265,18 +373,7 @@ export default function WechatRegister() {
|
|||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
>
|
>
|
||||||
{/* 灰色二维码底图 - 始终显示 */}
|
{/* 灰色二维码底图 - 始终显示 */}
|
||||||
{wechatStatus === WECHAT_STATUS.WAITING ? (
|
|
||||||
<Box position="relative" width="96px" height="96px">
|
|
||||||
<iframe
|
|
||||||
src={wechatAuthUrl}
|
|
||||||
width="96"
|
|
||||||
height="96"
|
|
||||||
style={{ borderRadius: '8px', border: 'none' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
) : (
|
|
||||||
<Icon as={FaQrcode} w={24} h={24} color="gray.300" />
|
<Icon as={FaQrcode} w={24} h={24} color="gray.300" />
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 加载动画 */}
|
{/* 加载动画 */}
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
@@ -335,7 +432,9 @@ export default function WechatRegister() {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* 扫码状态提示 */}
|
{/* 扫码状态提示 */}
|
||||||
{renderStatusText()}
|
{/* {renderStatusText()} */}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,22 +36,10 @@ const ProtectedRoute = ({ children }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 未登录时显示占位符(弹窗会自动打开)
|
// 未登录时,渲染子组件 + 自动打开弹窗(通过 useEffect)
|
||||||
|
// 弹窗会在 useEffect 中自动触发,页面正常显示
|
||||||
if (!isAuthenticated || !user) {
|
if (!isAuthenticated || !user) {
|
||||||
return (
|
return children;
|
||||||
<Box
|
|
||||||
height="100vh"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="center"
|
|
||||||
bg="gray.50"
|
|
||||||
>
|
|
||||||
<VStack spacing={4}>
|
|
||||||
<Spinner size="xl" color="blue.500" thickness="4px" />
|
|
||||||
<Text fontSize="lg" color="gray.600">请先登录...</Text>
|
|
||||||
</VStack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已登录,渲染子组件
|
// 已登录,渲染子组件
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useToast } from '@chakra-ui/react';
|
|||||||
|
|
||||||
// API基础URL配置
|
// API基础URL配置
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const API_BASE_URL = isProduction ? "" : process.env.REACT_APP_API_URL || "http://49.232.185.254:5000";
|
const API_BASE_URL = isProduction ? "" : process.env.REACT_APP_API_URL || "http://49.232.185.254:5001";
|
||||||
|
|
||||||
// 创建认证上下文
|
// 创建认证上下文
|
||||||
const AuthContext = createContext();
|
const AuthContext = createContext();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const useAuth = () => {
|
|||||||
// 认证提供者组件
|
// 认证提供者组件
|
||||||
export const AuthProvider = ({ children }) => {
|
export const AuthProvider = ({ children }) => {
|
||||||
const [user, setUser] = useState(null);
|
const [user, setUser] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(false); // ⚡ 改为 false,不阻塞首屏渲染
|
const [isLoading, setIsLoading] = useState(true); // ⚡ 串行执行,阻塞渲染直到 Session 检查完成
|
||||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -66,8 +66,10 @@ export const AuthProvider = ({ children }) => {
|
|||||||
// 网络错误或超时,设置为未登录状态
|
// 网络错误或超时,设置为未登录状态
|
||||||
setUser(null);
|
setUser(null);
|
||||||
setIsAuthenticated(false);
|
setIsAuthenticated(false);
|
||||||
|
} finally {
|
||||||
|
// ⚡ Session 检查完成后,停止加载状态
|
||||||
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
// ⚡ 移除 finally 中的 setIsLoading(false),不再阻塞渲染
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ⚡ 初始化时检查Session - 并行执行,不阻塞页面渲染
|
// ⚡ 初始化时检查Session - 并行执行,不阻塞页面渲染
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// src/contexts/AuthModalContext.js
|
// src/contexts/AuthModalContext.js
|
||||||
import { createContext, useContext, useState, useCallback } from 'react';
|
import { createContext, useContext, useState, useCallback } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { useAuth } from './AuthContext';
|
||||||
|
|
||||||
const AuthModalContext = createContext();
|
const AuthModalContext = createContext();
|
||||||
|
|
||||||
@@ -19,6 +21,9 @@ export const useAuthModal = () => {
|
|||||||
* 管理统一的认证弹窗状态(登录/注册合并)
|
* 管理统一的认证弹窗状态(登录/注册合并)
|
||||||
*/
|
*/
|
||||||
export const AuthModalProvider = ({ children }) => {
|
export const AuthModalProvider = ({ children }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { isAuthenticated } = useAuth();
|
||||||
|
|
||||||
// 弹窗状态(统一的认证弹窗)
|
// 弹窗状态(统一的认证弹窗)
|
||||||
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
|
||||||
|
|
||||||
@@ -41,12 +46,18 @@ export const AuthModalProvider = ({ children }) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭认证弹窗
|
* 关闭认证弹窗
|
||||||
|
* 如果用户未登录,跳转到首页
|
||||||
*/
|
*/
|
||||||
const closeModal = useCallback(() => {
|
const closeModal = useCallback(() => {
|
||||||
setIsAuthModalOpen(false);
|
setIsAuthModalOpen(false);
|
||||||
setRedirectUrl(null);
|
setRedirectUrl(null);
|
||||||
setOnSuccessCallback(null);
|
setOnSuccessCallback(null);
|
||||||
}, []);
|
|
||||||
|
// ⭐ 如果用户关闭弹窗时仍未登录,跳转到首页
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
navigate('/home');
|
||||||
|
}
|
||||||
|
}, [isAuthenticated, navigate]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录/注册成功处理
|
* 登录/注册成功处理
|
||||||
@@ -62,17 +73,12 @@ export const AuthModalProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有重定向URL,则跳转
|
// ⭐ 登录成功后,只关闭弹窗,留在当前页面(不跳转)
|
||||||
if (redirectUrl) {
|
// 移除了原有的 redirectUrl 跳转逻辑
|
||||||
// 使用 window.location.href 确保完整刷新页面状态
|
setIsAuthModalOpen(false);
|
||||||
setTimeout(() => {
|
setRedirectUrl(null);
|
||||||
window.location.href = redirectUrl;
|
setOnSuccessCallback(null);
|
||||||
}, 500); // 延迟500ms,让用户看到成功提示
|
}, [onSuccessCallback]);
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭弹窗
|
|
||||||
closeModal();
|
|
||||||
}, [onSuccessCallback, redirectUrl, closeModal]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提供给子组件的上下文值
|
* 提供给子组件的上下文值
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import WechatRegister from "../../../components/Auth/WechatRegister";
|
|||||||
|
|
||||||
// API配置
|
// API配置
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000";
|
const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5001";
|
||||||
|
|
||||||
export default function SignInIllustration() {
|
export default function SignInIllustration() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export default function SettingsPage() {
|
|||||||
try {
|
try {
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
||||||
? ""
|
? ""
|
||||||
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5000";
|
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5001";
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/api/account/password-status`, {
|
const response = await fetch(`${API_BASE_URL}/api/account/password-status`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
@@ -188,7 +188,7 @@ export default function SettingsPage() {
|
|||||||
// 调用后端API修改密码
|
// 调用后端API修改密码
|
||||||
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
const API_BASE_URL = process.env.NODE_ENV === 'production'
|
||||||
? ""
|
? ""
|
||||||
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5000";
|
: process.env.REACT_APP_API_URL || "http://49.232.185.254:5001";
|
||||||
|
|
||||||
const response = await fetch(`${API_BASE_URL}/api/account/change-password`, {
|
const response = await fetch(`${API_BASE_URL}/api/account/change-password`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
Reference in New Issue
Block a user