refactor(HomeNavbar): Phase 5 - 提取移动端抽屉菜单组件
**背景**
继 Phase 1-4 后,进一步优化 HomeNavbar 的移动端菜单结构
**重构内容**
1. **新增组件目录** `src/components/Navbars/components/MobileDrawer/`
- MobileDrawer.js (314行) - 移动端完整抽屉菜单
* 用户信息展示
* 日夜模式切换
* 完整导航菜单(高频跟踪、行情复盘、AGENT社群、联系我们)
* 登录/退出登录按钮
- index.js - 统一导出
2. **HomeNavbar.js 优化**
- 删除 ~262 行移动端 Drawer JSX 代码
- 精简 Chakra UI 导入(移除 Drawer、DrawerBody、DrawerHeader 等 12 个组件)
- 替换为 MobileDrawer 组件调用
- 1065 → 815 行 (-250行, -23%)
**技术亮点**
- React.memo 优化渲染性能
- 封装导航点击逻辑(handleNavigate)
- 独立管理主题切换状态
- 响应式颜色模式(useColorModeValue)
- 完整的用户状态判断和 UI 展示
**累计成果** (Phase 1-5)
- 原始: 1623 行
- 当前: 815 行
- 减少: 808 行 (-50%)
- 提取: 11 个组件
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,37 +6,20 @@ import {
|
||||
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,
|
||||
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';
|
||||
@@ -63,6 +46,9 @@ import { DesktopUserMenu, TabletUserMenu } from './components/UserMenu';
|
||||
// Phase 4 优化: 提取的导航菜单组件
|
||||
import { DesktopNav, MoreMenu, PersonalCenterMenu } from './components/Navigation';
|
||||
|
||||
// Phase 5 优化: 提取的移动端抽屉菜单组件
|
||||
import { MobileDrawer } from './components/MobileDrawer';
|
||||
|
||||
/** 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项 */
|
||||
const SecondaryNav = ({ showCompletenessAlert }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -810,251 +796,15 @@ export default function HomeNavbar() {
|
||||
</Flex>
|
||||
</Container>
|
||||
|
||||
{/* 移动端抽屉菜单 */}
|
||||
<Drawer isOpen={isOpen} placement="right" onClose={onClose}>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent>
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader>
|
||||
<HStack>
|
||||
<Text>菜单</Text>
|
||||
{isAuthenticated && user && (
|
||||
<Badge colorScheme="green" ml={2}>已登录</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<VStack spacing={4} align="stretch">
|
||||
{/* 移动端:日夜模式切换 */}
|
||||
<Button
|
||||
leftIcon={colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
|
||||
variant="ghost"
|
||||
justifyContent="flex-start"
|
||||
onClick={toggleColorMode}
|
||||
size="sm"
|
||||
>
|
||||
切换到{colorMode === 'light' ? '深色' : '浅色'}模式
|
||||
</Button>
|
||||
{/* 移动端用户信息 */}
|
||||
{isAuthenticated && user && (
|
||||
<>
|
||||
<Box p={3} bg={useColorModeValue('gray.50', 'whiteAlpha.100')} borderRadius="md">
|
||||
<HStack>
|
||||
<Avatar
|
||||
size="sm"
|
||||
name={getDisplayName()}
|
||||
src={user.avatar_url}
|
||||
bg="blue.500"
|
||||
/>
|
||||
<Box>
|
||||
<Text fontSize="sm" fontWeight="bold">{getDisplayName()}</Text>
|
||||
<Text fontSize="xs" color={useColorModeValue('gray.500', 'gray.300')}>{user.email}</Text>
|
||||
</Box>
|
||||
</HStack>
|
||||
</Box>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 首页链接 */}
|
||||
<Link
|
||||
onClick={() => {
|
||||
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"
|
||||
>
|
||||
<Text fontSize="md">🏠 首页</Text>
|
||||
</Link>
|
||||
<Divider />
|
||||
<Box>
|
||||
<Text fontWeight="bold" mb={2}>高频跟踪</Text>
|
||||
<VStack spacing={2} align="stretch">
|
||||
<Link
|
||||
onClick={() => {
|
||||
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'}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Text fontSize="sm">事件中心</Text>
|
||||
<HStack spacing={1}>
|
||||
<Badge size="xs" colorScheme="green">HOT</Badge>
|
||||
<Badge size="xs" colorScheme="red">NEW</Badge>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Link>
|
||||
<Link
|
||||
onClick={() => {
|
||||
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'}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Text fontSize="sm">概念中心</Text>
|
||||
<Badge size="xs" colorScheme="red">NEW</Badge>
|
||||
</HStack>
|
||||
</Link>
|
||||
</VStack>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box>
|
||||
<Text fontWeight="bold" mb={2}>行情复盘</Text>
|
||||
<VStack spacing={2} align="stretch">
|
||||
<Link
|
||||
onClick={() => {
|
||||
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'}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Text fontSize="sm">涨停分析</Text>
|
||||
<Badge size="xs" colorScheme="blue">FREE</Badge>
|
||||
</HStack>
|
||||
</Link>
|
||||
<Link
|
||||
onClick={() => {
|
||||
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'}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Text fontSize="sm">个股中心</Text>
|
||||
<Badge size="xs" colorScheme="green">HOT</Badge>
|
||||
</HStack>
|
||||
</Link>
|
||||
<Link
|
||||
onClick={() => {
|
||||
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'}
|
||||
>
|
||||
<HStack justify="space-between">
|
||||
<Text fontSize="sm">模拟盘</Text>
|
||||
<Badge size="xs" colorScheme="red">NEW</Badge>
|
||||
</HStack>
|
||||
</Link>
|
||||
</VStack>
|
||||
</Box>
|
||||
|
||||
<Divider />
|
||||
<Box>
|
||||
<Text fontWeight="bold" mb={2}>AGENT社群</Text>
|
||||
<VStack spacing={2} align="stretch">
|
||||
<Link
|
||||
py={1}
|
||||
px={3}
|
||||
borderRadius="md"
|
||||
_hover={{}}
|
||||
cursor="not-allowed"
|
||||
color="gray.400"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<Text fontSize="sm" color="gray.400">今日热议</Text>
|
||||
</Link>
|
||||
<Link
|
||||
py={1}
|
||||
px={3}
|
||||
borderRadius="md"
|
||||
_hover={{}}
|
||||
cursor="not-allowed"
|
||||
color="gray.400"
|
||||
pointerEvents="none"
|
||||
>
|
||||
<Text fontSize="sm" color="gray.400">个股社区</Text>
|
||||
</Link>
|
||||
</VStack>
|
||||
</Box>
|
||||
<Divider />
|
||||
<Box>
|
||||
<Text fontWeight="bold" mb={2}>联系我们</Text>
|
||||
<Text fontSize="sm" color={useColorModeValue('gray.500', 'gray.300')}>敬请期待</Text>
|
||||
</Box>
|
||||
|
||||
{/* 移动端登录/登出按钮 */}
|
||||
<Divider />
|
||||
{isAuthenticated && user ? (
|
||||
<Button
|
||||
colorScheme="red"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
handleLogout();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
🚪 退出登录
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
openAuthModal();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
🔐 登录 / 注册
|
||||
</Button>
|
||||
)}
|
||||
</VStack>
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
{/* 移动端抽屉菜单 (Phase 5 优化) */}
|
||||
<MobileDrawer
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
isAuthenticated={isAuthenticated}
|
||||
user={user}
|
||||
handleLogout={handleLogout}
|
||||
openAuthModal={openAuthModal}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* 二级导航栏 - 显示当前页面所属的二级菜单 */}
|
||||
|
||||
Reference in New Issue
Block a user