Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref
This commit is contained in:
@@ -1,13 +1,7 @@
|
||||
// src/components/Auth/AuthModalManager.js
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
useBreakpointValue
|
||||
} from '@chakra-ui/react';
|
||||
import { Modal } from 'antd';
|
||||
import { useBreakpointValue } from '@chakra-ui/react';
|
||||
import { useAuthModal } from '../../hooks/useAuthModal';
|
||||
import AuthFormContent from './AuthFormContent';
|
||||
import { trackEventAsync } from '@lib/posthog';
|
||||
@@ -44,85 +38,43 @@ export default function AuthModalManager() {
|
||||
}
|
||||
}, [isAuthModalOpen]);
|
||||
|
||||
// 响应式尺寸配置
|
||||
const modalSize = useBreakpointValue({
|
||||
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
|
||||
if (!isAuthModalOpen) {
|
||||
return null;
|
||||
}
|
||||
// 响应式宽度配置(Ant Design Modal 使用数字或字符串)
|
||||
const modalMaxW = useBreakpointValue(
|
||||
{
|
||||
base: "90%", // 移动端:屏幕宽度的90%
|
||||
sm: "90%", // 小屏:90%
|
||||
md: "700px", // 中屏:固定700px
|
||||
lg: "700px" // 大屏:固定700px
|
||||
},
|
||||
{ fallback: "700px", ssr: false }
|
||||
);
|
||||
|
||||
// ✅ 使用 Ant Design Modal,完全避开 Chakra UI Portal 的 AnimatePresence 问题
|
||||
// Ant Design Modal 不使用 Framer Motion,不会有 React 18 并发渲染的 insertBefore 错误
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isAuthModalOpen}
|
||||
onClose={closeModal}
|
||||
size={modalSize}
|
||||
isCentered
|
||||
closeOnOverlayClick={false} // 防止误点击背景关闭
|
||||
closeOnEsc={true} // 允许ESC键关闭
|
||||
scrollBehavior="inside" // 内容滚动
|
||||
zIndex={999} // 低于导航栏(1000),不覆盖导航
|
||||
open={isAuthModalOpen}
|
||||
onCancel={closeModal}
|
||||
footer={null}
|
||||
width={modalMaxW}
|
||||
centered
|
||||
destroyOnHidden={true}
|
||||
maskClosable={false}
|
||||
keyboard={true}
|
||||
zIndex={999}
|
||||
styles={{
|
||||
body: {
|
||||
padding: '24px',
|
||||
maxHeight: 'calc(90vh - 120px)',
|
||||
overflowY: 'auto'
|
||||
},
|
||||
mask: {
|
||||
backdropFilter: 'blur(10px)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{/* 半透明背景 + 模糊效果 */}
|
||||
<ModalOverlay
|
||||
bg="blackAlpha.700"
|
||||
backdropFilter="blur(10px)"
|
||||
/>
|
||||
|
||||
{/* 弹窗内容容器 */}
|
||||
<ModalContent
|
||||
bg="white"
|
||||
boxShadow="2xl"
|
||||
borderRadius="2xl"
|
||||
maxW={modalMaxW}
|
||||
mx={modalMx}
|
||||
my={modalMy}
|
||||
position="relative"
|
||||
>
|
||||
{/* 关闭按钮 */}
|
||||
<ModalCloseButton
|
||||
position="absolute"
|
||||
right={4}
|
||||
top={4}
|
||||
zIndex={9999}
|
||||
color="gray.500"
|
||||
bg="transparent"
|
||||
_hover={{ bg: "gray.100" }}
|
||||
borderRadius="full"
|
||||
size="lg"
|
||||
onClick={closeModal}
|
||||
/>
|
||||
|
||||
{/* 弹窗主体内容 */}
|
||||
<ModalBody p={6}>
|
||||
<AuthFormContent />
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
<AuthFormContent />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// src/components/GlobalComponents.js
|
||||
// 集中管理应用的全局组件
|
||||
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useNotification } from '../contexts/NotificationContext';
|
||||
import { logger } from '../utils/logger';
|
||||
@@ -75,6 +75,9 @@ function ConnectionStatusBarWrapper() {
|
||||
export function GlobalComponents() {
|
||||
const location = useLocation();
|
||||
|
||||
// ✅ 缓存 Bytedesk 配置对象,避免每次渲染都创建新引用导致重新加载
|
||||
const bytedeskConfigMemo = useMemo(() => getBytedeskConfig(), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Socket 连接状态条 */}
|
||||
@@ -89,9 +92,9 @@ export function GlobalComponents() {
|
||||
{/* 通知容器 */}
|
||||
<NotificationContainer />
|
||||
|
||||
{/* Bytedesk在线客服 - 根据路径条件性显示 */}
|
||||
{/* Bytedesk在线客服 - 使用缓存的配置对象 */}
|
||||
<BytedeskWidget
|
||||
config={getBytedeskConfig()}
|
||||
config={bytedeskConfigMemo}
|
||||
autoLoad={true}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -167,19 +167,8 @@ export default function HomeNavbar() {
|
||||
<BrandLogo />
|
||||
|
||||
{/* 中间导航区域 - 响应式 (Phase 4 优化) */}
|
||||
{isMobile ? (
|
||||
// 移动端:汉堡菜单
|
||||
<IconButton
|
||||
icon={<HamburgerIcon />}
|
||||
variant="ghost"
|
||||
onClick={onOpen}
|
||||
aria-label="Open menu"
|
||||
/>
|
||||
) : isTablet ? (
|
||||
// 中屏(平板):"更多"下拉菜单
|
||||
<MoreMenu isAuthenticated={isAuthenticated} user={user} />
|
||||
) : (
|
||||
// 大屏(桌面):完整导航菜单
|
||||
{isDesktop && (
|
||||
// 桌面端:完整导航菜单(移动端和平板端的汉堡菜单已移至右侧)
|
||||
<DesktopNav isAuthenticated={isAuthenticated} user={user} />
|
||||
)}
|
||||
|
||||
@@ -189,6 +178,9 @@ export default function HomeNavbar() {
|
||||
isAuthenticated={isAuthenticated}
|
||||
user={user}
|
||||
isDesktop={isDesktop}
|
||||
isTablet={isTablet}
|
||||
isMobile={isMobile}
|
||||
onMenuOpen={onOpen}
|
||||
handleLogout={handleLogout}
|
||||
watchlistQuotes={watchlistQuotes}
|
||||
followingEvents={followingEvents}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// Navbar 右侧功能区组件
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { HStack, Spinner } from '@chakra-ui/react';
|
||||
import { HStack, Spinner, IconButton, Box } from '@chakra-ui/react';
|
||||
import { HamburgerIcon } from '@chakra-ui/icons';
|
||||
// import ThemeToggleButton from '../ThemeToggleButton'; // ❌ 已删除 - 不再支持深色模式切换
|
||||
import LoginButton from '../LoginButton';
|
||||
import CalendarButton from '../CalendarButton';
|
||||
import { WatchlistMenu, FollowingEventsMenu } from '../FeatureMenus';
|
||||
import { DesktopUserMenu, TabletUserMenu } from '../UserMenu';
|
||||
import { PersonalCenterMenu } from '../Navigation';
|
||||
import { PersonalCenterMenu, MoreMenu } from '../Navigation';
|
||||
|
||||
/**
|
||||
* Navbar 右侧功能区组件
|
||||
@@ -18,6 +20,9 @@ import { PersonalCenterMenu } from '../Navigation';
|
||||
* @param {boolean} props.isAuthenticated - 是否已登录
|
||||
* @param {Object} props.user - 用户对象
|
||||
* @param {boolean} props.isDesktop - 是否为桌面端
|
||||
* @param {boolean} props.isTablet - 是否为平板端
|
||||
* @param {boolean} props.isMobile - 是否为移动端
|
||||
* @param {Function} props.onMenuOpen - 打开移动端抽屉菜单的回调
|
||||
* @param {Function} props.handleLogout - 登出回调
|
||||
* @param {Array} props.watchlistQuotes - 自选股数据(用于 TabletUserMenu)
|
||||
* @param {Array} props.followingEvents - 关注事件数据(用于 TabletUserMenu)
|
||||
@@ -27,6 +32,9 @@ const NavbarActions = memo(({
|
||||
isAuthenticated,
|
||||
user,
|
||||
isDesktop,
|
||||
isTablet,
|
||||
isMobile,
|
||||
onMenuOpen,
|
||||
handleLogout,
|
||||
watchlistQuotes,
|
||||
followingEvents
|
||||
@@ -60,13 +68,26 @@ const NavbarActions = memo(({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 个人中心下拉菜单 - 仅大屏显示 */}
|
||||
{isDesktop && (
|
||||
{/* 头像右侧的菜单 - 响应式(互斥逻辑,确保只渲染一个) */}
|
||||
{isDesktop ? (
|
||||
// 桌面端:个人中心下拉菜单
|
||||
<PersonalCenterMenu user={user} handleLogout={handleLogout} />
|
||||
) : isTablet ? (
|
||||
// 平板端:MoreMenu 下拉菜单
|
||||
<MoreMenu isAuthenticated={isAuthenticated} user={user} />
|
||||
) : (
|
||||
// 移动端:汉堡菜单(打开抽屉)
|
||||
<IconButton
|
||||
icon={<HamburgerIcon />}
|
||||
variant="ghost"
|
||||
onClick={onMenuOpen}
|
||||
aria-label="打开菜单"
|
||||
size="md"
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
) : (
|
||||
// 未登录状态 - 单一按钮
|
||||
// 未登录状态 - 仅显示登录按钮
|
||||
<LoginButton />
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
Reference in New Issue
Block a user