feat: 首页更新

This commit is contained in:
2025-11-11 18:48:54 +08:00
parent 230191f181
commit b48cce06f4
43 changed files with 3186 additions and 1029 deletions

View File

@@ -0,0 +1,112 @@
/**
* 游戏分类菜单组件
*
* 展示游戏分类,支持切换,使用真实数据
*/
import React, { useRef, useEffect, useState, useCallback, useMemo } from 'react';
import { View, Text, ScrollView, TouchableOpacity, Animated } from 'react-native';
// import type { GameCategory } from '@/types/home';
import { styles } from './styles';
import { useGameMainMenus, useMenuDataLoaded } from '@/hooks/useGameMenus';
// import useGameStore from '@/stores/gameStore';
import { ThemeEnum } from '@/constants/theme';
interface GameMainMenuProps {
theme: ThemeEnum;
selectedCategory?: string;
onCategorySelect?: (categoryId: string) => void;
topHeight?: number;
showSubMenus?: boolean;
}
/**
* 游戏分类菜单组件
*/
export default function GameMainMenu({
theme,
selectedCategory = '103',
onCategorySelect,
topHeight = 0,
showSubMenus = true,
}: GameMainMenuProps) {
const s = styles[theme];
const scrollViewRef = useRef<ScrollView>(null);
const gameMenus = useGameMainMenus(theme);
// 检查数据加载完成
const isDataLoaded = useMenuDataLoaded();
// 使用 useMemo 缓存找到的索引,避免每次都重新计算
const selectedIndex = useMemo(() => {
return gameMenus.findIndex((cat) => cat.key === selectedCategory);
}, [selectedCategory, gameMenus]);
// 当分类改变时,滚动到该分类
useEffect(() => {
if (selectedIndex >= 0) {
scrollViewRef.current?.scrollTo({
x: selectedIndex * 100,
animated: true,
});
}
}, [selectedIndex]);
// 使用 useCallback 稳定 onPress 回调
const handleCategoryPress = useCallback((categoryKey: string) => {
onCategorySelect?.(categoryKey);
}, [onCategorySelect]);
// 骨架屏 - 显示加载中的占位符
const renderSkeleton = () => (
<View style={s.container}>
<ScrollView
style={s.scrollView}
horizontal
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
>
{[1, 2, 3, 4, 5].map((index) => (
<View
key={`skeleton-${index}`}
style={[s.categoryItem, { backgroundColor: theme === 'dark' ? '#333' : '#e0e0e0' }]}
/>
))}
</ScrollView>
</View>
);
// 如果动态数据还未加载,显示骨架屏
if (!isDataLoaded) {
return renderSkeleton();
}
return (
<View style={s.container}>
<ScrollView
ref={scrollViewRef}
style={s.scrollView}
horizontal
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]}
>
{menu.icon || '🎮'} {menu.name}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
}

View File

@@ -0,0 +1,47 @@
import { createThemeStyles } from '@/theme';
import { Dimensions } from 'react-native';
// const { width } = Dimensions.get('window');
// const BANNER_HEIGHT = width * 0.32534; // 保持 32.534% 的宽高比
/**
* 创建主题样式
*/
export const styles = createThemeStyles((colors) => ({
container: {
backgroundColor: colors.background,
paddingVertical: 10,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
scrollView: {
paddingHorizontal: 12,
},
menuItem: {
paddingHorizontal: 16,
paddingVertical: 10,
marginRight: 8,
borderRadius: 22,
backgroundColor: colors.backgroundSecondary,
justifyContent: 'center',
alignItems: 'center',
elevation: 1,
shadowColor: colors.cardShadow,
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.1,
shadowRadius: 2,
},
menuItemActive: {
backgroundColor: colors.primary,
elevation: 2,
shadowOpacity: 0.15,
},
menuText: {
fontSize: 13,
color: colors.text,
fontWeight: '600',
},
menuTextActive: {
color: '#FFFFFF',
},
}));