fix: 修复导航菜单 hover 触发实现方式

修复之前提交(47f84c5)中使用的无效 trigger="hover" 属性。
Chakra UI Menu 组件不支持 trigger 属性,改用正确的实现方式:

**实现方式:**
- 使用 useDisclosure Hook 管理菜单开关状态
- 为 MenuButton 和 MenuList 添加 onMouseEnter/onMouseLeave 事件
- 这样可以确保鼠标从按钮移到菜单列表时保持打开状态

**修改的组件:**
- DesktopNav.js: 为4个菜单添加独立的 useDisclosure Hook
- MoreMenu.js: 平板版"更多"菜单
- PersonalCenterMenu.js: 个人中心菜单

**技术要点:**
- MenuButton 和 MenuList 都需要 hover 事件处理
- 每个菜单使用独立的 useDisclosure 实例
- 符合 Chakra UI 官方推荐的 hover 菜单实现方式

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-10-30 19:06:21 +08:00
parent 85d505cd53
commit 2461ce81c9
3 changed files with 52 additions and 17 deletions

View File

@@ -12,7 +12,8 @@ import {
Text,
Flex,
Badge,
useColorModeValue
useColorModeValue,
useDisclosure
} from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { useNavigate, useLocation } from 'react-router-dom';
@@ -36,6 +37,12 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
// 🎯 初始化导航埋点Hook
const navEvents = useNavigationEvents({ component: 'top_nav' });
// 🎯 为每个菜单创建独立的 useDisclosure Hook
const { isOpen: isHighFreqOpen, onOpen: onHighFreqOpen, onClose: onHighFreqClose } = useDisclosure();
const { isOpen: isMarketReviewOpen, onOpen: onMarketReviewOpen, onClose: onMarketReviewClose } = useDisclosure();
const { isOpen: isAgentCommunityOpen, onOpen: onAgentCommunityOpen, onClose: onAgentCommunityClose } = useDisclosure();
const { isOpen: isContactUsOpen, onOpen: onContactUsOpen, onClose: onContactUsClose } = useDisclosure();
// 辅助函数:判断导航项是否激活
const isActive = useCallback((paths) => {
return paths.some(path => location.pathname.includes(path));
@@ -46,7 +53,7 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
return (
<HStack spacing={8}>
{/* 高频跟踪 */}
<Menu trigger="hover">
<Menu isOpen={isHighFreqOpen} onClose={onHighFreqClose}>
<MenuButton
as={Button}
variant="ghost"
@@ -57,10 +64,12 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
borderBottom={isActive(['/community', '/concepts']) ? '2px solid' : 'none'}
borderColor="blue.600"
_hover={{ bg: isActive(['/community', '/concepts']) ? 'blue.100' : 'gray.50' }}
onMouseEnter={onHighFreqOpen}
onMouseLeave={onHighFreqClose}
>
高频跟踪
</MenuButton>
<MenuList minW="260px" p={2}>
<MenuList minW="260px" p={2} onMouseEnter={onHighFreqOpen} onMouseLeave={onHighFreqClose}>
<MenuItem
onClick={() => {
// 🎯 追踪菜单项点击
@@ -102,7 +111,7 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
</Menu>
{/* 行情复盘 */}
<Menu trigger="hover">
<Menu isOpen={isMarketReviewOpen} onClose={onMarketReviewClose}>
<MenuButton
as={Button}
variant="ghost"
@@ -113,10 +122,12 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
borderBottom={isActive(['/limit-analyse', '/stocks', '/trading-simulation']) ? '2px solid' : 'none'}
borderColor="blue.600"
_hover={{ bg: isActive(['/limit-analyse', '/stocks', '/trading-simulation']) ? 'blue.100' : 'gray.50' }}
onMouseEnter={onMarketReviewOpen}
onMouseLeave={onMarketReviewClose}
>
行情复盘
</MenuButton>
<MenuList minW="260px" p={2}>
<MenuList minW="260px" p={2} onMouseEnter={onMarketReviewOpen} onMouseLeave={onMarketReviewClose}>
<MenuItem
onClick={() => navigate('/limit-analyse')}
borderRadius="md"
@@ -160,11 +171,17 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
</Menu>
{/* AGENT社群 */}
<Menu trigger="hover">
<MenuButton as={Button} variant="ghost" rightIcon={<ChevronDownIcon />}>
<Menu isOpen={isAgentCommunityOpen} onClose={onAgentCommunityClose}>
<MenuButton
as={Button}
variant="ghost"
rightIcon={<ChevronDownIcon />}
onMouseEnter={onAgentCommunityOpen}
onMouseLeave={onAgentCommunityClose}
>
AGENT社群
</MenuButton>
<MenuList minW="300px" p={4}>
<MenuList minW="300px" p={4} onMouseEnter={onAgentCommunityOpen} onMouseLeave={onAgentCommunityClose}>
<MenuItem
isDisabled
cursor="not-allowed"
@@ -183,11 +200,17 @@ const DesktopNav = memo(({ isAuthenticated, user }) => {
</Menu>
{/* 联系我们 */}
<Menu trigger="hover">
<MenuButton as={Button} variant="ghost" rightIcon={<ChevronDownIcon />}>
<Menu isOpen={isContactUsOpen} onClose={onContactUsClose}>
<MenuButton
as={Button}
variant="ghost"
rightIcon={<ChevronDownIcon />}
onMouseEnter={onContactUsOpen}
onMouseLeave={onContactUsClose}
>
联系我们
</MenuButton>
<MenuList minW="260px" p={4}>
<MenuList minW="260px" p={4} onMouseEnter={onContactUsOpen} onMouseLeave={onContactUsClose}>
<Text fontSize="sm" color={contactTextColor}>敬请期待</Text>
</MenuList>
</Menu>

View File

@@ -12,7 +12,8 @@ import {
Text,
Flex,
HStack,
Badge
Badge,
useDisclosure
} from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { useNavigate, useLocation } from 'react-router-dom';
@@ -29,6 +30,9 @@ const MoreMenu = memo(({ isAuthenticated, user }) => {
const navigate = useNavigate();
const location = useLocation();
// 🎯 为"更多"菜单创建 useDisclosure Hook
const { isOpen, onOpen, onClose } = useDisclosure();
// 辅助函数:判断导航项是否激活
const isActive = useCallback((paths) => {
return paths.some(path => location.pathname.includes(path));
@@ -37,16 +41,18 @@ const MoreMenu = memo(({ isAuthenticated, user }) => {
if (!isAuthenticated || !user) return null;
return (
<Menu trigger="hover">
<Menu isOpen={isOpen} onClose={onClose}>
<MenuButton
as={Button}
variant="ghost"
rightIcon={<ChevronDownIcon />}
fontWeight="medium"
onMouseEnter={onOpen}
onMouseLeave={onClose}
>
更多
</MenuButton>
<MenuList minW="300px" p={2}>
<MenuList minW="300px" p={2} onMouseEnter={onOpen} onMouseLeave={onClose}>
{/* 高频跟踪组 */}
<Text fontSize="xs" fontWeight="bold" px={3} py={2} color="gray.500">高频跟踪</Text>
<MenuItem

View File

@@ -12,7 +12,8 @@ import {
Box,
Text,
Badge,
useColorModeValue
useColorModeValue,
useDisclosure
} from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
import { FiHome, FiUser, FiSettings, FiLogOut } from 'react-icons/fi';
@@ -31,6 +32,9 @@ const PersonalCenterMenu = memo(({ user, handleLogout }) => {
const navigate = useNavigate();
const hoverBg = useColorModeValue('gray.100', 'gray.700');
// 🎯 为个人中心菜单创建 useDisclosure Hook
const { isOpen, onOpen, onClose } = useDisclosure();
// 获取显示名称
const getDisplayName = () => {
if (user.nickname) return user.nickname;
@@ -41,17 +45,19 @@ const PersonalCenterMenu = memo(({ user, handleLogout }) => {
};
return (
<Menu trigger="hover">
<Menu isOpen={isOpen} onClose={onClose}>
<MenuButton
as={Button}
size="sm"
variant="ghost"
rightIcon={<ChevronDownIcon />}
_hover={{ bg: hoverBg }}
onMouseEnter={onOpen}
onMouseLeave={onClose}
>
个人中心
</MenuButton>
<MenuList>
<MenuList onMouseEnter={onOpen} onMouseLeave={onClose}>
{/* 用户信息区 */}
<Box px={3} py={2} borderBottom="1px" borderColor="gray.200">
<Text fontSize="sm" fontWeight="bold">{getDisplayName()}</Text>