feat: 导航UI调整

This commit is contained in:
zdl
2025-10-21 15:43:35 +08:00
parent eef383f56f
commit 98653f042b
2 changed files with 405 additions and 55 deletions

View File

@@ -43,7 +43,7 @@ import { logger } from '../../utils/logger';
import { getApiBase } from '../../utils/apiConfig';
import SubscriptionButton from '../Subscription/SubscriptionButton';
import SubscriptionModal from '../Subscription/SubscriptionModal';
import CrownTooltip, { TooltipContent } from '../Subscription/CrownTooltip';
import { CrownIcon, TooltipContent } from '../Subscription/CrownTooltip';
/** 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项 */
const SecondaryNav = ({ showCompletenessAlert }) => {
@@ -186,6 +186,112 @@ const SecondaryNav = ({ showCompletenessAlert }) => {
);
};
/** 中屏"更多"菜单 - 用于平板和小笔记本 */
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 (
<Menu>
<MenuButton
as={Button}
variant="ghost"
rightIcon={<ChevronDownIcon />}
fontWeight="medium"
>
更多
</MenuButton>
<MenuList minW="300px" p={2}>
{/* 高频跟踪组 */}
<Text fontSize="xs" fontWeight="bold" px={3} py={2} color="gray.500">高频跟踪</Text>
<MenuItem
onClick={() => navigate('/community')}
borderRadius="md"
bg={location.pathname.includes('/community') ? 'blue.50' : 'transparent'}
>
<Flex justify="space-between" align="center" w="100%">
<Text fontSize="sm">新闻催化分析</Text>
<HStack spacing={1}>
<Badge size="sm" colorScheme="green">HOT</Badge>
<Badge size="sm" colorScheme="red">NEW</Badge>
</HStack>
</Flex>
</MenuItem>
<MenuItem
onClick={() => navigate('/concepts')}
borderRadius="md"
bg={location.pathname.includes('/concepts') ? 'blue.50' : 'transparent'}
>
<Flex justify="space-between" align="center" w="100%">
<Text fontSize="sm">概念中心</Text>
<Badge size="sm" colorScheme="red">NEW</Badge>
</Flex>
</MenuItem>
<MenuDivider />
{/* 行情复盘组 */}
<Text fontSize="xs" fontWeight="bold" px={3} py={2} color="gray.500">行情复盘</Text>
<MenuItem
onClick={() => navigate('/limit-analyse')}
borderRadius="md"
bg={location.pathname.includes('/limit-analyse') ? 'blue.50' : 'transparent'}
>
<Flex justify="space-between" align="center" w="100%">
<Text fontSize="sm">涨停分析</Text>
<Badge size="sm" colorScheme="blue">FREE</Badge>
</Flex>
</MenuItem>
<MenuItem
onClick={() => navigate('/stocks')}
borderRadius="md"
bg={location.pathname.includes('/stocks') ? 'blue.50' : 'transparent'}
>
<Flex justify="space-between" align="center" w="100%">
<Text fontSize="sm">个股中心</Text>
<Badge size="sm" colorScheme="green">HOT</Badge>
</Flex>
</MenuItem>
<MenuItem
onClick={() => navigate('/trading-simulation')}
borderRadius="md"
bg={location.pathname.includes('/trading-simulation') ? 'blue.50' : 'transparent'}
>
<Flex justify="space-between" align="center" w="100%">
<Text fontSize="sm">模拟盘</Text>
<Badge size="sm" colorScheme="red">NEW</Badge>
</Flex>
</MenuItem>
<MenuDivider />
{/* AGENT社群组 */}
<Text fontSize="xs" fontWeight="bold" px={3} py={2} color="gray.500">AGENT社群</Text>
<MenuItem isDisabled cursor="not-allowed" color="gray.400">
<Text fontSize="sm" color="gray.400">今日热议</Text>
</MenuItem>
<MenuItem isDisabled cursor="not-allowed" color="gray.400">
<Text fontSize="sm" color="gray.400">个股社区</Text>
</MenuItem>
<MenuDivider />
{/* 联系我们 */}
<MenuItem isDisabled cursor="not-allowed" color="gray.400">
<Text fontSize="sm" color="gray.400">联系我们</Text>
</MenuItem>
</MenuList>
</Menu>
);
};
/** 桌面端导航 - 完全按照原网站
* @TODO 添加逻辑 不展示导航case
* 1.未登陆状态 && 是首页
@@ -360,6 +466,8 @@ 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();
@@ -746,15 +854,22 @@ export default function HomeNavbar() {
</Text>
</HStack>
{/* 移动端菜单按钮 */}
{/* 中间导航区域 - 响应式 */}
{isMobile ? (
// 移动端:汉堡菜单
<IconButton
icon={<HamburgerIcon />}
variant="ghost"
onClick={onOpen}
aria-label="Open menu"
/>
) : <NavItems isAuthenticated={isAuthenticated} user={user} />}
) : isTablet ? (
// 中屏(平板):"更多"下拉菜单
<MoreNavMenu isAuthenticated={isAuthenticated} user={user} />
) : (
// 大屏(桌面):完整导航菜单
<NavItems isAuthenticated={isAuthenticated} user={user} />
)}
{/* 右侧:日夜模式切换 + 登录/用户区 */}
<HStack spacing={{ base: 2, md: 4 }}>
@@ -774,8 +889,8 @@ export default function HomeNavbar() {
) : isAuthenticated && user ? (
// 已登录状态 - 用户菜单 + 功能菜单排列
<HStack spacing={{ base: 2, md: 3 }}>
{/* 自选股 - 移动端隐藏 */}
{!isMobile && (
{/* 自选股 - 仅大屏显示 */}
{isDesktop && (
<Menu onOpen={loadWatchlistQuotes}>
<MenuButton
as={Button}
@@ -852,8 +967,8 @@ export default function HomeNavbar() {
</Menu>
)}
{/* 关注的事件 - 头像右侧 - 移动端隐藏 */}
{!isMobile && (
{/* 关注的事件 - 仅大屏显示 */}
{isDesktop && (
<Menu onOpen={loadFollowingEvents}>
<MenuButton
as={Button}
@@ -936,58 +1051,155 @@ export default function HomeNavbar() {
</Menu>
)}
{/* 带会员标识的头像 - 点击打开订阅弹窗 */}
<Box
position="relative"
cursor="pointer"
onClick={() => setIsSubscriptionModalOpen(true)}
>
{/* 会员图标徽章 - 使用独立的 CrownTooltip 组件 */}
<CrownTooltip subscriptionInfo={subscriptionInfo} />
{/* 头像区域 - 响应式 */}
{isDesktop ? (
// 大屏:头像点击打开订阅弹窗
<>
<Tooltip
label={<TooltipContent subscriptionInfo={subscriptionInfo} />}
placement="bottom"
hasArrow
bg={useColorModeValue('white', 'gray.800')}
borderRadius="lg"
border="1px solid"
borderColor={useColorModeValue('gray.200', 'gray.600')}
boxShadow="lg"
p={3}
>
<Box
position="relative"
cursor="pointer"
onClick={() => setIsSubscriptionModalOpen(true)}
>
<CrownIcon subscriptionInfo={subscriptionInfo} />
<Avatar
size="sm"
name={getDisplayName()}
src={user.avatar_url}
bg="blue.500"
border={subscriptionInfo.type !== 'free' ? '2.5px solid' : 'none'}
borderColor={
subscriptionInfo.type === 'max' ? '#667eea' :
subscriptionInfo.type === 'pro' ? '#667eea' : 'transparent'
}
_hover={{
transform: 'scale(1.05)',
boxShadow: subscriptionInfo.type !== 'free'
? '0 4px 12px rgba(102, 126, 234, 0.4)'
: 'md',
}}
transition="all 0.2s"
/>
</Box>
</Tooltip>
{isSubscriptionModalOpen && (
<SubscriptionModal
isOpen={isSubscriptionModalOpen}
onClose={() => setIsSubscriptionModalOpen(false)}
subscriptionInfo={subscriptionInfo}
/>
)}
</>
) : (
// 中屏:头像作为下拉菜单,包含所有功能
<Menu>
<MenuButton>
<Box position="relative">
<CrownIcon subscriptionInfo={subscriptionInfo} />
<Avatar
size="sm"
name={getDisplayName()}
src={user.avatar_url}
bg="blue.500"
border={subscriptionInfo.type !== 'free' ? '2.5px solid' : 'none'}
borderColor={
subscriptionInfo.type === 'max' ? '#667eea' :
subscriptionInfo.type === 'pro' ? '#667eea' : 'transparent'
}
_hover={{ transform: 'scale(1.05)' }}
transition="all 0.2s"
/>
</Box>
</MenuButton>
<MenuList minW="320px">
{/* 用户信息区 */}
<Box px={3} py={2} borderBottom="1px" borderColor={useColorModeValue('gray.200', 'gray.600')}>
<Text fontSize="sm" fontWeight="bold">{getDisplayName()}</Text>
<Text fontSize="xs" color="gray.500">{user.email}</Text>
{user.phone && (
<Text fontSize="xs" color="gray.500">{user.phone}</Text>
)}
{user.has_wechat && (
<Badge size="sm" colorScheme="green" mt={1}>微信已绑定</Badge>
)}
</Box>
{/* 头像 - 带会员等级边框和详细信息 Tooltip */}
<Tooltip
label={<TooltipContent subscriptionInfo={subscriptionInfo} />}
placement="bottom"
hasArrow
bg={useColorModeValue('white', 'gray.800')}
borderRadius="lg"
border="1px solid"
borderColor={useColorModeValue('gray.200', 'gray.600')}
boxShadow="lg"
p={3}
>
<Avatar
size="sm"
name={getDisplayName()}
src={user.avatar_url}
bg="blue.500"
border={subscriptionInfo.type !== 'free' ? '2.5px solid' : 'none'}
borderColor={
subscriptionInfo.type === 'max' ? '#667eea' :
subscriptionInfo.type === 'pro' ? '#667eea' : 'transparent'
}
_hover={{
transform: 'scale(1.05)',
boxShadow: subscriptionInfo.type !== 'free'
? '0 4px 12px rgba(102, 126, 234, 0.4)'
: 'md',
}}
transition="all 0.2s"
/>
</Tooltip>
</Box>
{/* 订阅管理 */}
<MenuItem icon={<FaCrown />} onClick={() => setIsSubscriptionModalOpen(true)}>
<Flex justify="space-between" align="center" w="100%">
<Text>订阅管理</Text>
<Badge colorScheme={subscriptionInfo.type === 'free' ? 'gray' : 'purple'}>
{subscriptionInfo.type === 'max' ? 'MAX' :
subscriptionInfo.type === 'pro' ? 'PRO' : '免费版'}
</Badge>
</Flex>
</MenuItem>
{/* 订阅管理弹窗 - 只在打开时渲染 */}
{isSubscriptionModalOpen && (
<SubscriptionModal
isOpen={isSubscriptionModalOpen}
onClose={() => setIsSubscriptionModalOpen(false)}
subscriptionInfo={subscriptionInfo}
/>
{isSubscriptionModalOpen && (
<SubscriptionModal
isOpen={isSubscriptionModalOpen}
onClose={() => setIsSubscriptionModalOpen(false)}
subscriptionInfo={subscriptionInfo}
/>
)}
<MenuDivider />
{/* 自选股 */}
<MenuItem icon={<FiStar />} onClick={() => navigate('/home/center')}>
<Flex justify="space-between" align="center" w="100%">
<Text>我的自选股</Text>
{watchlistQuotes && watchlistQuotes.length > 0 && (
<Badge>{watchlistQuotes.length}</Badge>
)}
</Flex>
</MenuItem>
{/* 自选事件 */}
<MenuItem icon={<FiCalendar />} onClick={() => navigate('/home/center')}>
<Flex justify="space-between" align="center" w="100%">
<Text>我的自选事件</Text>
{followingEvents && followingEvents.length > 0 && (
<Badge>{followingEvents.length}</Badge>
)}
</Flex>
</MenuItem>
<MenuDivider />
{/* 个人中心 */}
<MenuItem icon={<FiHome />} onClick={() => navigate('/home/center')}>
个人中心
</MenuItem>
<MenuItem icon={<FiUser />} onClick={() => navigate('/home/profile')}>
个人资料
</MenuItem>
<MenuItem icon={<FiSettings />} onClick={() => navigate('/home/settings')}>
账户设置
</MenuItem>
<MenuDivider />
{/* 退出登录 */}
<MenuItem icon={<FiLogOut />} onClick={handleLogout} color="red.500">
退出登录
</MenuItem>
</MenuList>
</Menu>
)}
{/* 个人中心下拉菜单 */}
{/* 个人中心下拉菜单 - 仅大屏显示 */}
{isDesktop && (
<Menu>
<MenuButton
as={Button}
@@ -1033,6 +1245,7 @@ export default function HomeNavbar() {
</MenuItem>
</MenuList>
</Menu>
)}
</HStack>
) : (
// 未登录状态 - 单一按钮