feat: 注册和登录兼容h5
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
Heading,
|
Heading,
|
||||||
VStack,
|
VStack,
|
||||||
HStack,
|
HStack,
|
||||||
|
Stack,
|
||||||
useToast,
|
useToast,
|
||||||
Icon,
|
Icon,
|
||||||
FormErrorMessage,
|
FormErrorMessage,
|
||||||
@@ -22,10 +23,14 @@ import {
|
|||||||
AlertDialogOverlay,
|
AlertDialogOverlay,
|
||||||
Text,
|
Text,
|
||||||
Link as ChakraLink,
|
Link as ChakraLink,
|
||||||
|
useBreakpointValue,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { FaLock } from "react-icons/fa";
|
import { FaLock, FaWeixin } from "react-icons/fa";
|
||||||
import { useAuth } from "../../contexts/AuthContext";
|
import { useAuth } from "../../contexts/AuthContext";
|
||||||
import { useAuthModal } from "../../contexts/AuthModalContext";
|
import { useAuthModal } from "../../contexts/AuthModalContext";
|
||||||
|
import { authService } from "../../services/authService";
|
||||||
import AuthHeader from './AuthHeader';
|
import AuthHeader from './AuthHeader';
|
||||||
import VerificationCodeInput from './VerificationCodeInput';
|
import VerificationCodeInput from './VerificationCodeInput';
|
||||||
import WechatRegister from './WechatRegister';
|
import WechatRegister from './WechatRegister';
|
||||||
@@ -37,7 +42,7 @@ const API_BASE_URL = isProduction ? "" : "http://49.232.185.254:5000";
|
|||||||
// 统一配置对象
|
// 统一配置对象
|
||||||
const AUTH_CONFIG = {
|
const AUTH_CONFIG = {
|
||||||
// UI文本
|
// UI文本
|
||||||
title: "欢迎使用价值前沿",
|
title: "价值前沿",
|
||||||
subtitle: "开启您的投资之旅",
|
subtitle: "开启您的投资之旅",
|
||||||
formTitle: "手机号验证",
|
formTitle: "手机号验证",
|
||||||
buttonText: "登录/注册",
|
buttonText: "登录/注册",
|
||||||
@@ -79,6 +84,10 @@ export default function AuthFormContent() {
|
|||||||
const [showNicknamePrompt, setShowNicknamePrompt] = useState(false);
|
const [showNicknamePrompt, setShowNicknamePrompt] = useState(false);
|
||||||
const [currentPhone, setCurrentPhone] = useState("");
|
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({
|
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(() => {
|
useEffect(() => {
|
||||||
isMountedRef.current = true;
|
isMountedRef.current = true;
|
||||||
@@ -311,8 +358,8 @@ export default function AuthFormContent() {
|
|||||||
<>
|
<>
|
||||||
<Box width="100%">
|
<Box width="100%">
|
||||||
<AuthHeader title={config.title} subtitle={config.subtitle} />
|
<AuthHeader title={config.title} subtitle={config.subtitle} />
|
||||||
<HStack spacing={8} align="stretch">
|
<Stack direction={stackDirection} spacing={stackSpacing} align="stretch">
|
||||||
<Box flex="4">
|
<Box flex={{ base: "1", md: "4" }}>
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<VStack spacing={4}>
|
<VStack spacing={4}>
|
||||||
<Heading size="md" color="gray.700" alignSelf="flex-start">{config.formTitle}</Heading>
|
<Heading size="md" color="gray.700" alignSelf="flex-start">{config.formTitle}</Heading>
|
||||||
@@ -320,12 +367,43 @@ export default function AuthFormContent() {
|
|||||||
<Input name="phone" value={formData.phone} onChange={handleInputChange} placeholder="请输入11位手机号" />
|
<Input name="phone" value={formData.phone} onChange={handleInputChange} placeholder="请输入11位手机号" />
|
||||||
<FormErrorMessage>{errors.phone}</FormErrorMessage>
|
<FormErrorMessage>{errors.phone}</FormErrorMessage>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<VerificationCodeInput value={formData.verificationCode} onChange={handleInputChange} onSendCode={sendVerificationCode} countdown={countdown} isLoading={isLoading} isSending={sendingCode} error={errors.verificationCode} colorScheme="green" />
|
|
||||||
|
{/* 验证码输入框 + 移动端微信图标 */}
|
||||||
|
<Box width="100%" position="relative">
|
||||||
|
<VerificationCodeInput value={formData.verificationCode} onChange={handleInputChange} onSendCode={sendVerificationCode} countdown={countdown} isLoading={isLoading} isSending={sendingCode} error={errors.verificationCode} colorScheme="green" />
|
||||||
|
|
||||||
|
{/* 移动端:验证码下方的微信登录图标 */}
|
||||||
|
{isMobile && (
|
||||||
|
<HStack spacing={0} mt={2} alignItems="center">
|
||||||
|
<Text fontSize="xs" color="gray.500">其他登录方式:</Text>
|
||||||
|
<IconButton
|
||||||
|
aria-label="微信登录"
|
||||||
|
icon={<Icon as={FaWeixin} w={4} h={4} />}
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
color="#07C160"
|
||||||
|
borderRadius="md"
|
||||||
|
minW="24px"
|
||||||
|
minH="24px"
|
||||||
|
_hover={{
|
||||||
|
bg: "green.50",
|
||||||
|
color: "#06AD56"
|
||||||
|
}}
|
||||||
|
_active={{
|
||||||
|
bg: "green.100"
|
||||||
|
}}
|
||||||
|
onClick={handleWechatH5Login}
|
||||||
|
isDisabled={isLoading}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Button type="submit" width="100%" size="lg" colorScheme="green" color="white" borderRadius="lg" isLoading={isLoading} loadingText={config.loadingText} fontWeight="bold"><Icon as={FaLock} mr={2} />{config.buttonText}</Button>
|
<Button type="submit" width="100%" size="lg" colorScheme="green" color="white" borderRadius="lg" isLoading={isLoading} loadingText={config.loadingText} fontWeight="bold"><Icon as={FaLock} mr={2} />{config.buttonText}</Button>
|
||||||
|
|
||||||
{/* 隐私声明 */}
|
{/* 隐私声明 */}
|
||||||
<Text fontSize="xs" color="gray.500" textAlign="center" mt={2}>
|
<Text fontSize="xs" color="gray.500" textAlign="center" mt={2}>
|
||||||
登录即表示你同意价值前沿{" "}
|
登录即表示您同意价值前沿{" "}
|
||||||
<ChakraLink
|
<ChakraLink
|
||||||
as="a"
|
as="a"
|
||||||
href="/home/user-agreement"
|
href="/home/user-agreement"
|
||||||
@@ -353,10 +431,16 @@ export default function AuthFormContent() {
|
|||||||
</VStack>
|
</VStack>
|
||||||
</form>
|
</form>
|
||||||
</Box>
|
</Box>
|
||||||
<Box flex="1">
|
|
||||||
<Center width="100%" bg="gray.50" borderRadius="lg" p={8}><WechatRegister /></Center>
|
{/* 桌面端:右侧二维码扫描 */}
|
||||||
</Box>
|
{!isMobile && (
|
||||||
</HStack>
|
<Box flex="1">
|
||||||
|
<Center width="100%" bg="gray.50" borderRadius="lg" p={8}>
|
||||||
|
<WechatRegister />
|
||||||
|
</Center>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* 只在需要时才渲染 AlertDialog,避免创建不必要的 Portal */}
|
{/* 只在需要时才渲染 AlertDialog,避免创建不必要的 Portal */}
|
||||||
|
|||||||
@@ -23,10 +23,30 @@ export default function AuthModalManager() {
|
|||||||
|
|
||||||
// 响应式尺寸配置
|
// 响应式尺寸配置
|
||||||
const modalSize = useBreakpointValue({
|
const modalSize = useBreakpointValue({
|
||||||
base: "full", // 移动端:全屏
|
base: "md", // 移动端:md(不占满全屏)
|
||||||
sm: "xl", // 小屏:xl
|
sm: "md", // 小屏:md
|
||||||
md: "2xl", // 中屏:2xl
|
md: "lg", // 中屏:lg
|
||||||
lg: "4xl" // 大屏:4xl
|
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
|
// 条件渲染:只在打开时才渲染 Modal,避免创建不必要的 Portal
|
||||||
@@ -56,8 +76,9 @@ export default function AuthModalManager() {
|
|||||||
bg="white"
|
bg="white"
|
||||||
boxShadow="2xl"
|
boxShadow="2xl"
|
||||||
borderRadius="2xl"
|
borderRadius="2xl"
|
||||||
maxW={modalSize === "full" ? "100%" : "900px"}
|
maxW={modalMaxW}
|
||||||
my={modalSize === "full" ? 0 : 8}
|
mx={modalMx}
|
||||||
|
my={modalMy}
|
||||||
position="relative"
|
position="relative"
|
||||||
>
|
>
|
||||||
{/* 关闭按钮 */}
|
{/* 关闭按钮 */}
|
||||||
@@ -75,7 +96,7 @@ export default function AuthModalManager() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 弹窗主体内容 */}
|
{/* 弹窗主体内容 */}
|
||||||
<ModalBody p={8}>
|
<ModalBody p={6}>
|
||||||
<AuthFormContent />
|
<AuthFormContent />
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import SettingsPage from "views/Settings/SettingsPage";
|
|||||||
import CenterDashboard from "views/Dashboard/Center";
|
import CenterDashboard from "views/Dashboard/Center";
|
||||||
import Subscription from "views/Pages/Account/Subscription";
|
import Subscription from "views/Pages/Account/Subscription";
|
||||||
|
|
||||||
// 懒加载隐私政策和用户协议页面
|
// 懒加载隐私政策、用户协议和微信回调页面
|
||||||
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
||||||
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
||||||
|
const WechatCallback = React.lazy(() => import("views/Pages/WechatCallback"));
|
||||||
|
|
||||||
// 导入保护路由组件
|
// 导入保护路由组件
|
||||||
import ProtectedRoute from "../components/ProtectedRoute";
|
import ProtectedRoute from "../components/ProtectedRoute";
|
||||||
@@ -76,6 +77,9 @@ export default function Home() {
|
|||||||
{/* 用户协议页面 - 无需登录 */}
|
{/* 用户协议页面 - 无需登录 */}
|
||||||
<Route path="/user-agreement" element={<UserAgreement />} />
|
<Route path="/user-agreement" element={<UserAgreement />} />
|
||||||
|
|
||||||
|
{/* 微信授权回调页面 - 无需登录 */}
|
||||||
|
<Route path="/wechat-callback" element={<WechatCallback />} />
|
||||||
|
|
||||||
{/* 其他可能的路由 */}
|
{/* 其他可能的路由 */}
|
||||||
<Route path="*" element={<HomePage />} />
|
<Route path="*" element={<HomePage />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ const StockOverview = React.lazy(() => import("views/StockOverview"));
|
|||||||
const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
|
const TradingSimulation = React.lazy(() => import("views/TradingSimulation"));
|
||||||
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
const PrivacyPolicy = React.lazy(() => import("views/Pages/PrivacyPolicy"));
|
||||||
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
const UserAgreement = React.lazy(() => import("views/Pages/UserAgreement"));
|
||||||
|
const WechatCallback = React.lazy(() => import("views/Pages/WechatCallback"));
|
||||||
const dashRoutes = [
|
const dashRoutes = [
|
||||||
{
|
{
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
@@ -229,6 +230,14 @@ const dashRoutes = [
|
|||||||
layout: "/home",
|
layout: "/home",
|
||||||
invisible: true, // 不在侧边栏显示
|
invisible: true, // 不在侧边栏显示
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "微信授权回调",
|
||||||
|
path: "/wechat-callback",
|
||||||
|
icon: <DocumentIcon color="inherit" />,
|
||||||
|
component: WechatCallback,
|
||||||
|
layout: "/home",
|
||||||
|
invisible: true, // 不在侧边栏显示
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "PAGES",
|
name: "PAGES",
|
||||||
category: "pages",
|
category: "pages",
|
||||||
|
|||||||
@@ -63,13 +63,38 @@ const apiRequest = async (url, options = {}) => {
|
|||||||
|
|
||||||
export const authService = {
|
export const authService = {
|
||||||
/**
|
/**
|
||||||
* 获取微信二维码授权链接
|
* 获取微信二维码授权链接(PC扫码登录)
|
||||||
* @returns {Promise<{auth_url: string, session_id: string}>}
|
* @returns {Promise<{auth_url: string, session_id: string}>}
|
||||||
*/
|
*/
|
||||||
getWechatQRCode: async () => {
|
getWechatQRCode: async () => {
|
||||||
return await apiRequest('/api/auth/wechat/qrcode');
|
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
|
* @param {string} sessionId - 会话ID
|
||||||
|
|||||||
144
src/views/Pages/WechatCallback.js
Normal file
144
src/views/Pages/WechatCallback.js
Normal file
@@ -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 (
|
||||||
|
<Box minH="100vh" bg={bgColor} py={12}>
|
||||||
|
<Container maxW="container.sm">
|
||||||
|
<Box
|
||||||
|
bg={boxBg}
|
||||||
|
p={8}
|
||||||
|
borderRadius="2xl"
|
||||||
|
boxShadow="xl"
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
<VStack spacing={6}>
|
||||||
|
{/* 状态图标 */}
|
||||||
|
{status === "loading" && (
|
||||||
|
<>
|
||||||
|
<Spinner size="xl" color="green.500" thickness="4px" />
|
||||||
|
<Heading size="md" color="gray.700">
|
||||||
|
处理中
|
||||||
|
</Heading>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === "success" && (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
as={FaCheckCircle}
|
||||||
|
w={16}
|
||||||
|
h={16}
|
||||||
|
color="green.500"
|
||||||
|
/>
|
||||||
|
<Heading size="md" color="green.600">
|
||||||
|
授权成功
|
||||||
|
</Heading>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status === "error" && (
|
||||||
|
<>
|
||||||
|
<Icon
|
||||||
|
as={FaTimesCircle}
|
||||||
|
w={16}
|
||||||
|
h={16}
|
||||||
|
color="red.500"
|
||||||
|
/>
|
||||||
|
<Heading size="md" color="red.600">
|
||||||
|
授权失败
|
||||||
|
</Heading>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 提示信息 */}
|
||||||
|
<Text fontSize="md" color="gray.600">
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
</VStack>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user