主要改动: 1. 创建乐观更新工具函数 (optimisticUpdate.ts) - 消除 userStore.ts 中的重复代码 2. 拆分 useResponsive.ts (485行 -> 12个专注模块) - useBreakpoint: 断点检测 - useOrientation: 方向检测 - usePlatform: 平台检测 - useScreenSize: 屏幕尺寸 - useResponsiveValue: 响应式值 - useResponsiveStyle: 响应式样式 - useMediaQuery: 媒体查询 - useColumnCount: 列数计算 - useResponsiveSpacing: 响应式间距 3. 整理数据层 (Repository 层) - ApiDataSource: API数据源 - LocalDataSource: 本地数据源 - CacheDataSource: 缓存数据源 - MessageRepository: 消息仓库 4. 重构 messageManager.ts (2194行 -> 4个模块) - MessageStateManager: 状态管理 - WebSocketMessageHandler: WebSocket处理 - MessageSyncService: 消息同步 - ReadReceiptManager: 已读管理 5. 导航解耦 (MainNavigator.tsx: 1118行 -> 100行) - 创建 NavigationService 解耦层 - 拆分多个 Navigator 组件 架构改进: - 单一职责原则: 每个模块职责明确 - 依赖倒置: 通过接口解耦 - 代码复用: 工具函数可被多处使用 - 可测试性: 各模块可独立测试
156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
/**
|
|
* Tab 导航器
|
|
* 处理底部标签导航(移动端)
|
|
*/
|
|
import React from 'react';
|
|
import { View } from 'react-native';
|
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
|
|
import type { MainTabParamList } from './types';
|
|
import { colors, shadows } from '../theme';
|
|
|
|
import { HomeNavigator } from './HomeNavigator';
|
|
import { MessageNavigator } from './MessageNavigator';
|
|
import { ScheduleNavigator } from './ScheduleNavigator';
|
|
import { ProfileNavigator } from './ProfileNavigator';
|
|
|
|
const Tab = createBottomTabNavigator<MainTabParamList>();
|
|
|
|
// 常量
|
|
const MOBILE_TAB_FLOATING_MARGIN = 12;
|
|
|
|
interface TabNavigatorProps {
|
|
unreadCount: number;
|
|
}
|
|
|
|
export function TabNavigator({ unreadCount }: TabNavigatorProps) {
|
|
const insets = useSafeAreaInsets();
|
|
|
|
return (
|
|
<Tab.Navigator
|
|
screenOptions={{
|
|
tabBarActiveTintColor: colors.primary.main,
|
|
tabBarInactiveTintColor: colors.text.secondary,
|
|
tabBarHideOnKeyboard: true,
|
|
tabBarStyle: {
|
|
backgroundColor: colors.background.paper,
|
|
borderTopWidth: 1,
|
|
borderTopColor: `${colors.divider}88`,
|
|
borderRadius: 24,
|
|
marginHorizontal: 14,
|
|
marginBottom: MOBILE_TAB_FLOATING_MARGIN + insets.bottom,
|
|
height: 64,
|
|
paddingBottom: 6,
|
|
paddingTop: 8,
|
|
paddingHorizontal: 8,
|
|
position: 'absolute',
|
|
...shadows.lg,
|
|
},
|
|
tabBarItemStyle: {
|
|
borderRadius: 18,
|
|
paddingVertical: 1,
|
|
marginHorizontal: 2,
|
|
},
|
|
tabBarLabelStyle: {
|
|
fontSize: 12,
|
|
fontWeight: '600',
|
|
marginTop: -2,
|
|
letterSpacing: 0.2,
|
|
},
|
|
tabBarBadgeStyle: {
|
|
backgroundColor: colors.error.main,
|
|
color: colors.primary.contrast,
|
|
fontSize: 10,
|
|
fontWeight: '700',
|
|
top: 4,
|
|
},
|
|
headerShown: false,
|
|
}}
|
|
>
|
|
<Tab.Screen
|
|
name="HomeTab"
|
|
component={HomeNavigator}
|
|
options={{
|
|
tabBarLabel: '首页',
|
|
tabBarIcon: ({ color, size, focused }) => (
|
|
<View style={[styles.tabIconContainer, focused && styles.tabIconActive]}>
|
|
<MaterialCommunityIcons
|
|
name={focused ? 'home' : 'home-outline'}
|
|
size={focused ? 26 : 24}
|
|
color={color}
|
|
/>
|
|
</View>
|
|
),
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="MessageTab"
|
|
component={MessageNavigator}
|
|
options={{
|
|
tabBarLabel: '消息',
|
|
tabBarBadge: unreadCount > 0 ? (unreadCount > 99 ? '99+' : unreadCount) : undefined,
|
|
tabBarIcon: ({ color, size, focused }) => (
|
|
<View style={[styles.tabIconContainer, focused && styles.tabIconActive]}>
|
|
<MaterialCommunityIcons
|
|
name={focused ? 'message-text' : 'message-text-outline'}
|
|
size={focused ? 26 : 24}
|
|
color={color}
|
|
/>
|
|
</View>
|
|
),
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="ScheduleTab"
|
|
component={ScheduleNavigator}
|
|
options={{
|
|
tabBarLabel: '课表',
|
|
tabBarIcon: ({ color, size, focused }) => (
|
|
<View style={[styles.tabIconContainer, focused && styles.tabIconActive]}>
|
|
<MaterialCommunityIcons
|
|
name={focused ? 'calendar-today' : 'calendar-today'}
|
|
size={focused ? 26 : 24}
|
|
color={color}
|
|
/>
|
|
</View>
|
|
),
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="ProfileTab"
|
|
component={ProfileNavigator}
|
|
options={{
|
|
tabBarLabel: '我的',
|
|
tabBarIcon: ({ color, size, focused }) => (
|
|
<View style={[styles.tabIconContainer, focused && styles.tabIconActive]}>
|
|
<MaterialCommunityIcons
|
|
name={focused ? 'account' : 'account-outline'}
|
|
size={focused ? 26 : 24}
|
|
color={color}
|
|
/>
|
|
</View>
|
|
),
|
|
}}
|
|
/>
|
|
</Tab.Navigator>
|
|
);
|
|
}
|
|
|
|
import { StyleSheet } from 'react-native';
|
|
|
|
const styles = StyleSheet.create({
|
|
tabIconContainer: {
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
width: 38,
|
|
height: 30,
|
|
borderRadius: 15,
|
|
},
|
|
tabIconActive: {
|
|
backgroundColor: `${colors.primary.main}20`,
|
|
transform: [{ translateY: -1 }],
|
|
},
|
|
});
|