From 7d283aab8e59a0051a0d70b67bcb4cc8ad4a273a Mon Sep 17 00:00:00 2001
From: zdl <3489966805@qq.com>
Date: Wed, 15 Oct 2025 11:43:04 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B3=A8=E5=86=8C=E5=92=8C=E7=99=BB?=
=?UTF-8?q?=E5=BD=95=E5=85=BC=E5=AE=B9h5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/Auth/AuthFormContent.js | 104 +++++++++++++++--
src/components/Auth/AuthModalManager.js | 35 ++++--
src/layouts/Home.js | 6 +-
src/routes.js | 9 ++
src/services/authService.js | 27 ++++-
src/views/Pages/WechatCallback.js | 144 ++++++++++++++++++++++++
6 files changed, 306 insertions(+), 19 deletions(-)
create mode 100644 src/views/Pages/WechatCallback.js
diff --git a/src/components/Auth/AuthFormContent.js b/src/components/Auth/AuthFormContent.js
index 7a7cd6f7..98f6540b 100644
--- a/src/components/Auth/AuthFormContent.js
+++ b/src/components/Auth/AuthFormContent.js
@@ -10,6 +10,7 @@ import {
Heading,
VStack,
HStack,
+ Stack,
useToast,
Icon,
FormErrorMessage,
@@ -22,10 +23,14 @@ import {
AlertDialogOverlay,
Text,
Link as ChakraLink,
+ useBreakpointValue,
+ Divider,
+ IconButton,
} from "@chakra-ui/react";
-import { FaLock } from "react-icons/fa";
+import { FaLock, FaWeixin } from "react-icons/fa";
import { useAuth } from "../../contexts/AuthContext";
import { useAuthModal } from "../../contexts/AuthModalContext";
+import { authService } from "../../services/authService";
import AuthHeader from './AuthHeader';
import VerificationCodeInput from './VerificationCodeInput';
import WechatRegister from './WechatRegister';
@@ -37,7 +42,7 @@ const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000";
// 统一配置对象
const AUTH_CONFIG = {
// UI文本
- title: "欢迎使用价值前沿",
+ title: "价值前沿",
subtitle: "开启您的投资之旅",
formTitle: "手机号验证",
buttonText: "登录/注册",
@@ -79,6 +84,10 @@ export default function AuthFormContent() {
const [showNicknamePrompt, setShowNicknamePrompt] = useState(false);
const [currentPhone, setCurrentPhone] = useState("");
+ // 响应式布局配置
+ const isMobile = useBreakpointValue({ base: true, md: false });
+ const stackDirection = useBreakpointValue({ base: "column", md: "row" });
+ const stackSpacing = useBreakpointValue({ base: 4, md: 8 });
// 表单数据
const [formData, setFormData] = useState({
@@ -298,6 +307,44 @@ export default function AuthFormContent() {
}
};
+ // 微信H5登录处理
+ const handleWechatH5Login = async () => {
+ try {
+ // 1. 构建回调URL
+ const redirectUrl = `${window.location.origin}/home/wechat-callback`;
+
+ // 2. 显示提示
+ toast({
+ title: "即将跳转",
+ description: "正在跳转到微信授权页面...",
+ status: "info",
+ duration: 2000,
+ isClosable: true,
+ });
+
+ // 3. 获取微信H5授权URL
+ const response = await authService.getWechatH5AuthUrl(redirectUrl);
+
+ if (!response || !response.auth_url) {
+ throw new Error('获取授权链接失败');
+ }
+
+ // 4. 延迟跳转,让用户看到提示
+ setTimeout(() => {
+ window.location.href = response.auth_url;
+ }, 500);
+ } catch (error) {
+ console.error('微信H5登录失败:', error);
+ toast({
+ title: "跳转失败",
+ description: error.message || "请稍后重试",
+ status: "error",
+ duration: 3000,
+ isClosable: true,
+ });
+ }
+ };
+
// 组件卸载时清理
useEffect(() => {
isMountedRef.current = true;
@@ -311,8 +358,8 @@ export default function AuthFormContent() {
<>
-
-
+
+
-
-
-
-
+
+ {/* 桌面端:右侧二维码扫描 */}
+ {!isMobile && (
+
+
+
+
+
+ )}
+
{/* 只在需要时才渲染 AlertDialog,避免创建不必要的 Portal */}
diff --git a/src/components/Auth/AuthModalManager.js b/src/components/Auth/AuthModalManager.js
index 6fcacf62..40e38cd6 100644
--- a/src/components/Auth/AuthModalManager.js
+++ b/src/components/Auth/AuthModalManager.js
@@ -23,10 +23,30 @@ export default function AuthModalManager() {
// 响应式尺寸配置
const modalSize = useBreakpointValue({
- base: "full", // 移动端:全屏
- sm: "xl", // 小屏:xl
- md: "2xl", // 中屏:2xl
- lg: "4xl" // 大屏:4xl
+ base: "md", // 移动端:md(不占满全屏)
+ sm: "md", // 小屏:md
+ md: "lg", // 中屏:lg
+ lg: "xl" // 大屏:xl(更紧凑)
+ });
+
+ // 响应式宽度配置
+ const modalMaxW = useBreakpointValue({
+ base: "90%", // 移动端:屏幕宽度的90%
+ sm: "90%", // 小屏:90%
+ md: "700px", // 中屏:固定700px
+ lg: "700px" // 大屏:固定700px
+ });
+
+ // 响应式水平边距
+ const modalMx = useBreakpointValue({
+ base: 4, // 移动端:左右各16px边距
+ md: "auto" // 桌面端:自动居中
+ });
+
+ // 响应式垂直边距
+ const modalMy = useBreakpointValue({
+ base: 8, // 移动端:上下各32px边距
+ md: 8 // 桌面端:上下各32px边距
});
// 条件渲染:只在打开时才渲染 Modal,避免创建不必要的 Portal
@@ -56,8 +76,9 @@ export default function AuthModalManager() {
bg="white"
boxShadow="2xl"
borderRadius="2xl"
- maxW={modalSize === "full" ? "100%" : "900px"}
- my={modalSize === "full" ? 0 : 8}
+ maxW={modalMaxW}
+ mx={modalMx}
+ my={modalMy}
position="relative"
>
{/* 关闭按钮 */}
@@ -75,7 +96,7 @@ export default function AuthModalManager() {
/>
{/* 弹窗主体内容 */}
-
+
diff --git a/src/layouts/Home.js b/src/layouts/Home.js
index 165d0f2e..edc8545a 100755
--- a/src/layouts/Home.js
+++ b/src/layouts/Home.js
@@ -13,9 +13,10 @@ import SettingsPage from "views/Settings/SettingsPage";
import CenterDashboard from "views/Dashboard/Center";
import Subscription from "views/Pages/Account/Subscription";
-// 懒加载隐私政策和用户协议页面
+// 懒加载隐私政策、用户协议和微信回调页面
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
+const WechatCallback = React.lazy(() => import("views/Pages/WechatCallback"));
// 导入保护路由组件
import ProtectedRoute from "../components/ProtectedRoute";
@@ -76,6 +77,9 @@ export default function Home() {
{/* 用户协议页面 - 无需登录 */}
} />
+ {/* 微信授权回调页面 - 无需登录 */}
+ } />
+
{/* 其他可能的路由 */}
} />
diff --git a/src/routes.js b/src/routes.js
index c357fec0..7f692fef 100755
--- a/src/routes.js
+++ b/src/routes.js
@@ -85,6 +85,7 @@ const StockOverview = React.lazy(() => import("views/StockOverview"));
const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
+const WechatCallback = React.lazy(() => import("views/Pages/WechatCallback"));
const dashRoutes = [
{
name: "Dashboard",
@@ -229,6 +230,14 @@ const dashRoutes = [
layout: "/home",
invisible: true, // 不在侧边栏显示
},
+ {
+ name: "微信授权回调",
+ path: "/wechat-callback",
+ icon: ,
+ component: WechatCallback,
+ layout: "/home",
+ invisible: true, // 不在侧边栏显示
+ },
{
name: "PAGES",
category: "pages",
diff --git a/src/services/authService.js b/src/services/authService.js
index 9edc57ab..b7e55a8f 100644
--- a/src/services/authService.js
+++ b/src/services/authService.js
@@ -63,13 +63,38 @@ const apiRequest = async (url, options = {}) => {
export const authService = {
/**
- * 获取微信二维码授权链接
+ * 获取微信二维码授权链接(PC扫码登录)
* @returns {Promise<{auth_url: string, session_id: string}>}
*/
getWechatQRCode: async () => {
return await apiRequest('/api/auth/wechat/qrcode');
},
+ /**
+ * 获取微信H5授权链接(移动端网页授权)
+ * @param {string} redirectUrl - 授权成功后的回调地址
+ * @returns {Promise<{auth_url: string}>}
+ */
+ getWechatH5AuthUrl: async (redirectUrl) => {
+ return await apiRequest('/api/auth/wechat/h5-auth', {
+ method: 'POST',
+ body: JSON.stringify({ redirect_url: redirectUrl }),
+ });
+ },
+
+ /**
+ * 微信H5授权回调处理
+ * @param {string} code - 微信授权code
+ * @param {string} state - 状态参数
+ * @returns {Promise<{success: boolean, user?: object, token?: string}>}
+ */
+ handleWechatH5Callback: async (code, state) => {
+ return await apiRequest('/api/auth/wechat/h5-callback', {
+ method: 'POST',
+ body: JSON.stringify({ code, state }),
+ });
+ },
+
/**
* 检查微信扫码状态
* @param {string} sessionId - 会话ID
diff --git a/src/views/Pages/WechatCallback.js b/src/views/Pages/WechatCallback.js
new file mode 100644
index 00000000..b795e81f
--- /dev/null
+++ b/src/views/Pages/WechatCallback.js
@@ -0,0 +1,144 @@
+// src/views/Pages/WechatCallback.js
+import React, { useEffect, useState } from "react";
+import { useNavigate, useSearchParams } from "react-router-dom";
+import {
+ Box,
+ Container,
+ VStack,
+ Spinner,
+ Text,
+ Icon,
+ useColorModeValue,
+ Heading,
+} from "@chakra-ui/react";
+import { FaCheckCircle, FaTimesCircle } from "react-icons/fa";
+import { authService } from "../../services/authService";
+import { useAuth } from "../../contexts/AuthContext";
+
+/**
+ * 微信H5授权回调页面
+ * 处理微信授权后的回调,完成登录流程
+ */
+export default function WechatCallback() {
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const { checkSession } = useAuth();
+
+ const [status, setStatus] = useState("loading"); // loading, success, error
+ const [message, setMessage] = useState("正在处理微信授权...");
+
+ const bgColor = useColorModeValue("gray.50", "gray.900");
+ const boxBg = useColorModeValue("white", "gray.800");
+
+ useEffect(() => {
+ const handleCallback = async () => {
+ try {
+ // 1. 获取URL参数
+ const code = searchParams.get("code");
+ const state = searchParams.get("state");
+
+ // 2. 参数验证
+ if (!code) {
+ throw new Error("授权失败:缺少授权码");
+ }
+
+ // 3. 调用后端处理回调
+ const response = await authService.handleWechatH5Callback(code, state);
+
+ if (!response || !response.success) {
+ throw new Error(response?.error || "授权失败,请重试");
+ }
+
+ // 4. 存储用户信息(如果有返回token)
+ if (response.token) {
+ localStorage.setItem("token", response.token);
+ }
+ if (response.user) {
+ localStorage.setItem("user", JSON.stringify(response.user));
+ }
+
+ // 5. 更新session
+ await checkSession();
+
+ // 6. 显示成功状态
+ setStatus("success");
+ setMessage("登录成功!正在跳转...");
+
+ // 7. 延迟跳转到首页
+ setTimeout(() => {
+ navigate("/home", { replace: true });
+ }, 1500);
+ } catch (error) {
+ console.error("微信授权回调处理失败:", error);
+ setStatus("error");
+ setMessage(error.message || "授权失败,请重试");
+
+ // 3秒后返回首页
+ setTimeout(() => {
+ navigate("/home", { replace: true });
+ }, 3000);
+ }
+ };
+
+ handleCallback();
+ }, [searchParams, navigate, checkSession]);
+
+ return (
+
+
+
+
+ {/* 状态图标 */}
+ {status === "loading" && (
+ <>
+
+
+ 处理中
+
+ >
+ )}
+
+ {status === "success" && (
+ <>
+
+
+ 授权成功
+
+ >
+ )}
+
+ {status === "error" && (
+ <>
+
+
+ 授权失败
+
+ >
+ )}
+
+ {/* 提示信息 */}
+
+ {message}
+
+
+
+
+
+ );
+}