Browse Source

feat: 首页更新

master
echo 1 month ago
parent
commit
9ef9233797
  1. 2
      app/(tabs)/_layout.tsx
  2. 3
      app/(tabs)/demo.tsx
  3. 2
      app/(tabs)/index.tsx
  4. 6
      app/theme-test.tsx
  5. BIN
      assets/images/game/menu/blockThird.png
  6. BIN
      assets/images/game/menu/chess.png
  7. BIN
      assets/images/game/menu/clock-solid.png
  8. BIN
      assets/images/game/menu/clock-solid_dark.png
  9. BIN
      assets/images/game/menu/electronic.png
  10. BIN
      assets/images/game/menu/fishing.png
  11. BIN
      assets/images/game/menu/home-star-solid.png
  12. BIN
      assets/images/game/menu/live.png
  13. BIN
      assets/images/game/menu/lottery.png
  14. BIN
      assets/images/game/menu/recommend.png
  15. BIN
      assets/images/game/menu/sports.png
  16. BIN
      assets/images/game/menu/trial.png
  17. 2
      components/EditScreenInfo.tsx
  18. 165
      components/Header/index.tsx
  19. 6
      components/ThemeDemo.tsx
  20. 2
      components/Themed.tsx
  21. 3
      hooks/index.ts
  22. 72
      hooks/useGameMenus.ts
  23. 48
      hooks/useTheme.ts
  24. 1
      package.json
  25. 180
      pages/HomeScreen/HomeScreenComplete.tsx
  26. 14
      pages/HomeScreen/components/BannerSwiper/index.tsx
  27. 2
      pages/HomeScreen/components/BottomTabs.tsx
  28. 8
      pages/HomeScreen/components/FastFootNav.tsx
  29. 165
      pages/HomeScreen/components/GameMainMenus/index.tsx
  30. 41
      pages/HomeScreen/components/GameMainMenus/styles.ts
  31. 9
      pages/HomeScreen/components/Header.tsx
  32. 8
      pages/HomeScreen/components/HighPrizeGame.tsx
  33. 15
      pages/HomeScreen/components/Lobby.tsx
  34. 8
      pages/HomeScreen/components/NoticeBar.tsx
  35. 4
      pages/HomeScreen/components/index.ts
  36. 166
      pages/HomeScreen/index.tsx
  37. 14
      pnpm-lock.yaml
  38. 2
      services/mockHomeService.ts
  39. 9
      stores/gameStore.ts
  40. 2
      stores/index.ts
  41. 18
      stores/settingsStore.ts
  42. 20
      stores/userStore.ts
  43. 9
      theme/Colors/index.ts
  44. 7
      theme/index.ts
  45. 6
      theme/styles.ts
  46. 2
      theme/utils.ts

2
app/(tabs)/_layout.tsx

@ -3,7 +3,7 @@ import FontAwesome from '@expo/vector-icons/FontAwesome';
import { Link, Tabs } from 'expo-router';
import { Pressable } from 'react-native';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
import { useColorScheme, useClientOnlyValue } from '@/hooks';
// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/

3
app/(tabs)/demo.tsx

@ -40,7 +40,6 @@ import {
useTheme,
useLanguage,
useHapticsEnabled,
useSettingsActions,
} from '@/stores';
import { useTenantLoad, useTenantInfo } from '@/stores/tenantStore';
@ -72,7 +71,7 @@ export default function DemoScreen() {
const theme = useTheme();
const language = useLanguage();
const hapticsEnabled = useHapticsEnabled();
const { setTheme, setLanguage, setHapticsEnabled } = useSettingsActions();
const { setTheme, setLanguage, setHapticsEnabled } = useSettingsStore();
// const setTheme = useSettingsStore((state) => state.setTheme);
// const setLanguage = useSettingsStore((state) => state.setLanguage);
// const setHapticsEnabled = useSettingsStore((state) => state.setHapticsEnabled);

2
app/(tabs)/index.tsx

@ -8,7 +8,7 @@
import { Stack } from 'expo-router';
import HomeScreen from '@/pages/HomeScreen';
export default function TabOneScreen() {
export default function TabHoneScreen() {
return (
<>
<Stack.Screen

6
app/theme-test.tsx

@ -1,13 +1,13 @@
import { StyleSheet, ScrollView, TouchableOpacity, View, Text, useColorScheme as useSystemColorScheme } from 'react-native';
import { Stack } from 'expo-router';
import { useState, useEffect, useMemo } from 'react';
import { useTheme, useSettingsActions } from '@/stores';
import { useTheme, useSettingsStore } from '@/stores';
import { useHaptics } from '@/hooks';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
export default function ThemeTestScreen() {
const currentTheme = useTheme();
const { setTheme } = useSettingsActions();
const { setTheme } = useSettingsStore();
const haptics = useHaptics();
const systemColorScheme = useSystemColorScheme();

BIN
assets/images/game/menu/blockThird.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
assets/images/game/menu/chess.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets/images/game/menu/clock-solid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
assets/images/game/menu/clock-solid_dark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
assets/images/game/menu/electronic.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

BIN
assets/images/game/menu/fishing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
assets/images/game/menu/home-star-solid.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
assets/images/game/menu/live.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
assets/images/game/menu/lottery.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/images/game/menu/recommend.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
assets/images/game/menu/sports.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
assets/images/game/menu/trial.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

2
components/EditScreenInfo.tsx

@ -5,7 +5,7 @@ import { ExternalLink } from './ExternalLink';
import { MonoText } from './StyledText';
import { Text, View } from './Themed';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
export default function EditScreenInfo({ path }: { path: string }) {
return (

165
components/Header/index.tsx

@ -0,0 +1,165 @@
/**
* Header
*
*/
import React, { useState, useCallback } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TextInput,
Image,
Dimensions,
} from 'react-native';
import { createThemeStyles, useColorScheme, useThemeInfo } from '@/theme';
const { width } = Dimensions.get('window');
/**
*
*/
const styles = createThemeStyles((colors) => ({
container: {
backgroundColor: colors.background,
paddingHorizontal: 12,
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 8,
},
logo: {
fontSize: 18,
fontWeight: '600',
color: colors.primary,
},
searchContainer: {
flex: 1,
marginHorizontal: 12,
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.card,
borderRadius: 20,
paddingHorizontal: 12,
height: 36,
},
searchInput: {
flex: 1,
marginLeft: 8,
fontSize: 14,
color: colors.text,
},
searchPlaceholder: {
color: colors.text + '80',
},
iconButton: {
width: 36,
height: 36,
borderRadius: 18,
justifyContent: 'center',
alignItems: 'center',
marginLeft: 8,
},
badge: {
position: 'absolute',
top: -4,
right: -4,
backgroundColor: colors.primary,
borderRadius: 8,
minWidth: 16,
height: 16,
justifyContent: 'center',
alignItems: 'center',
},
badgeText: {
color: '#fff',
fontSize: 10,
fontWeight: '600',
textAlign: 'center',
},
}));
interface HeaderProps {
onSearch?: (keyword: string) => void;
onMessagePress?: () => void;
onUserPress?: () => void;
unreadCount?: number;
}
/**
* Header
*/
export default function Header({
onSearch,
onMessagePress,
onUserPress,
unreadCount = 0,
}: HeaderProps) {
const theme = useColorScheme();
const s = styles[theme];
const { colors } = useThemeInfo();
const [searchText, setSearchText] = useState('');
const [isSearching, setIsSearching] = useState(false);
const handleSearch = useCallback(() => {
if (searchText.trim()) {
onSearch?.(searchText);
}
}, [searchText, onSearch]);
const handleClearSearch = useCallback(() => {
setSearchText('');
}, []);
return (
<View style={s.container}>
{/* 顶部栏 */}
<View style={s.header}>
{/* Logo */}
<Text style={s.logo}>🎮 </Text>
{/* 搜索框 */}
<View style={s.searchContainer}>
<Text style={{ color: colors.text + '60', fontSize: 16 }}>🔍</Text>
<TextInput
style={[s.searchInput, s.searchPlaceholder]}
placeholder="搜索游戏..."
placeholderTextColor={colors.text + '60'}
value={searchText}
onChangeText={setSearchText}
onSubmitEditing={handleSearch}
returnKeyType="search"
/>
{searchText ? (
<TouchableOpacity onPress={handleClearSearch}>
<Text style={{ fontSize: 16 }}></Text>
</TouchableOpacity>
) : null}
</View>
{/* 消息按钮 */}
<TouchableOpacity style={s.iconButton} onPress={onMessagePress} activeOpacity={0.7}>
<Text style={{ fontSize: 18 }}>💬</Text>
{unreadCount > 0 && (
<View style={s.badge}>
<Text style={s.badgeText}>{unreadCount > 99 ? '99+' : unreadCount}</Text>
</View>
)}
</TouchableOpacity>
{/* 用户按钮 */}
<TouchableOpacity style={s.iconButton} onPress={onUserPress} activeOpacity={0.7}>
<Text style={{ fontSize: 18 }}>👤</Text>
</TouchableOpacity>
</View>
</View>
);
}

6
components/ThemeDemo.tsx

@ -1,18 +1,18 @@
/**
*
*
*
*
*/
import React from 'react';
import { StyleSheet, View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { ThemedText, ThemedView, useThemeColor } from './Themed';
import { useTheme, useSettingsActions } from '@/stores';
import { useTheme, useSettingsStore } from '@/stores';
import { useHaptics } from '@/hooks';
export function ThemeDemo() {
const theme = useTheme();
const { setTheme } = useSettingsActions();
const { setTheme } = useSettingsStore();
const haptics = useHaptics();
// 获取主题颜色

2
components/Themed.tsx

@ -7,7 +7,7 @@
import { Text as DefaultText, View as DefaultView, TextStyle } from 'react-native';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
import { useColorScheme } from '@/hooks/useTheme';
type ThemeProps = {

3
hooks/index.ts

@ -24,3 +24,6 @@ export {
// Client-only value (for SSR/Web compatibility)
export { useClientOnlyValue } from './useClientOnlyValue';
// Game Menus
export { useGameMainMenus, useMenuDataLoaded, useSelectedCategory } from './useGameMenus';

72
hooks/useGameMenus.ts

@ -1,10 +1,20 @@
import { useEffect, useState, useMemo } from 'react';
import { useEffect, useState, useMemo, useCallback } from 'react';
import useGameStore from '@/stores/gameStore';
import { GameMainTypesEnum, defaultHomeGameTabMenus, gameMainTypesMap } from '@/constants/game';
import { ThemeEnum } from '@/constants/theme';
import { forEach, cloneDeep, map, filter } from 'lodash-es';
import { useIsLoggedIn } from '@/stores/userStore';
import { useShallow } from 'zustand/react/shallow';
import storageManager, { STORAGE_KEYS } from '@/utils/storageManager';
type GameMenu = {
name: string;
key: string;
icon?: string;
logo?: string;
children?: GameMenu[];
};
// 有子菜单的游戏类型
const hasSubGameMainTypes = [
@ -78,10 +88,68 @@ export const useGameMainMenus = (theme: ThemeEnum) => {
],
(item) => ({
...item,
// 为了在 React Native 中正确加载本地图片,使用 require 的方式
// 这里保留原始的 icon 名称,在组件中使用 require 加载
icon: item.icon,
key: `${item.key}`,
})
);
) as GameMenu[];
}, [theme, isLogin, menuSort, gameBigClass]);
};
export const useMenuDataLoaded = () => useGameStore((state) => state.menuSort?.length > 0);
/**
* Hook
*
*
* - gameStore
* - session storage
* -
*
* @returns {Object} selectedCategory setSelectedCategory
*
* @example
* ```typescript
* const { selectedCategory, setSelectedCategory } = useSelectedCategory();
*
* // 获取当前选中分类
* console.log(selectedCategory); // '103'
*
* // 更新选中分类
* setSelectedCategory('1');
* ```
*/
export const useSelectedCategory = () => {
const selectedCategory = useGameStore((state) => state.selectedCategory);
const setSelectedCategoryInStore = useGameStore((state) => state.setSelectedCategory);
// 初始化时从 session storage 恢复选中分类
useEffect(() => {
const initializeSelectedCategory = async () => {
try {
const savedCategory = storageManager.session.getItem(STORAGE_KEYS.APP_ACTIVE_MAIN_MENU_TAB);
if (savedCategory) {
setSelectedCategoryInStore(savedCategory);
}
} catch (error) {
console.error('Failed to restore selected category:', error);
}
};
initializeSelectedCategory();
}, [setSelectedCategoryInStore]);
// 更新选中分类的回调函数
const setSelectedCategory = useCallback(
(categoryId: string) => {
setSelectedCategoryInStore(categoryId);
},
[setSelectedCategoryInStore]
);
return {
selectedCategory,
setSelectedCategory,
};
};

48
hooks/useTheme.ts

@ -1,49 +1,50 @@
/**
* Hooks
*
*
* 访
*/
import { useMemo } from 'react';
import { useColorScheme as useSystemColorScheme } from 'react-native';
import { useTheme as useThemeStore } from '@/stores';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
import { ThemeEnum } from '@/constants/theme';
/**
* light | dark
*
* light | dark | orange
*
* settingsStore
* 'light' | 'dark' | 'auto'
* 'light' | 'dark' | 'orange' | 'auto'
*/
export function useColorScheme(): 'light' | 'dark' {
export function useColorScheme(): ThemeEnum {
const userTheme = useThemeStore();
const systemTheme = useSystemColorScheme();
// 如果用户选择了 'auto',则使用系统主题
if (userTheme === 'auto') {
return systemTheme === 'dark' ? 'dark' : 'light';
return systemTheme === 'dark' ? ThemeEnum.DARK : ThemeEnum.LIGHT;
}
// 否则使用用户选择的主题
return userTheme;
return userTheme as ThemeEnum;
}
/**
*
*
* @param props - { light?: string; dark?: string }
*
* @param props - { light?: string; dark?: string; orange?: string }
* @param colorName - Colors
* @returns
*
*
* @example
* ```tsx
* const textColor = useThemeColor({}, 'text');
* const customColor = useThemeColor({ light: '#000', dark: '#fff' }, 'text');
* const customColor = useThemeColor({ light: '#000', dark: '#fff', orange: '#f90' }, 'text');
* ```
*/
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
props: { light?: string; dark?: string; orange?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark & keyof typeof Colors.orange
): string {
const theme = useColorScheme();
const colorFromProps = props[theme];
@ -57,9 +58,9 @@ export function useThemeColor(
/**
*
*
*
* @returns
*
*
* @example
* ```tsx
* const colors = useThemeColors();
@ -70,7 +71,7 @@ export function useThemeColor(
*/
export function useThemeColors() {
const theme = useColorScheme();
return useMemo(() => {
return Colors[theme];
}, [theme]);
@ -78,23 +79,24 @@ export function useThemeColors() {
/**
*
*
*
* @returns
*
*
* @example
* ```tsx
* const { theme, colors, isDark } = useThemeInfo();
* const { theme, colors, isDark, isLight, isOrange } = useThemeInfo();
* ```
*/
export function useThemeInfo() {
const theme = useColorScheme();
const colors = useThemeColors();
return useMemo(() => ({
theme,
colors,
isDark: theme === 'dark',
isLight: theme === 'light',
isDark: theme === ThemeEnum.DARK,
isLight: theme === ThemeEnum.LIGHT,
isOrange: theme === ThemeEnum.ORANGE,
}), [theme, colors]);
}

1
package.json

@ -37,6 +37,7 @@
"react-dom": "19.1.0",
"react-hook-form": "^7.66.0",
"react-native": "0.81.5",
"react-native-linear-gradient": "^2.8.3",
"react-native-paper": "^5.14.5",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",

180
pages/HomeScreen/HomeScreenComplete.tsx

@ -1,180 +0,0 @@
/**
*
* HeaderBottomTabs
*
*/
import React, { useState, useEffect, useCallback } from 'react';
import { View, ScrollView, RefreshControl, StyleSheet, SafeAreaView, Alert } from 'react-native';
import { useColorScheme } from '@/hooks';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import Header from './components/Header';
import BannerSwiper from './components/BannerSwiper';
import NoticeBar from './components/NoticeBar';
import GameMainMenus from './components/GameMainMenus';
import Lobby from './components/Lobby';
import HighPrizeGame from './components/HighPrizeGame';
import FastFootNav from './components/FastFootNav';
import { requestHomePageData } from '@/stores/gameStore';
import { useTenantLoad } from '@/stores/tenantStore';
import type {
Banner,
Notice,
GameCategory,
Game,
HighPrizeGame as HighPrizeGameType,
} from '@/types/home';
/**
*
*/
const styles = createThemeStyles((colors) => ({
container: {
flex: 1,
backgroundColor: colors.background,
},
contentContainer: {
flex: 1,
},
scrollContent: {
paddingBottom: 20,
},
}));
interface HomeScreenCompleteProps {
theme?: 'light' | 'dark';
isDarkTheme?: boolean;
}
/**
*
*/
export default function HomeScreenComplete({
theme = 'light',
isDarkTheme = false,
}: HomeScreenCompleteProps) {
const colorScheme = useColorScheme();
const actualTheme = theme === 'light' || theme === 'dark' ? theme : colorScheme;
const s = styles[actualTheme];
const [refreshing, setRefreshing] = useState(false);
const [selectedCategory, setSelectedCategory] = useState<number>(0);
const tenantLoad = useTenantLoad();
// 加载首页数据
const loadHomePageData = useCallback(async () => {
try {
await requestHomePageData();
} catch (error) {
console.error('加载首页数据失败:', error);
}
}, []);
// 初始化加载
useEffect(() => {
console.log('租户数据加载完成:', tenantLoad);
if (tenantLoad) {
loadHomePageData();
}
}, [loadHomePageData, tenantLoad]);
// 下拉刷新
const handleRefresh = useCallback(async () => {
setRefreshing(true);
try {
await loadHomePageData();
} finally {
setRefreshing(false);
}
}, [loadHomePageData]);
// 处理分类选择
const handleCategorySelect = useCallback((categoryId: number) => {
setSelectedCategory(categoryId);
// 这里可以根据分类过滤游戏
}, []);
// 处理游戏点击
const handleGamePress = useCallback((game: Game) => {
Alert.alert('游戏', `点击了: ${game.play_up_name}`);
// 这里可以添加打开游戏的逻辑
}, []);
// 处理底部 Tab 点击
const handleTabPress = useCallback((tabId: string, action: string) => {
Alert.alert('导航', `点击了: ${tabId}`);
// 这里可以添加导航逻辑
}, []);
// 处理搜索
const handleSearch = useCallback((keyword: string) => {
Alert.alert('搜索', `搜索关键词: ${keyword}`);
// 这里可以添加搜索逻辑
}, []);
// 根据主题选择要显示的组件
const renderContent = () => {
if (isDarkTheme || actualTheme === 'dark') {
// 深色主题布局
return (
<>
<GameMainMenus
theme={actualTheme}
selectedCategory={selectedCategory}
onCategorySelect={handleCategorySelect}
/>
<BannerSwiper theme={actualTheme} />
<NoticeBar theme={actualTheme} />
<HighPrizeGame theme={actualTheme} onGamePress={handleGamePress} />
<Lobby theme={actualTheme} onGamePress={handleGamePress} />
<FastFootNav theme={actualTheme} onTabPress={handleTabPress} />
</>
);
} else {
// 浅色主题布局
return (
<>
<BannerSwiper theme={actualTheme} />
<NoticeBar theme={actualTheme} />
<GameMainMenus
theme={actualTheme}
selectedCategory={selectedCategory}
onCategorySelect={handleCategorySelect}
/>
<Lobby theme={actualTheme} onGamePress={handleGamePress} />
</>
);
}
};
return (
<SafeAreaView style={s.container}>
{/* Header */}
<Header
theme={actualTheme}
onSearch={handleSearch}
onMessagePress={() => Alert.alert('消息', '消息功能')}
onUserPress={() => Alert.alert('用户', '用户中心')}
unreadCount={3}
/>
{/* 内容区域 */}
<View style={s.contentContainer}>
<ScrollView
style={s.contentContainer}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
tintColor={Colors[actualTheme].primary}
/>
}
showsVerticalScrollIndicator={false}
>
<View style={s.scrollContent}>{renderContent()}</View>
</ScrollView>
</View>
</SafeAreaView>
);
}

14
pages/HomeScreen/components/BannerSwiper/index.tsx

@ -2,7 +2,6 @@
*
*
*
* 使
*/
import React, { useState, useEffect, useRef, useCallback } from 'react';
@ -17,23 +16,22 @@ import {
Dimensions,
Alert,
} from 'react-native';
import Colors from '@/constants/Colors';
import { useColorScheme } from '@/hooks';
// import type { Banner } from '@/types/home';
import { styles } from './styles';
import useMsgStore from '@/stores/msgStore';
interface BannerSwiperProps {
theme: 'light' | 'dark';
}
interface BannerSwiperProps {}
const { width } = Dimensions.get('window');
/**
*
*/
export default function BannerSwiper({ theme }: BannerSwiperProps) {
const s = styles[theme];
export default function BannerSwiper({}: BannerSwiperProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const [currentIndex, setCurrentIndex] = useState(0);
const [loading, setLoading] = useState(true);
const scrollViewRef = useRef<ScrollView>(null);
@ -102,7 +100,7 @@ export default function BannerSwiper({ theme }: BannerSwiperProps) {
style={[
s.image,
{
backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0',
backgroundColor: colorScheme === 'dark' ? '#333' : '#e0e0e0',
},
]}
/>

2
pages/HomeScreen/components/BottomTabs.tsx

@ -12,7 +12,7 @@ import {
} from 'react-native';
import { useColorScheme } from '@/hooks';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
const { width } = Dimensions.get('window');

8
pages/HomeScreen/components/FastFootNav.tsx

@ -14,7 +14,7 @@ import {
Animated,
} from 'react-native';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { useColorScheme } from '@/hooks';
import { mockNavItems } from '@/services/mockHomeService';
import type { NavItem } from '@/types/home';
@ -66,7 +66,6 @@ const styles = createThemeStyles((colors) => ({
}));
interface FastFootNavProps {
theme: 'light' | 'dark';
items?: NavItem[];
onTabPress?: (tabId: string, action: string) => void;
}
@ -74,8 +73,9 @@ interface FastFootNavProps {
/**
*
*/
export default function FastFootNav({ theme, items: propItems, onTabPress }: FastFootNavProps) {
const s = styles[theme];
export default function FastFootNav({ items: propItems, onTabPress }: FastFootNavProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const [items, setItems] = useState<NavItem[]>(propItems || []);
const [selectedId, setSelectedId] = useState<string | null>(null);

165
pages/HomeScreen/components/GameMainMenus/index.tsx

@ -5,19 +5,37 @@
*/
import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react';
import { View, Text, ScrollView, TouchableOpacity, Animated } from 'react-native';
import { View, Text, ScrollView, TouchableOpacity, Animated, Image, Platform } from 'react-native';
// import type { GameCategory } from '@/types/home';
import { styles } from './styles';
import { useGameMainMenus, useMenuDataLoaded } from '@/hooks/useGameMenus';
import { styles } from './styles';
import { useGameMainMenus, useMenuDataLoaded, useSelectedCategory } from '@/hooks/useGameMenus';
// import useGameStore from '@/stores/gameStore';
import { ThemeEnum } from '@/constants/theme';
// import { ThemeEnum } from '@/constants/theme';
import { Colors, useColorScheme } from '@/theme';
// 条件导入 LinearGradient - 仅在非 Web 平台使用
let LinearGradient: any = null;
if (Platform.OS !== 'web') {
LinearGradient = require('react-native-linear-gradient').default;
}
// 游戏菜单图片映射 - 使用 require 加载本地资源
const MENU_ICON_MAP: Record<string, any> = {
'recommend': require('../../../../assets/images/game/menu/recommend.png'),
'chess': require('../../../../assets/images/game/menu/chess.png'),
'electronic': require('../../../../assets/images/game/menu/electronic.png'),
'fishing': require('../../../../assets/images/game/menu/fishing.png'),
'lottery': require('../../../../assets/images/game/menu/lottery.png'),
'sports': require('../../../../assets/images/game/menu/sports.png'),
'trial': require('../../../../assets/images/game/menu/trial.png'),
'blockThird': require('../../../../assets/images/game/menu/blockThird.png'),
'clock-solid': require('../../../../assets/images/game/menu/clock-solid.png'),
'clock-solid_dark': require('../../../../assets/images/game/menu/clock-solid_dark.png'),
'home-star-solid': require('../../../../assets/images/game/menu/home-star-solid.png'),
'live': require('../../../../assets/images/game/menu/live.png'),
};
interface GameMainMenuProps {
theme: ThemeEnum;
selectedCategory?: string;
onCategorySelect?: (categoryId: string) => void;
topHeight?: number;
showSubMenus?: boolean;
}
@ -26,16 +44,17 @@ interface GameMainMenuProps {
*
*/
export default function GameMainMenu({
theme,
selectedCategory = '103',
onCategorySelect,
topHeight = 0,
showSubMenus = true,
}: GameMainMenuProps) {
const theme = useColorScheme();
const s = styles[theme];
const scrollViewRef = useRef<ScrollView>(null);
const gameMenus = useGameMainMenus(theme);
// 从 hook 获取选中分类和更新方法
const { selectedCategory, setSelectedCategory } = useSelectedCategory();
// 检查数据加载完成
const isDataLoaded = useMenuDataLoaded();
@ -55,9 +74,12 @@ export default function GameMainMenu({
}, [selectedIndex]);
// 使用 useCallback 稳定 onPress 回调
const handleCategoryPress = useCallback((categoryKey: string) => {
onCategorySelect?.(categoryKey);
}, [onCategorySelect]);
const handleCategoryPress = useCallback(
(categoryKey: string) => {
setSelectedCategory(categoryKey);
},
[setSelectedCategory]
);
// 骨架屏 - 显示加载中的占位符
const renderSkeleton = () => (
@ -68,16 +90,16 @@ export default function GameMainMenu({
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
{[1, 2, 3, 4, 5].map((index) => (
{Array.from({ length: 6 }).map((_, index) => (
<View
key={`skeleton-${index}`}
style={[s.categoryItem, { backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0' }]}
style={[s.menuItem, { backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0' }]}
/>
))}
</ScrollView>
</View>
);
console.log('isDataLoaded', isDataLoaded);
// 如果动态数据还未加载,显示骨架屏
if (!isDataLoaded) {
return renderSkeleton();
@ -92,20 +114,105 @@ export default function GameMainMenu({
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
{gameMenus.map((menu) => (
<TouchableOpacity
key={menu.key}
style={[s.menuItem, selectedCategory === menu.key && s.menuItemActive]}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
<Text
style={[s.menuText, selectedCategory === menu.key && s.menuTextActive]}
{gameMenus.map((menu) => {
// 处理图片源 - 优先使用 logo(URL),其次使用 icon(本地资源)
let imageSource: any = null;
if (menu.logo) {
// logo 是 URL,直接使用
imageSource = { uri: menu.logo };
} else if (menu.icon) {
// icon 是本地资源名称,从映射表中获取
imageSource = MENU_ICON_MAP[menu.icon];
}
const isActive = selectedCategory === menu.key;
const themeColors = Colors[theme];
// 获取渐变色 - 从主题色到透明
const gradientStart = `${themeColors.tint}40`; // 主题色 + 40% 透明度
const gradientEnd = `${themeColors.tint}00`; // 完全透明
const menuContent = (
<>
{imageSource && (
<Image source={imageSource} style={s.menuIcon} resizeMode="contain" />
)}
<Text style={[s.menuText, isActive && s.menuTextActive]}>
{menu.name}
</Text>
</>
);
// 如果是选中状态,使用 LinearGradient 包装(非 Web 平台)或 CSS 渐变(Web 平台)
if (isActive) {
// Web 平台使用 CSS 渐变
if (Platform.OS === 'web') {
const webStyle = {
...s.menuItem,
...s.menuItemActive,
background: `linear-gradient(to top, ${gradientStart}, ${gradientEnd})`,
backgroundColor: undefined, // 移除 backgroundColor,使用 background
} as any;
return (
<TouchableOpacity
key={menu.key}
style={webStyle}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
);
}
// 非 Web 平台使用 LinearGradient
if (LinearGradient) {
return (
<LinearGradient
key={menu.key}
colors={[gradientStart, gradientEnd]}
start={{ x: 0.5, y: 1 }} // 从下往上
end={{ x: 0.5, y: 0 }}
style={[s.menuItem, s.menuItemActive]}
>
<TouchableOpacity
style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
</LinearGradient>
);
}
// 备用方案:如果 LinearGradient 不可用,使用纯色
return (
<TouchableOpacity
key={menu.key}
style={[s.menuItem, s.menuItemActive]}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menuContent}
</TouchableOpacity>
);
}
// 未选中状态,使用普通 TouchableOpacity
return (
<TouchableOpacity
key={menu.key}
style={s.menuItem}
onPress={() => handleCategoryPress(menu.key)}
activeOpacity={0.7}
>
{menu.icon || '🎮'} {menu.name}
</Text>
</TouchableOpacity>
))}
{menuContent}
</TouchableOpacity>
);
})}
</ScrollView>
</View>
);

41
pages/HomeScreen/components/GameMainMenus/styles.ts

@ -1,4 +1,4 @@
import { createThemeStyles } from '@/theme';
import { createThemeStyles, createResponsiveThemeStyles } from '@/theme';
import { Dimensions } from 'react-native';
// const { width } = Dimensions.get('window');
@ -10,7 +10,7 @@ import { Dimensions } from 'react-native';
export const styles = createThemeStyles((colors) => ({
container: {
backgroundColor: colors.background,
paddingVertical: 10,
paddingTop: 5,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
@ -18,11 +18,11 @@ export const styles = createThemeStyles((colors) => ({
paddingHorizontal: 12,
},
menuItem: {
paddingHorizontal: 16,
paddingVertical: 10,
paddingHorizontal: 12,
paddingVertical: 5,
marginRight: 8,
borderRadius: 22,
backgroundColor: colors.backgroundSecondary,
borderRadius: 0,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
elevation: 1,
@ -30,18 +30,41 @@ export const styles = createThemeStyles((colors) => ({
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
borderBottomColor: 'transparent',
borderBottomWidth: 2,
},
menuItemActive: {
backgroundColor: colors.primary,
// backgroundColor: `${colors.tint}15`, // 主题色 + 20% 透明度
elevation: 2,
shadowOpacity: 0.15,
borderBottomColor: colors.tint,
borderRadius: 0,
},
menuText: {
fontSize: 13,
fontSize: 14,
color: colors.text,
fontWeight: '600',
},
menuTextActive: {
color: '#FFFFFF',
color: colors.tint,
},
menuIcon: {
width: 30,
height: 30,
marginBottom: 4,
},
}));
export const themeStyles = createResponsiveThemeStyles({
menuItemActive: {
backgroundColor: '',
},
}, {
menuItemActive: {
backgroundColor: '',
},
}, {
menuItemActive: {
backgroundColor: '',
},
});

9
pages/HomeScreen/components/Header.tsx

@ -15,7 +15,7 @@ import {
} from 'react-native';
import { useColorScheme } from '@/hooks';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
const { width } = Dimensions.get('window');
@ -88,7 +88,6 @@ const styles = createThemeStyles((colors) => ({
}));
interface HeaderProps {
theme?: 'light' | 'dark';
onSearch?: (keyword: string) => void;
onMessagePress?: () => void;
onUserPress?: () => void;
@ -99,16 +98,14 @@ interface HeaderProps {
* Header
*/
export default function Header({
theme = 'light',
onSearch,
onMessagePress,
onUserPress,
unreadCount = 0,
}: HeaderProps) {
const colorScheme = useColorScheme();
const actualTheme = theme === 'light' || theme === 'dark' ? theme : colorScheme;
const s = styles[actualTheme];
const colors = Colors[actualTheme];
const s = styles[colorScheme];
const colors = Colors[colorScheme];
const [searchText, setSearchText] = useState('');
const [isSearching, setIsSearching] = useState(false);

8
pages/HomeScreen/components/HighPrizeGame.tsx

@ -15,7 +15,7 @@ import {
Image,
} from 'react-native';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { useColorScheme } from '@/hooks';
import { mockHighPrizeGames } from '@/services/mockHomeService';
import type { HighPrizeGame as HighPrizeGameType } from '@/types/home';
@ -85,7 +85,6 @@ const styles = createThemeStyles((colors) => ({
}));
interface HighPrizeGameProps {
theme: 'light' | 'dark';
games?: HighPrizeGameType[];
onGamePress?: (game: HighPrizeGameType) => void;
}
@ -93,8 +92,9 @@ interface HighPrizeGameProps {
/**
*
*/
export default function HighPrizeGame({ theme, games: propGames, onGamePress }: HighPrizeGameProps) {
const s = styles[theme];
export default function HighPrizeGame({ games: propGames, onGamePress }: HighPrizeGameProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const [games, setGames] = useState<HighPrizeGameType[]>(propGames || []);
const [selectedId, setSelectedId] = useState<string | null>(null);

15
pages/HomeScreen/components/Lobby.tsx

@ -15,8 +15,8 @@ import {
ActivityIndicator,
Dimensions,
} from 'react-native';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { createThemeStyles, useColorScheme, useThemeInfo } from '@/theme';
import { useSelectedCategory } from '@/hooks/useGameMenus';
import { getMockGamesByCategory } from '@/services/mockHomeService';
import type { Game } from '@/types/home';
@ -100,9 +100,7 @@ const styles = createThemeStyles((colors) => ({
}));
interface LobbyProps {
theme: 'light' | 'dark';
games?: Game[];
selectedCategory?: number;
onGamePress?: (game: Game) => void;
topHeight?: number;
}
@ -111,13 +109,14 @@ interface LobbyProps {
*
*/
export default function Lobby({
theme,
games: propGames,
selectedCategory = 0,
onGamePress,
topHeight = 0,
}: LobbyProps) {
const s = styles[theme];
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const { colors } = useThemeInfo();
const { selectedCategory } = useSelectedCategory();
const [games, setGames] = useState<Game[]>(propGames || []);
const [loading, setLoading] = useState(true);
@ -179,7 +178,7 @@ export default function Lobby({
if (loading) {
return (
<View style={s.loadingContainer}>
<ActivityIndicator size="large" color={Colors[theme].primary} />
<ActivityIndicator size="large" color={colors.primary} />
</View>
);
}

8
pages/HomeScreen/components/NoticeBar.tsx

@ -14,7 +14,7 @@ import {
Dimensions,
} from 'react-native';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import { useColorScheme } from '@/hooks';
import { mockNotices } from '@/services/mockHomeService';
import type { Notice } from '@/types/home';
@ -66,7 +66,6 @@ const styles = createThemeStyles((colors) => ({
}));
interface NoticeBarProps {
theme: 'light' | 'dark';
notices?: Notice[];
onNoticePress?: (notice: Notice) => void;
}
@ -74,8 +73,9 @@ interface NoticeBarProps {
/**
*
*/
export default function NoticeBar({ theme, notices: propNotices, onNoticePress }: NoticeBarProps) {
const s = styles[theme];
export default function NoticeBar({ notices: propNotices, onNoticePress }: NoticeBarProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const [notices, setNotices] = useState<Notice[]>(propNotices || []);
const [currentNotice, setCurrentNotice] = useState(0);
const [visible, setVisible] = useState(true);

4
pages/HomeScreen/components/index.ts

@ -7,7 +7,7 @@
import React from 'react';
import BannerSwiperComponent from './BannerSwiper';
import NoticeBarComponent from './NoticeBar';
import GameCategoryMenuComponent from './GameCategoryMenu';
import GameMainMenusComponent from './GameMainMenus';
import LobbyComponent from './Lobby';
import HighPrizeGameComponent from './HighPrizeGame';
import FastFootNavComponent from './FastFootNav';
@ -17,7 +17,7 @@ import BottomTabsComponent from './BottomTabs';
// 使用 React.memo 优化组件性能,避免不必要的重新渲染
export const BannerSwiper = React.memo(BannerSwiperComponent);
export const NoticeBar = React.memo(NoticeBarComponent);
export const GameCategoryMenu = React.memo(GameCategoryMenuComponent);
export const GameMainMenus = React.memo(GameMainMenusComponent);
export const Lobby = React.memo(LobbyComponent);
export const HighPrizeGame = React.memo(HighPrizeGameComponent);
export const FastFootNav = React.memo(FastFootNavComponent);

166
pages/HomeScreen/index.tsx

@ -1,23 +1,30 @@
/**
*
*
* /
* - Header
* -
* -
* -
* -
* -
* -
* - BottomTabs
*
* HeaderBottomTabs
*
*/
import React, { useMemo, useCallback } from 'react';
import { ScrollView, StyleSheet, RefreshControl } from 'react-native';
import { useColorScheme } from '@/hooks';
import { createThemeStyles } from '@/theme';
import Colors from '@/constants/Colors';
import HomeScreenComplete from './HomeScreenComplete';
import React, { useState, useEffect, useCallback } from 'react';
import { View, ScrollView, RefreshControl, Alert } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { createThemeStyles, useColorScheme, useThemeInfo } from '@/theme';
import {
Header,
BannerSwiper,
NoticeBar,
GameMainMenus,
Lobby,
HighPrizeGame,
FastFootNav,
} from './components';
import { requestHomePageData } from '@/stores/gameStore';
import { useTenantLoad } from '@/stores/tenantStore';
import type {
Banner,
Notice,
GameCategory,
Game,
HighPrizeGame as HighPrizeGameType,
} from '@/types/home';
/**
*
@ -27,33 +34,124 @@ const styles = createThemeStyles((colors) => ({
flex: 1,
backgroundColor: colors.background,
},
scrollView: {
contentContainer: {
flex: 1,
},
scrollContent: {
paddingBottom: 20,
},
}));
/**
*
*
*/
export default function HomeScreen() {
const theme = useColorScheme();
const s = styles[theme];
const [refreshing, setRefreshing] = React.useState(false);
export default function HomePage() {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const { isDark: isDarkTheme, colors } = useThemeInfo();
const [refreshing, setRefreshing] = useState(false);
const tenantLoad = useTenantLoad();
// 下拉刷新处理
const onRefresh = useCallback(() => {
// 加载首页数据
const loadHomePageData = useCallback(async () => {
try {
await requestHomePageData();
} catch (error) {
console.error('加载首页数据失败:', error);
}
}, []);
// 初始化加载
useEffect(() => {
console.log('租户数据加载完成:', tenantLoad);
if (tenantLoad) {
loadHomePageData();
}
}, [loadHomePageData, tenantLoad]);
// 下拉刷新
const handleRefresh = useCallback(async () => {
setRefreshing(true);
// 模拟刷新延迟
setTimeout(() => {
try {
await loadHomePageData();
} finally {
setRefreshing(false);
}, 1000);
}
}, [loadHomePageData]);
// 处理游戏点击
const handleGamePress = useCallback((game: Game) => {
Alert.alert('游戏', `点击了: ${game.play_up_name}`);
// 这里可以添加打开游戏的逻辑
}, []);
// 处理底部 Tab 点击
const handleTabPress = useCallback((tabId: string, action: string) => {
Alert.alert('导航', `点击了: ${tabId}`);
// 这里可以添加导航逻辑
}, []);
// 处理搜索
const handleSearch = useCallback((keyword: string) => {
Alert.alert('搜索', `搜索关键词: ${keyword}`);
// 这里可以添加搜索逻辑
}, []);
// 根据主题选择要显示的组件
const renderContent = () => {
if (isDarkTheme) {
// 深色主题布局
return (
<>
<GameMainMenus />
<BannerSwiper />
<NoticeBar />
<HighPrizeGame onGamePress={handleGamePress} />
<Lobby onGamePress={handleGamePress} />
<FastFootNav onTabPress={handleTabPress} />
</>
);
} else {
// 浅色主题布局
return (
<>
<BannerSwiper />
<NoticeBar />
<GameMainMenus />
<Lobby onGamePress={handleGamePress} />
</>
);
}
};
return (
<HomeScreenComplete
theme={theme}
isDarkTheme={theme === 'dark'}
/>
<SafeAreaView style={s.container}>
{/* Header */}
<Header
onSearch={handleSearch}
onMessagePress={() => Alert.alert('消息', '消息功能')}
onUserPress={() => Alert.alert('用户', '用户中心')}
unreadCount={3}
/>
{/* 内容区域 */}
<View style={s.contentContainer}>
<ScrollView
style={s.contentContainer}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
tintColor={colors.primary}
/>
}
showsVerticalScrollIndicator={false}
>
<View style={s.scrollContent}>{renderContent()}</View>
</ScrollView>
</View>
</SafeAreaView>
);
}

14
pnpm-lock.yaml

@ -80,6 +80,9 @@ importers:
react-native:
specifier: 0.81.5
version: 0.81.5(@babel/[email protected])(@types/[email protected])([email protected])
react-native-linear-gradient:
specifier: ^2.8.3
version: 2.8.3([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected])
react-native-paper:
specifier: ^5.14.5
version: 5.14.5([email protected]([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected])
@ -2985,6 +2988,12 @@ packages:
react: '*'
react-native: '*'
[email protected]:
resolution: {integrity: sha512-KflAXZcEg54PXkLyflaSZQ3PJp4uC4whM7nT/Uot9m0e/qxFV3p6uor1983D1YOBJbJN7rrWdqIjq0T42jOJyA==}
peerDependencies:
react: '*'
react-native: '*'
[email protected]:
resolution: {integrity: sha512-eaIH5bUQjJ/mYm4AkI6caaiyc7BcHDwX6CqNDi6RIxfxfWxROsHpll1oBuwn/cFvknvA8uEAkqLk/vzVihI3AQ==}
peerDependencies:
@ -7208,6 +7217,11 @@ snapshots:
react: 19.1.0
react-native: 0.81.5(@babel/[email protected])(@types/[email protected])([email protected])
[email protected]([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected]):
dependencies:
react: 19.1.0
react-native: 0.81.5(@babel/[email protected])(@types/[email protected])([email protected])
[email protected]([email protected]([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected]))([email protected](@babel/[email protected])(@types/[email protected])([email protected]))([email protected]):
dependencies:
'@callstack/react-theme-provider': 3.0.9([email protected])

2
services/mockHomeService.ts

@ -224,7 +224,7 @@ export const getMockHomePageData = () => {
/**
* Mock
*/
export const getMockGamesByCategory = (categoryId: number) => {
export const getMockGamesByCategory = (categoryId: string | number) => {
if (categoryId === 0) {
return mockGames; // 推荐分类返回所有游戏
}

9
stores/gameStore.ts

@ -27,6 +27,7 @@ interface State {
gamesTryPlayIds: number[];
smallClassGames: Record<string, any>;
gameBigClass: Record<string, any>;
selectedCategory: string; // 当前选中的游戏分类
}
// 操作
@ -38,6 +39,7 @@ interface Actions {
setHomeHotGames: (data: Record<string, any>[]) => void;
setSmallClassGame: (data: Record<string, any>) => void;
setGameBigClass: (data: Record<string, any>) => void;
setSelectedCategory: (categoryId: string) => void; // 设置选中的游戏分类
// requestHomePageData: (data?: Record<string, any>) => Promise<any>;
}
@ -58,6 +60,7 @@ const useGameStore = create<State & Actions>()((set, get) => ({
gamesTryPlayIds: [], // 试玩游戏id列表
smallClassGames: {},
gameBigClass: {},
selectedCategory: '103', // 默认选中推荐分类
// 保存首页数据
@ -157,6 +160,12 @@ const useGameStore = create<State & Actions>()((set, get) => ({
[GameMainKeysEnum.BLOCK_THIRD]: groupByType?.[10] || [],
} });
},
setSelectedCategory: (categoryId: string) => {
set({ selectedCategory: categoryId });
// 保存到 session storage,页面刷新后仍然保留
storageManager.session.setItem(STORAGE_KEYS.APP_ACTIVE_MAIN_MENU_TAB, categoryId);
},
}));
// 从 AsyncStorage 恢复状态的函数

2
stores/index.ts

@ -8,7 +8,6 @@ export {
useUser,
useIsLoggedIn,
useToken,
useUserActions,
restoreUserState,
} from './userStore';
export type { User } from './userStore';
@ -21,7 +20,6 @@ export {
useNotificationsEnabled,
useSoundEnabled,
useHapticsEnabled,
useSettingsActions,
restoreSettingsState,
} from './settingsStore';
export type { Theme, Language } from './settingsStore';

18
stores/settingsStore.ts

@ -10,7 +10,7 @@ import storageManager, { STORAGE_KEYS } from '@/utils/storageManager';
/**
*
*/
export type Theme = 'light' | 'dark' | 'auto';
export type Theme = 'light' | 'dark' | 'orange' | 'auto';
/**
*
@ -121,8 +121,7 @@ export const restoreSettingsState = async () => {
try {
const stored = await storageManager.local.getItem(STORAGE_KEYS.SETTINGS_STORE);
if (stored) {
const state = JSON.parse(stored);
useSettingsStore.setState(state);
useSettingsStore.setState(stored);
if (__DEV__) {
console.log('✅ Settings state restored from storage');
}
@ -152,16 +151,3 @@ export const useSoundEnabled = () => useSettingsStore((state) => state.soundEnab
// 获取触觉反馈状态
export const useHapticsEnabled = () => useSettingsStore((state) => state.hapticsEnabled);
// 获取设置操作方法
// 使用 useShallow 避免每次渲染都返回新对象
export const useSettingsActions = () =>
useSettingsStore(
useShallow((state) => ({
setTheme: state.setTheme,
setLanguage: state.setLanguage,
setNotificationsEnabled: state.setNotificationsEnabled,
setSoundEnabled: state.setSoundEnabled,
setHapticsEnabled: state.setHapticsEnabled,
resetSettings: state.resetSettings,
}))
);

20
stores/userStore.ts

@ -147,13 +147,13 @@ export const useToken = () => useUserStore((state) => state.token);
// 获取用户操作方法
// 使用 useShallow 避免每次渲染都返回新对象
export const useUserActions = () =>
useUserStore(
useShallow((state) => ({
setUser: state.setUser,
setToken: state.setToken,
login: state.login,
logout: state.logout,
updateUser: state.updateUser,
}))
);
// export const useUserActions = () =>
// useUserStore(
// useShallow((state) => ({
// setUser: state.setUser,
// setToken: state.setToken,
// login: state.login,
// logout: state.logout,
// updateUser: state.updateUser,
// }))
// );

9
constants/Colors.ts → theme/Colors/index.ts

@ -4,8 +4,9 @@
* settingsStore
*/
const tintColorLight = '#007AFF';
const tintColorDark = '#0A84FF';
const tintColorLight = '#10c8e3';
const tintColorDark = '#ffd69f';
const tintColorOrange = '#bd9534';
export default {
light: {
@ -71,7 +72,7 @@ export default {
backgroundTertiary: '#E5E5E5',
// 主题色
tint: tintColorLight,
tint: tintColorOrange,
primary: '#007AFF',
secondary: '#5856D6',
success: '#34C759',
@ -85,7 +86,7 @@ export default {
// Tab 图标
tabIconDefault: '#8E8E93',
tabIconSelected: tintColorLight,
tabIconSelected: tintColorOrange,
// 卡片
card: '#FFFFFF',

7
theme/index.ts

@ -1,11 +1,11 @@
/**
*
*
*
*
*/
// 导出颜色配置
export { default as Colors } from '@/constants/Colors';
export { default as Colors } from './Colors';
// 导出主题 Hooks
export {
@ -23,6 +23,9 @@ export {
View as ThemeView,
} from '@/components/Themed';
// 导出主题类型
export { ThemeEnum } from '@/constants/theme';
export type {
ThemedTextProps,
ThemedViewProps,

6
theme/styles.ts

@ -7,7 +7,7 @@
*/
import { StyleSheet, TextStyle, ViewStyle } from 'react-native';
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
import { ThemeEnum } from '@/constants/theme';
/**
@ -53,7 +53,7 @@ export function createThemeStyles<T extends StyleSheet.NamedStyles<T>>(
return {
light: StyleSheet.create(createStyles(Colors.light)),
dark: StyleSheet.create(createStyles(Colors.dark)),
orange: StyleSheet.create(createStyles(Colors.light)),
orange: StyleSheet.create(createStyles(Colors.orange)),
};
}
@ -89,7 +89,7 @@ export function createResponsiveThemeStyles<T extends StyleSheet.NamedStyles<T>>
return {
light: StyleSheet.create(lightStyles),
dark: StyleSheet.create(darkStyles),
orange: StyleSheet.create(lightStyles),
orange: StyleSheet.create(orangeStyles),
};
}

2
theme/utils.ts

@ -4,7 +4,7 @@
*
*/
import Colors from '@/constants/Colors';
import { Colors } from '@/theme';
import { ThemeEnum } from '@/constants/theme';
/**

Loading…
Cancel
Save