refactor(HomeNavbar): Phase 3 - 提取用户菜单组件
**背景** 继 Phase 1 (静态组件) 和 Phase 2 (Redux订阅) 后,进一步优化 HomeNavbar **重构内容** 1. **新增组件目录** `src/components/Navbars/components/UserMenu/` - UserAvatar.js (101行) - 头像 + 皇冠图标 + 订阅边框 - DesktopUserMenu.js (93行) - 桌面版 Tooltip + 订阅弹窗 - TabletUserMenu.js (166行) - 平板版下拉菜单 (含所有功能) - index.js - 统一导出 2. **HomeNavbar.js 优化** - 删除 ~150 行用户菜单 JSX 代码 - 移除未使用的 Tooltip 导入 - 替换为 DesktopUserMenu / TabletUserMenu 组件调用 - 1533 → 1394 行 (-139行, -9%) **技术亮点** - React.memo 优化渲染性能 - 复用 Redux subscriptionSlice (Phase 2) - 响应式设计 (isDesktop vs isTablet) - 组件内聚,降低父组件耦合 **累计成果** (Phase 1-3) - 原始: 1623 行 - 当前: 1394 行 - 减少: 229 行 (-14%) - 提取: 7 个组件 (4 静态 + 3 用户菜单) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -31,7 +31,6 @@ import {
|
||||
useColorMode,
|
||||
useColorModeValue,
|
||||
useToast,
|
||||
Tooltip,
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
@@ -48,8 +47,6 @@ import { useAuthModal } from '../../hooks/useAuthModal';
|
||||
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 { useNavigationEvents } from '../../hooks/useNavigationEvents';
|
||||
|
||||
// Phase 1 优化: 提取的子组件
|
||||
@@ -60,6 +57,9 @@ import CalendarButton from './components/CalendarButton';
|
||||
// Phase 2 优化: 使用 Redux 管理订阅数据
|
||||
import { useSubscription } from '../../hooks/useSubscription';
|
||||
|
||||
// Phase 3 优化: 提取的用户菜单组件
|
||||
import { DesktopUserMenu, TabletUserMenu } from './components/UserMenu';
|
||||
|
||||
/** 二级导航栏组件 - 显示当前一级菜单下的所有二级菜单项 */
|
||||
const SecondaryNav = ({ showCompletenessAlert }) => {
|
||||
const navigate = useNavigate();
|
||||
@@ -1069,156 +1069,16 @@ export default function HomeNavbar() {
|
||||
</Menu>
|
||||
)}
|
||||
|
||||
{/* 头像区域 - 响应式 */}
|
||||
{/* 头像区域 - 响应式 (Phase 3 优化) */}
|
||||
{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={openSubscriptionModal}
|
||||
>
|
||||
<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={closeSubscriptionModal}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
<DesktopUserMenu user={user} />
|
||||
) : (
|
||||
// 中屏:头像作为下拉菜单,包含所有功能
|
||||
<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>
|
||||
|
||||
{/* 订阅管理 */}
|
||||
<MenuItem icon={<FaCrown />} onClick={openSubscriptionModal}>
|
||||
<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={closeSubscriptionModal}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
<MenuDivider />
|
||||
|
||||
{/* 投资日历 */}
|
||||
<MenuItem icon={<FiCalendar />} onClick={() => navigate('/community')}>
|
||||
<Text>投资日历</Text>
|
||||
</MenuItem>
|
||||
|
||||
{/* 自选股 */}
|
||||
<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>
|
||||
<TabletUserMenu
|
||||
user={user}
|
||||
handleLogout={handleLogout}
|
||||
watchlistQuotes={watchlistQuotes}
|
||||
followingEvents={followingEvents}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 个人中心下拉菜单 - 仅大屏显示 */}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// src/components/Navbars/components/UserMenu/DesktopUserMenu.js
|
||||
// 桌面版用户菜单 - 头像 + Tooltip + 订阅弹窗
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { Tooltip, useColorModeValue } from '@chakra-ui/react';
|
||||
import UserAvatar from './UserAvatar';
|
||||
import SubscriptionModal from '../../../Subscription/SubscriptionModal';
|
||||
import { useSubscription } from '../../../../hooks/useSubscription';
|
||||
|
||||
/**
|
||||
* Tooltip 内容组件
|
||||
* 显示用户订阅信息和剩余天数
|
||||
*/
|
||||
const TooltipContent = memo(({ subscriptionInfo }) => {
|
||||
const getSubscriptionBadgeText = () => {
|
||||
if (!subscriptionInfo || !subscriptionInfo.type) {
|
||||
return '免费版';
|
||||
}
|
||||
|
||||
const type = subscriptionInfo.type.toLowerCase();
|
||||
|
||||
switch (type) {
|
||||
case 'max':
|
||||
return subscriptionInfo.is_active
|
||||
? `Max版 (剩余 ${subscriptionInfo.days_left || 0} 天)`
|
||||
: 'Max版 (已过期)';
|
||||
case 'pro':
|
||||
return subscriptionInfo.is_active
|
||||
? `Pro版 (剩余 ${subscriptionInfo.days_left || 0} 天)`
|
||||
: 'Pro版 (已过期)';
|
||||
case 'free':
|
||||
default:
|
||||
return '免费版 (点击升级)';
|
||||
}
|
||||
};
|
||||
|
||||
return getSubscriptionBadgeText();
|
||||
});
|
||||
|
||||
TooltipContent.displayName = 'TooltipContent';
|
||||
|
||||
/**
|
||||
* 桌面版用户菜单组件
|
||||
* 大屏幕 (md+) 显示,头像点击打开订阅弹窗
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} props.user - 用户信息
|
||||
*/
|
||||
const DesktopUserMenu = memo(({ user }) => {
|
||||
const {
|
||||
subscriptionInfo,
|
||||
isSubscriptionModalOpen,
|
||||
openSubscriptionModal,
|
||||
closeSubscriptionModal
|
||||
} = useSubscription();
|
||||
|
||||
const tooltipBg = useColorModeValue('white', 'gray.800');
|
||||
const tooltipBorderColor = useColorModeValue('gray.200', 'gray.600');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip
|
||||
label={<TooltipContent subscriptionInfo={subscriptionInfo} />}
|
||||
placement="bottom"
|
||||
hasArrow
|
||||
bg={tooltipBg}
|
||||
borderRadius="lg"
|
||||
border="1px solid"
|
||||
borderColor={tooltipBorderColor}
|
||||
boxShadow="lg"
|
||||
p={3}
|
||||
>
|
||||
<UserAvatar
|
||||
user={user}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
onClick={openSubscriptionModal}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
{isSubscriptionModalOpen && (
|
||||
<SubscriptionModal
|
||||
isOpen={isSubscriptionModalOpen}
|
||||
onClose={closeSubscriptionModal}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
DesktopUserMenu.displayName = 'DesktopUserMenu';
|
||||
|
||||
export default DesktopUserMenu;
|
||||
166
src/components/Navbars/components/UserMenu/TabletUserMenu.js
Normal file
166
src/components/Navbars/components/UserMenu/TabletUserMenu.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// src/components/Navbars/components/UserMenu/TabletUserMenu.js
|
||||
// 平板版用户菜单 - 头像作为下拉菜单,包含所有功能
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Box,
|
||||
Text,
|
||||
Badge,
|
||||
Flex,
|
||||
useColorModeValue
|
||||
} from '@chakra-ui/react';
|
||||
import { FiStar, FiCalendar, FiUser, FiSettings, FiHome, FiLogOut } from 'react-icons/fi';
|
||||
import { FaCrown } from 'react-icons/fa';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import UserAvatar from './UserAvatar';
|
||||
import SubscriptionModal from '../../../Subscription/SubscriptionModal';
|
||||
import { useSubscription } from '../../../../hooks/useSubscription';
|
||||
|
||||
/**
|
||||
* 平板版用户菜单组件
|
||||
* 中屏幕 (sm-md) 显示,头像作为下拉菜单,包含所有功能
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} props.user - 用户信息
|
||||
* @param {Function} props.handleLogout - 退出登录回调
|
||||
* @param {Array} props.watchlistQuotes - 自选股列表
|
||||
* @param {Array} props.followingEvents - 自选事件列表
|
||||
*/
|
||||
const TabletUserMenu = memo(({
|
||||
user,
|
||||
handleLogout,
|
||||
watchlistQuotes,
|
||||
followingEvents
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
subscriptionInfo,
|
||||
isSubscriptionModalOpen,
|
||||
openSubscriptionModal,
|
||||
closeSubscriptionModal
|
||||
} = useSubscription();
|
||||
|
||||
const borderColor = useColorModeValue('gray.200', 'gray.600');
|
||||
|
||||
// 获取显示名称
|
||||
const getDisplayName = () => {
|
||||
if (user.nickname) return user.nickname;
|
||||
if (user.username) return user.username;
|
||||
if (user.email) return user.email.split('@')[0];
|
||||
if (user.phone) return user.phone;
|
||||
return '用户';
|
||||
};
|
||||
|
||||
// 获取订阅标签
|
||||
const getSubscriptionBadge = () => {
|
||||
if (subscriptionInfo.type === 'max') return 'MAX';
|
||||
if (subscriptionInfo.type === 'pro') return 'PRO';
|
||||
return '免费版';
|
||||
};
|
||||
|
||||
// 获取订阅标签颜色
|
||||
const getSubscriptionBadgeColor = () => {
|
||||
return subscriptionInfo.type === 'free' ? 'gray' : 'purple';
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Menu>
|
||||
<MenuButton>
|
||||
<UserAvatar
|
||||
user={user}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
</MenuButton>
|
||||
<MenuList minW="320px">
|
||||
{/* 用户信息区 */}
|
||||
<Box px={3} py={2} borderBottom="1px" borderColor={borderColor}>
|
||||
<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>
|
||||
|
||||
{/* 订阅管理 */}
|
||||
<MenuItem icon={<FaCrown />} onClick={openSubscriptionModal}>
|
||||
<Flex justify="space-between" align="center" w="100%">
|
||||
<Text>订阅管理</Text>
|
||||
<Badge colorScheme={getSubscriptionBadgeColor()}>
|
||||
{getSubscriptionBadge()}
|
||||
</Badge>
|
||||
</Flex>
|
||||
</MenuItem>
|
||||
|
||||
<MenuDivider />
|
||||
|
||||
{/* 投资日历 */}
|
||||
<MenuItem icon={<FiCalendar />} onClick={() => navigate('/community')}>
|
||||
<Text>投资日历</Text>
|
||||
</MenuItem>
|
||||
|
||||
{/* 自选股 */}
|
||||
<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>
|
||||
|
||||
{/* 订阅弹窗 */}
|
||||
{isSubscriptionModalOpen && (
|
||||
<SubscriptionModal
|
||||
isOpen={isSubscriptionModalOpen}
|
||||
onClose={closeSubscriptionModal}
|
||||
subscriptionInfo={subscriptionInfo}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
TabletUserMenu.displayName = 'TabletUserMenu';
|
||||
|
||||
export default TabletUserMenu;
|
||||
101
src/components/Navbars/components/UserMenu/UserAvatar.js
Normal file
101
src/components/Navbars/components/UserMenu/UserAvatar.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// src/components/Navbars/components/UserMenu/UserAvatar.js
|
||||
// 用户头像组件 - 带皇冠图标和订阅边框
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { Box, Avatar } from '@chakra-ui/react';
|
||||
import { FaCrown } from 'react-icons/fa';
|
||||
|
||||
/**
|
||||
* 皇冠图标组件
|
||||
* @param {Object} props.subscriptionInfo - 订阅信息
|
||||
*/
|
||||
const CrownIcon = memo(({ subscriptionInfo }) => {
|
||||
if (!subscriptionInfo || subscriptionInfo.type === 'free') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const crownColor = subscriptionInfo.type === 'max'
|
||||
? 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||||
: '#667eea';
|
||||
|
||||
return (
|
||||
<Box
|
||||
position="absolute"
|
||||
top="-4px"
|
||||
right="-4px"
|
||||
zIndex={2}
|
||||
fontSize="14px"
|
||||
background={crownColor}
|
||||
borderRadius="full"
|
||||
p="3px"
|
||||
boxShadow="0 2px 8px rgba(102, 126, 234, 0.4)"
|
||||
>
|
||||
<FaCrown color="white" />
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
CrownIcon.displayName = 'CrownIcon';
|
||||
|
||||
/**
|
||||
* 用户头像组件
|
||||
* 包含皇冠图标和订阅边框样式
|
||||
*
|
||||
* @param {Object} props
|
||||
* @param {Object} props.user - 用户信息
|
||||
* @param {Object} props.subscriptionInfo - 订阅信息
|
||||
* @param {string} props.size - 头像大小 (默认 'sm')
|
||||
* @param {Function} props.onClick - 点击回调
|
||||
* @param {Object} props.hoverStyle - 悬停样式
|
||||
*/
|
||||
const UserAvatar = memo(({
|
||||
user,
|
||||
subscriptionInfo,
|
||||
size = 'sm',
|
||||
onClick,
|
||||
hoverStyle = {}
|
||||
}) => {
|
||||
// 获取显示名称
|
||||
const getDisplayName = () => {
|
||||
if (user.nickname) return user.nickname;
|
||||
if (user.username) return user.username;
|
||||
if (user.email) return user.email.split('@')[0];
|
||||
if (user.phone) return user.phone;
|
||||
return '用户';
|
||||
};
|
||||
|
||||
// 边框颜色
|
||||
const getBorderColor = () => {
|
||||
if (subscriptionInfo.type === 'max') return '#667eea';
|
||||
if (subscriptionInfo.type === 'pro') return '#667eea';
|
||||
return 'transparent';
|
||||
};
|
||||
|
||||
// 默认悬停样式
|
||||
const defaultHoverStyle = {
|
||||
transform: 'scale(1.05)',
|
||||
boxShadow: subscriptionInfo.type !== 'free'
|
||||
? '0 4px 12px rgba(102, 126, 234, 0.4)'
|
||||
: 'md',
|
||||
};
|
||||
|
||||
return (
|
||||
<Box position="relative" cursor={onClick ? 'pointer' : 'default'} onClick={onClick}>
|
||||
<CrownIcon subscriptionInfo={subscriptionInfo} />
|
||||
<Avatar
|
||||
size={size}
|
||||
name={getDisplayName()}
|
||||
src={user.avatar_url}
|
||||
bg="blue.500"
|
||||
border={subscriptionInfo.type !== 'free' ? '2.5px solid' : 'none'}
|
||||
borderColor={getBorderColor()}
|
||||
_hover={onClick ? { ...defaultHoverStyle, ...hoverStyle } : undefined}
|
||||
transition="all 0.2s"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
});
|
||||
|
||||
UserAvatar.displayName = 'UserAvatar';
|
||||
|
||||
export default UserAvatar;
|
||||
6
src/components/Navbars/components/UserMenu/index.js
Normal file
6
src/components/Navbars/components/UserMenu/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// src/components/Navbars/components/UserMenu/index.js
|
||||
// 用户菜单组件统一导出
|
||||
|
||||
export { default as UserAvatar } from './UserAvatar';
|
||||
export { default as DesktopUserMenu } from './DesktopUserMenu';
|
||||
export { default as TabletUserMenu } from './TabletUserMenu';
|
||||
Reference in New Issue
Block a user