import React, { useCallback, useState } from 'react'; import { Box, Flex, Text, Button, Container, useDisclosure, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, VStack, HStack, Icon, Menu, MenuButton, MenuList, MenuItem, MenuDivider, Badge, Grid, IconButton, useBreakpointValue, Link, Divider, Avatar, Spinner, useColorMode, useColorModeValue, useToast, Tooltip, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, } from '@chakra-ui/react'; import { ChevronDownIcon, HamburgerIcon, SunIcon, MoonIcon } from '@chakra-ui/icons'; import { FiStar, FiCalendar, FiUser, FiSettings, FiHome, FiLogOut } from 'react-icons/fi'; import { FaCrown } from 'react-icons/fa'; import { useNavigate, useLocation } from 'react-router-dom'; import { useAuth } from '../../contexts/AuthContext'; import { useAuthModal } from '../../contexts/AuthModalContext'; import { logger } from '../../utils/logger'; import { getApiBase } from '../../utils/apiConfig'; import SubscriptionButton from '../Subscription/SubscriptionButton'; import SubscriptionModal from '../Subscription/SubscriptionModal'; import { CrownIcon, TooltipContent } from '../Subscription/CrownTooltip'; import InvestmentCalendar from '../../views/Community/components/InvestmentCalendar'; /** 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项 */ const SecondaryNav = ({ showCompletenessAlert }) => { const navigate = useNavigate(); const location = useLocation(); const navbarBg = useColorModeValue('gray.50', 'gray.700'); const itemHoverBg = useColorModeValue('white', 'gray.600'); // ⚠️ 必须在组件顶层调用所有Hooks(不能在JSX中调用) const borderColorValue = useColorModeValue('gray.200', 'gray.600'); // 定义二级导航结构 const secondaryNavConfig = { '/community': { title: '高频跟踪', items: [ { path: '/community', label: '事件中心', badges: [{ text: 'HOT', colorScheme: 'green' }, { text: 'NEW', colorScheme: 'red' }] }, { path: '/concepts', label: '概念中心', badges: [{ text: 'NEW', colorScheme: 'red' }] } ] }, '/concepts': { title: '高频跟踪', items: [ { path: '/community', label: '事件中心', badges: [{ text: 'HOT', colorScheme: 'green' }, { text: 'NEW', colorScheme: 'red' }] }, { path: '/concepts', label: '概念中心', badges: [{ text: 'NEW', colorScheme: 'red' }] } ] }, '/limit-analyse': { title: '行情复盘', items: [ { path: '/limit-analyse', label: '涨停分析', badges: [{ text: 'FREE', colorScheme: 'blue' }] }, { path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] }, { path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] } ] }, '/stocks': { title: '行情复盘', items: [ { path: '/limit-analyse', label: '涨停分析', badges: [{ text: 'FREE', colorScheme: 'blue' }] }, { path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] }, { path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] } ] }, '/trading-simulation': { title: '行情复盘', items: [ { path: '/limit-analyse', label: '涨停分析', badges: [{ text: 'FREE', colorScheme: 'blue' }] }, { path: '/stocks', label: '个股中心', badges: [{ text: 'HOT', colorScheme: 'green' }] }, { path: '/trading-simulation', label: '模拟盘', badges: [{ text: 'NEW', colorScheme: 'red' }] } ] } }; // 找到当前路径对应的二级导航配置 const currentConfig = Object.keys(secondaryNavConfig).find(key => location.pathname.includes(key) ); // 如果没有匹配的二级导航,不显示 if (!currentConfig) return null; const config = secondaryNavConfig[currentConfig]; return ( {/* 显示一级菜单标题 */} {config.title}: {/* 二级菜单项 */} {config.items.map((item, index) => { const isActive = location.pathname.includes(item.path); return item.external ? ( ) : ( ); })} ); }; /** 中屏"更多"菜单 - 用于平板和小笔记本 */ const MoreNavMenu = ({ isAuthenticated, user }) => { const navigate = useNavigate(); const location = useLocation(); // 辅助函数:判断导航项是否激活 const isActive = useCallback((paths) => { return paths.some(path => location.pathname.includes(path)); }, [location.pathname]); if (!isAuthenticated || !user) return null; return ( } fontWeight="medium" > 更多 {/* 高频跟踪组 */} 高频跟踪 navigate('/community')} borderRadius="md" bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'} > 事件中心 HOT NEW navigate('/concepts')} borderRadius="md" bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'} > 概念中心 NEW {/* 行情复盘组 */} 行情复盘 navigate('/limit-analyse')} borderRadius="md" bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'} > 涨停分析 FREE navigate('/stocks')} borderRadius="md" bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'} > 个股中心 HOT navigate('/trading-simulation')} borderRadius="md" bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'} > 模拟盘 NEW {/* AGENT社群组 */} AGENT社群 今日热议 个股社区 {/* 联系我们 */} 联系我们 ); }; /** 桌面端导航 - 完全按照原网站 * @TODO 添加逻辑 不展示导航case * 1.未登陆状态 && 是首页 * 2. !isMobile */ const NavItems = ({ isAuthenticated, user }) => { const navigate = useNavigate(); const location = useLocation(); // ⚠️ 必须在组件顶层调用所有Hooks(不能在JSX中调用) const contactTextColor = useColorModeValue('gray.500', 'gray.300'); // 辅助函数:判断导航项是否激活 const isActive = useCallback((paths) => { return paths.some(path => location.pathname.includes(path)); }, [location.pathname]); if (isAuthenticated && user) { return ( } bg={isActive(['/community', '/concepts']) ? 'blue.50' : 'transparent'} color={isActive(['/community', '/concepts']) ? 'blue.600' : 'inherit'} fontWeight={isActive(['/community', '/concepts']) ? 'bold' : 'normal'} borderBottom={isActive(['/community', '/concepts']) ? '2px solid' : 'none'} borderColor="blue.600" _hover={{ bg: isActive(['/community', '/concepts']) ? 'blue.100' : 'gray.50' }} > 高频跟踪 navigate('/community')} borderRadius="md" bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/community') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/community') ? 'bold' : 'normal'} > 事件中心 HOT NEW navigate('/concepts')} borderRadius="md" bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/concepts') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/concepts') ? 'bold' : 'normal'} > 概念中心 NEW } bg={isActive(['/limit-analyse', '/stocks']) ? 'blue.50' : 'transparent'} color={isActive(['/limit-analyse', '/stocks']) ? 'blue.600' : 'inherit'} fontWeight={isActive(['/limit-analyse', '/stocks']) ? 'bold' : 'normal'} borderBottom={isActive(['/limit-analyse', '/stocks']) ? '2px solid' : 'none'} borderColor="blue.600" _hover={{ bg: isActive(['/limit-analyse', '/stocks']) ? 'blue.100' : 'gray.50' }} > 行情复盘 navigate('/limit-analyse')} borderRadius="md" bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/limit-analyse') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/limit-analyse') ? 'bold' : 'normal'} > 涨停分析 FREE navigate('/stocks')} borderRadius="md" bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/stocks') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/stocks') ? 'bold' : 'normal'} > 个股中心 HOT navigate('/trading-simulation')} borderRadius="md" bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/trading-simulation') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/trading-simulation') ? 'bold' : 'normal'} > 模拟盘 NEW {false && isAuthenticated && ( }>自选股 )} }> AGENT社群 今日热议 个股社区 {false && isAuthenticated && ( }>关注的事件 )} }> 联系我们 敬请期待 ) } else { return null; } }; export default function HomeNavbar() { const { isOpen, onOpen, onClose } = useDisclosure(); const navigate = useNavigate(); const isMobile = useBreakpointValue({ base: true, md: false }); const isTablet = useBreakpointValue({ base: false, md: true, lg: false }); const isDesktop = useBreakpointValue({ base: false, md: false, lg: true }); const { user, isAuthenticated, logout, isLoading } = useAuth(); const { openAuthModal } = useAuthModal(); const { colorMode, toggleColorMode } = useColorMode(); const navbarBg = useColorModeValue('white', 'gray.800'); const navbarBorder = useColorModeValue('gray.200', 'gray.700'); const brandText = useColorModeValue('gray.800', 'white'); const brandHover = useColorModeValue('blue.600', 'blue.300'); const toast = useToast(); // ⚡ 提取 userId 为独立变量,避免 user 对象引用变化导致无限循环 const userId = user?.id; const prevUserIdRef = React.useRef(userId); const prevIsAuthenticatedRef = React.useRef(isAuthenticated); // 添加调试信息 logger.debug('HomeNavbar', '组件渲染状态', { hasUser: !!user, isAuthenticated, isLoading, userId: user?.id }); // 获取显示名称的函数 const getDisplayName = () => { if (!user) return ''; return user.nickname || user.username || user.name || user.email || '用户'; }; // 处理登出 const handleLogout = async () => { try { await logout(); // 重置资料完整性检查标志 hasCheckedCompleteness.current = false; setProfileCompleteness(null); setShowCompletenessAlert(false); // logout函数已经包含了跳转逻辑,这里不需要额外处理 } catch (error) { logger.error('HomeNavbar', 'handleLogout', error, { userId: user?.id }); } }; // 检查是否为禁用的链接(没有NEW标签的链接) // const isDisabledLink = true; // 自选股 / 关注事件 下拉所需状态 const [watchlistQuotes, setWatchlistQuotes] = useState([]); const [watchlistLoading, setWatchlistLoading] = useState(false); const [followingEvents, setFollowingEvents] = useState([]); const [eventsLoading, setEventsLoading] = useState(false); const [watchlistPage, setWatchlistPage] = useState(1); const [eventsPage, setEventsPage] = useState(1); const WATCHLIST_PAGE_SIZE = 10; const EVENTS_PAGE_SIZE = 8; // 投资日历 Modal 状态 const [calendarModalOpen, setCalendarModalOpen] = useState(false); // 用户信息完整性状态 const [profileCompleteness, setProfileCompleteness] = useState(null); const [showCompletenessAlert, setShowCompletenessAlert] = useState(false); // 添加标志位:追踪是否已经检查过资料完整性(避免重复请求) const hasCheckedCompleteness = React.useRef(false); // 订阅信息状态 const [subscriptionInfo, setSubscriptionInfo] = React.useState({ type: 'free', status: 'active', days_left: 0, is_active: true }); const [isSubscriptionModalOpen, setIsSubscriptionModalOpen] = React.useState(false); const loadWatchlistQuotes = useCallback(async () => { try { setWatchlistLoading(true); const base = getApiBase(); // 使用外部函数 const resp = await fetch(base + '/api/account/watchlist/realtime', { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); if (resp.ok) { const data = await resp.json(); if (data && data.success && Array.isArray(data.data)) { setWatchlistQuotes(data.data); } else { setWatchlistQuotes([]); } } else { setWatchlistQuotes([]); } } catch (e) { logger.warn('HomeNavbar', '加载自选股实时行情失败', { error: e.message }); setWatchlistQuotes([]); } finally { setWatchlistLoading(false); } }, []); // getApiBase 是外部函数,不需要作为依赖 const loadFollowingEvents = useCallback(async () => { try { setEventsLoading(true); const base = getApiBase(); const resp = await fetch(base + '/api/account/events/following', { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }); if (resp.ok) { const data = await resp.json(); if (data && data.success && Array.isArray(data.data)) { const ids = data.data.map((e) => e.id).filter(Boolean); if (ids.length === 0) { setFollowingEvents([]); } else { // 并行请求详情以获取涨幅字段 const detailResponses = await Promise.all(ids.map((id) => fetch(base + `/api/events/${id}`, { credentials: 'include', cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } }))); const detailJsons = await Promise.all(detailResponses.map((r) => r.ok ? r.json() : Promise.resolve({ success: false }))); const details = detailJsons .filter((j) => j && j.success && j.data) .map((j) => j.data); // 以原顺序合并,缺失则回退基础信息 const merged = ids.map((id) => { const d = details.find((x) => x.id === id); const baseItem = (data.data || []).find((x) => x.id === id) || {}; return d ? d : baseItem; }); setFollowingEvents(merged); } } else { setFollowingEvents([]); } } else { setFollowingEvents([]); } } catch (e) { logger.warn('HomeNavbar', '加载关注事件失败', { error: e.message }); setFollowingEvents([]); } finally { setEventsLoading(false); } }, []); // getApiBase 是外部函数,不需要作为依赖 // 从自选股移除 const handleRemoveFromWatchlist = useCallback(async (stockCode) => { try { const base = getApiBase(); const resp = await fetch(base + `/api/account/watchlist/${stockCode}`, { method: 'DELETE', credentials: 'include' }); const data = await resp.json().catch(() => ({})); if (resp.ok && data && data.success !== false) { setWatchlistQuotes((prev) => { const normalize6 = (code) => { const m = String(code || '').match(/(\d{6})/); return m ? m[1] : String(code || ''); }; const target = normalize6(stockCode); const updated = (prev || []).filter((x) => normalize6(x.stock_code) !== target); const newMaxPage = Math.max(1, Math.ceil((updated.length || 0) / WATCHLIST_PAGE_SIZE)); setWatchlistPage((p) => Math.min(p, newMaxPage)); return updated; }); toast({ title: '已从自选股移除', status: 'info', duration: 1500 }); } else { toast({ title: '移除失败', status: 'error', duration: 2000 }); } } catch (e) { toast({ title: '网络错误,移除失败', status: 'error', duration: 2000 }); } }, [toast]); // WATCHLIST_PAGE_SIZE 是常量,getApiBase 是外部函数,不需要作为依赖 // 取消关注事件 const handleUnfollowEvent = useCallback(async (eventId) => { try { const base = getApiBase(); const resp = await fetch(base + `/api/events/${eventId}/follow`, { method: 'POST', credentials: 'include' }); const data = await resp.json().catch(() => ({})); if (resp.ok && data && data.success !== false) { setFollowingEvents((prev) => { const updated = (prev || []).filter((x) => x.id !== eventId); const newMaxPage = Math.max(1, Math.ceil((updated.length || 0) / EVENTS_PAGE_SIZE)); setEventsPage((p) => Math.min(p, newMaxPage)); return updated; }); toast({ title: '已取消关注该事件', status: 'info', duration: 1500 }); } else { toast({ title: '操作失败', status: 'error', duration: 2000 }); } } catch (e) { toast({ title: '网络错误,操作失败', status: 'error', duration: 2000 }); } }, [toast]); // EVENTS_PAGE_SIZE 是常量,getApiBase 是外部函数,不需要作为依赖 // 检查用户资料完整性 const checkProfileCompleteness = useCallback(async () => { if (!isAuthenticated || !user) return; // 如果已经检查过,跳过(避免重复请求) if (hasCheckedCompleteness.current) { logger.debug('HomeNavbar', '已检查过资料完整性,跳过重复请求', { userId: user?.id }); return; } try { logger.debug('HomeNavbar', '开始检查资料完整性', { userId: user?.id }); const base = getApiBase(); const resp = await fetch(base + '/api/account/profile-completeness', { credentials: 'include' }); if (resp.ok) { const data = await resp.json(); if (data.success) { setProfileCompleteness(data.data); // 只有微信用户且资料不完整时才显示提醒 setShowCompletenessAlert(data.data.needsAttention); // 标记为已检查 hasCheckedCompleteness.current = true; logger.debug('HomeNavbar', '资料完整性检查完成', { userId: user?.id, completeness: data.data.completenessPercentage }); } } } catch (error) { logger.warn('HomeNavbar', '检查资料完整性失败', { userId: user?.id, error: error.message }); } }, [isAuthenticated, userId]); // ⚡ 使用 userId 而不是 user?.id // 监听用户变化,重置检查标志(用户切换或退出登录时) React.useEffect(() => { const userIdChanged = prevUserIdRef.current !== userId; const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated; if (userIdChanged || authChanged) { prevUserIdRef.current = userId; prevIsAuthenticatedRef.current = isAuthenticated; if (!isAuthenticated || !user) { // 用户退出登录,重置标志 hasCheckedCompleteness.current = false; setProfileCompleteness(null); setShowCompletenessAlert(false); } } }, [isAuthenticated, userId, user]); // ⚡ 使用 userId // 用户登录后检查资料完整性 React.useEffect(() => { const userIdChanged = prevUserIdRef.current !== userId; const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated; if ((userIdChanged || authChanged) && isAuthenticated && user) { // 延迟检查,避免过于频繁 const timer = setTimeout(checkProfileCompleteness, 1000); return () => clearTimeout(timer); } }, [isAuthenticated, userId, checkProfileCompleteness, user]); // ⚡ 使用 userId // 加载订阅信息 React.useEffect(() => { const userIdChanged = prevUserIdRef.current !== userId; const authChanged = prevIsAuthenticatedRef.current !== isAuthenticated; if (userIdChanged || authChanged) { if (isAuthenticated && user) { const loadSubscriptionInfo = async () => { try { const base = getApiBase(); const response = await fetch(base + '/api/subscription/current', { credentials: 'include', }); if (response.ok) { const data = await response.json(); if (data.success && data.data) { // 数据标准化处理:确保type字段是小写的 'free', 'pro', 或 'max' const normalizedData = { type: (data.data.type || data.data.subscription_type || 'free').toLowerCase(), status: data.data.status || 'active', days_left: data.data.days_left || 0, is_active: data.data.is_active !== false, end_date: data.data.end_date || null }; setSubscriptionInfo(normalizedData); } } } catch (error) { logger.error('HomeNavbar', '加载订阅信息失败', error); } }; loadSubscriptionInfo(); } else { // 用户未登录时,重置为免费版 setSubscriptionInfo({ type: 'free', status: 'active', days_left: 0, is_active: true }); } } }, [isAuthenticated, userId, user]); // ⚡ 使用 userId,防重复通过 ref 判断 return ( <> {/* 资料完整性提醒横幅 */} {showCompletenessAlert && profileCompleteness && ( 完善资料,享受更好服务 您还需要设置:{profileCompleteness.missingItems.join('、')} {profileCompleteness.completenessPercentage}% 完成 ×} onClick={() => setShowCompletenessAlert(false)} aria-label="关闭提醒" minW={{ base: '32px', md: '40px' }} /> )} {/* Logo - 价小前投研 */} navigate('/home')} style={{ minWidth: isMobile ? '100px' : '140px' }} noOfLines={1} > 价小前投研 {/* 中间导航区域 - 响应式 */} {isMobile ? ( // 移动端:汉堡菜单 } variant="ghost" onClick={onOpen} aria-label="Open menu" /> ) : isTablet ? ( // 中屏(平板):"更多"下拉菜单 ) : ( // 大屏(桌面):完整导航菜单 )} {/* 右侧:日夜模式切换 + 登录/用户区 */} : } onClick={toggleColorMode} variant="ghost" size="sm" minW={{ base: '36px', md: '40px' }} minH={{ base: '36px', md: '40px' }} /> {/* 显示加载状态 */} {isLoading ? ( ) : isAuthenticated && user ? ( // 已登录状态 - 用户菜单 + 功能菜单排列 {/* 投资日历 - 仅大屏显示 */} {isDesktop && ( )} {/* 自选股 - 仅大屏显示 */} {isDesktop && ( } leftIcon={} > 自选股 {watchlistQuotes && watchlistQuotes.length > 0 && ( {watchlistQuotes.length} )} 我的自选股 {watchlistLoading ? ( 加载中... ) : ( <> {(!watchlistQuotes || watchlistQuotes.length === 0) ? ( 暂无自选股 ) : ( {watchlistQuotes .slice((watchlistPage - 1) * WATCHLIST_PAGE_SIZE, watchlistPage * WATCHLIST_PAGE_SIZE) .map((item) => ( navigate(`/company?scode=${item.stock_code}`)}> {item.stock_name || item.stock_code} {item.stock_code} 0 ? 'red' : ((item.change_percent || 0) < 0 ? 'green' : 'gray')} fontSize="xs" > {(item.change_percent || 0) > 0 ? '+' : ''}{(item.change_percent || 0).toFixed(2)}% {item.current_price?.toFixed ? item.current_price.toFixed(2) : (item.current_price || '-')} ))} )} {watchlistPage} / {Math.max(1, Math.ceil((watchlistQuotes?.length || 0) / WATCHLIST_PAGE_SIZE))} )} )} {/* 关注的事件 - 仅大屏显示 */} {isDesktop && ( } leftIcon={} > 自选事件 {followingEvents && followingEvents.length > 0 && ( {followingEvents.length} )} 我关注的事件 {eventsLoading ? ( 加载中... ) : ( <> {(!followingEvents || followingEvents.length === 0) ? ( 暂未关注任何事件 ) : ( {followingEvents .slice((eventsPage - 1) * EVENTS_PAGE_SIZE, eventsPage * EVENTS_PAGE_SIZE) .map((ev) => ( navigate(`/event-detail/${ev.id}`)}> {ev.title} {ev.event_type && ( {ev.event_type} )} {ev.start_time && ( {new Date(ev.start_time).toLocaleString('zh-CN')} )} {typeof ev.related_avg_chg === 'number' && ( 0 ? 'red' : (ev.related_avg_chg < 0 ? 'green' : 'gray')} fontSize="xs">日均 {ev.related_avg_chg > 0 ? '+' : ''}{ev.related_avg_chg.toFixed(2)}% )} {typeof ev.related_week_chg === 'number' && ( 0 ? 'red' : (ev.related_week_chg < 0 ? 'green' : 'gray')} fontSize="xs">周涨 {ev.related_week_chg > 0 ? '+' : ''}{ev.related_week_chg.toFixed(2)}% )} ))} )} {eventsPage} / {Math.max(1, Math.ceil((followingEvents?.length || 0) / EVENTS_PAGE_SIZE))} )} )} {/* 头像区域 - 响应式 */} {isDesktop ? ( // 大屏:头像点击打开订阅弹窗 <> } placement="bottom" hasArrow bg={useColorModeValue('white', 'gray.800')} borderRadius="lg" border="1px solid" borderColor={useColorModeValue('gray.200', 'gray.600')} boxShadow="lg" p={3} > setIsSubscriptionModalOpen(true)} > {isSubscriptionModalOpen && ( setIsSubscriptionModalOpen(false)} subscriptionInfo={subscriptionInfo} /> )} ) : ( // 中屏:头像作为下拉菜单,包含所有功能 {/* 用户信息区 */} {getDisplayName()} {user.email} {user.phone && ( {user.phone} )} {user.has_wechat && ( 微信已绑定 )} {/* 订阅管理 */} } onClick={() => setIsSubscriptionModalOpen(true)}> 订阅管理 {subscriptionInfo.type === 'max' ? 'MAX' : subscriptionInfo.type === 'pro' ? 'PRO' : '免费版'} {isSubscriptionModalOpen && ( setIsSubscriptionModalOpen(false)} subscriptionInfo={subscriptionInfo} /> )} {/* 投资日历 */} } onClick={() => navigate('/community')}> 投资日历 {/* 自选股 */} } onClick={() => navigate('/home/center')}> 我的自选股 {watchlistQuotes && watchlistQuotes.length > 0 && ( {watchlistQuotes.length} )} {/* 自选事件 */} } onClick={() => navigate('/home/center')}> 我的自选事件 {followingEvents && followingEvents.length > 0 && ( {followingEvents.length} )} {/* 个人中心 */} } onClick={() => navigate('/home/center')}> 个人中心 } onClick={() => navigate('/home/profile')}> 个人资料 } onClick={() => navigate('/home/settings')}> 账户设置 {/* 退出登录 */} } onClick={handleLogout} color="red.500"> 退出登录 )} {/* 个人中心下拉菜单 - 仅大屏显示 */} {isDesktop && ( } _hover={{ bg: useColorModeValue('gray.100', 'gray.700') }} > 个人中心 {getDisplayName()} {user.email} {user.phone && ( {user.phone} )} {user.has_wechat && ( 微信已绑定 )} {/* 前往个人中心 */} } onClick={() => navigate('/home/center')}> 前往个人中心 {/* 账户管理组 */} } onClick={() => navigate('/home/profile')}> 个人资料 } onClick={() => navigate('/home/settings')}> 账户设置 {/* 功能入口组 */} } onClick={() => navigate('/home/pages/account/subscription')}> 订阅管理 {/* 退出 */} } onClick={handleLogout} color="red.500"> 退出登录 )} ) : ( // 未登录状态 - 单一按钮 )} {/* 移动端抽屉菜单 */} 菜单 {isAuthenticated && user && ( 已登录 )} {/* 移动端:日夜模式切换 */} {/* 移动端用户信息 */} {isAuthenticated && user && ( <> {getDisplayName()} {user.email} )} {/* 首页链接 */} { navigate('/home'); onClose(); }} py={2} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" color="blue.500" fontWeight="bold" bg={location.pathname === '/home' ? 'blue.50' : 'transparent'} borderLeft={location.pathname === '/home' ? '3px solid' : 'none'} borderColor="blue.600" > 🏠 首页 高频跟踪 { navigate('/community'); onClose(); }} py={1} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/community') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/community') ? 'bold' : 'normal'} > 事件中心 HOT NEW { navigate('/concepts'); onClose(); }} py={1} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/concepts') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/concepts') ? 'bold' : 'normal'} > 概念中心 NEW 行情复盘 { navigate('/limit-analyse'); onClose(); }} py={1} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/limit-analyse') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/limit-analyse') ? 'bold' : 'normal'} > 涨停分析 FREE { navigate('/stocks'); onClose(); }} py={1} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/stocks') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/stocks') ? 'bold' : 'normal'} > 个股中心 HOT { navigate('/trading-simulation'); onClose(); }} py={1} px={3} borderRadius="md" _hover={{ bg: 'gray.100' }} cursor="pointer" bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'} borderLeft={location.pathname.includes('/trading-simulation') ? '3px solid' : 'none'} borderColor="blue.600" fontWeight={location.pathname.includes('/trading-simulation') ? 'bold' : 'normal'} > 模拟盘 NEW AGENT社群 今日热议 个股社区 联系我们 敬请期待 {/* 移动端登录/登出按钮 */} {isAuthenticated && user ? ( ) : ( )} {/* 二级导航栏 - 显示当前页面所属的二级菜单 */} {!isMobile && } {/* 投资日历 Modal */} setCalendarModalOpen(false)} size="6xl" > 投资日历 ); }