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:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user