Files
rn-app1/app/(tabs)/activity.tsx
2025-11-11 15:54:13 +07:00

300 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { ThemedView } from "@/components/themed-view";
import { Text, SafeAreaView, StyleSheet, View, FlatList, TouchableOpacity, Dimensions, RefreshControl, Image } from "react-native";
const screenWidth = Dimensions.get('window').width;
// 分类列表 - 添加图标emoji扩展到12个
const categoryList = [
{ id: '0', title: '捕鱼', icon: '🎣' },
{ id: '1', title: '真人', icon: '👤' },
{ id: '2', title: '区块链', icon: '⛓️' },
{ id: '3', title: '棋牌', icon: '🎴' },
{ id: '4', title: '电子', icon: '🎰' },
{ id: '5', title: '体育', icon: '⚽' },
{ id: '6', title: '彩票', icon: '🎫' },
{ id: '7', title: '电竞', icon: '🎮' },
{ id: '8', title: '赛车', icon: '🏎️' },
{ id: '9', title: '街机', icon: '🕹️' },
{ id: '10', title: '桌游', icon: '🎲' },
{ id: '11', title: '其他', icon: '⭐' },
];
// 生成随机数量的游戏列表1-11个
const generateRandomGames = (categoryId: string, baseName: string): Array<{id: string, name: string, hot: boolean, image: string}> => {
const count = Math.floor(Math.random() * 11) + 1; // 1-11个随机数量
const games = [];
const gameTypes = ['经典版', '豪华版', '至尊版', '竞技版', '休闲版', '大师版', '传奇版', '极速版', '黄金版', '钻石版', '王者版'];
// 模拟图片URL使用picsum.photos随机图片服务
const colors = ['4A90E2', 'E94B3C', '50C878', 'F39C12', '9B59B6', 'E74C3C'];
for (let i = 0; i < count; i++) {
const colorIndex = Math.floor(Math.random() * colors.length);
games.push({
id: `${categoryId}-${i + 1}`,
name: `${baseName}${gameTypes[i % gameTypes.length]}`,
hot: Math.random() > 0.6, // 40%概率为热门
image: `https://via.placeholder.com/120x80/${colors[colorIndex]}/ffffff?text=Game+${i + 1}`,
});
}
return games;
};
// 模拟右侧列表数据 - 随机数量扩展到12个分类
const mockData: Record<string, Array<{id: string, name: string, hot: boolean, image: string}>> = {
'0': generateRandomGames('0', '捕鱼'),
'1': generateRandomGames('1', '真人百家乐'),
'2': generateRandomGames('2', '链上竞猜'),
'3': generateRandomGames('3', '棋牌对战'),
'4': generateRandomGames('4', '老虎机'),
'5': generateRandomGames('5', '体育竞猜'),
'6': generateRandomGames('6', '彩票'),
'7': generateRandomGames('7', '电竞对战'),
'8': generateRandomGames('8', '赛车竞速'),
'9': generateRandomGames('9', '街机游戏'),
'10': generateRandomGames('10', '桌面游戏'),
'11': generateRandomGames('11', '特色游戏'),
};
type LeftItemProps = {
id: string;
title: string;
icon: string;
isSelected: boolean;
onPress: () => void;
};
type RightItemProps = {
name: string;
hot: boolean;
image: string;
};
export default function ActivityScreen() {
const [selectedId, setSelectedId] = useState('0');
const [refreshingLeft, setRefreshingLeft] = useState(false);
const [refreshingRight, setRefreshingRight] = useState(false);
// 左侧下拉刷新
const onRefreshLeft = () => {
setRefreshingLeft(true);
// 模拟刷新延迟
setTimeout(() => {
setRefreshingLeft(false);
}, 1000);
};
// 右侧下拉刷新
const onRefreshRight = () => {
setRefreshingRight(true);
// 重新生成当前分类的数据
const categoryNames: Record<string, string> = {
'0': '捕鱼', '1': '真人百家乐', '2': '链上竞猜', '3': '棋牌对战',
'4': '老虎机', '5': '体育竞猜', '6': '彩票', '7': '电竞对战',
'8': '赛车竞速', '9': '街机游戏', '10': '桌面游戏', '11': '特色游戏'
};
mockData[selectedId] = generateRandomGames(selectedId, categoryNames[selectedId]);
setTimeout(() => {
setRefreshingRight(false);
}, 1000);
};
const LeftItem = ({ id, title, icon, isSelected, onPress }: LeftItemProps) => (
<TouchableOpacity
style={[styles.leftItem, isSelected && styles.leftItemActive]}
onPress={onPress}
>
<Text style={styles.leftIcon}>{icon}</Text>
<Text style={[styles.leftTitle, isSelected && styles.leftTitleActive]}>
{title}
</Text>
{isSelected && <View style={styles.activeIndicator} />}
</TouchableOpacity>
);
const RightItem = ({ name, hot, image }: RightItemProps) => (
<View style={styles.rightItem}>
<Image
source={{ uri: image }}
style={styles.gameImage}
resizeMode="cover"
/>
<View style={styles.rightItemInfo}>
<View style={styles.rightItemContent}>
<Text style={styles.rightItemTitle} numberOfLines={1}>{name}</Text>
{hot && <View style={styles.hotBadge}><Text style={styles.hotText}>HOT</Text></View>}
</View>
<Text style={styles.rightItemDesc}></Text>
</View>
</View>
);
return (
<SafeAreaView style={styles.safeAreaContainer}>
<ThemedView style={styles.container}>
<View style={styles.content}>
{/* 左侧菜单 */}
<View style={styles.leftWrapper}>
<FlatList
data={categoryList}
renderItem={({ item }) => (
<LeftItem
id={item.id}
title={item.title}
icon={item.icon}
isSelected={selectedId === item.id}
onPress={() => setSelectedId(item.id)}
/>
)}
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={refreshingLeft}
onRefresh={onRefreshLeft}
colors={['#1890ff']}
tintColor="#1890ff"
/>
}
/>
</View>
{/* 右侧列表 */}
<View style={styles.contentRight}>
<FlatList
data={mockData[selectedId] || []}
renderItem={({ item }) => (
<RightItem name={item.name} hot={item.hot} image={item.image} />
)}
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.rightListContent}
refreshControl={
<RefreshControl
refreshing={refreshingRight}
onRefresh={onRefreshRight}
colors={['#1890ff']}
tintColor="#1890ff"
/>
}
/>
</View>
</View>
</ThemedView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
safeAreaContainer: {
flex: 1,
backgroundColor: '#f5f5f5',
},
container: {
flex: 1,
},
content: {
flex: 1,
flexDirection: 'row',
},
leftWrapper: {
width: 90,
backgroundColor: '#fff',
borderRightWidth: 1,
borderRightColor: '#e0e0e0',
},
leftItem: {
paddingVertical: 18,
paddingHorizontal: 8,
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
},
leftItemActive: {
backgroundColor: '#f0f0f0',
},
leftIcon: {
fontSize: 24,
marginBottom: 4,
},
leftTitle: {
fontSize: 12,
color: '#666',
textAlign: 'center',
},
leftTitleActive: {
color: '#1890ff',
fontWeight: '600',
},
activeIndicator: {
position: 'absolute',
left: 0,
top: '50%',
marginTop: -12,
width: 3,
height: 24,
backgroundColor: '#1890ff',
borderTopRightRadius: 2,
borderBottomRightRadius: 2,
},
contentRight: {
flex: 1,
backgroundColor: '#f5f5f5',
},
rightListContent: {
padding: 12,
},
rightItem: {
backgroundColor: '#fff',
borderRadius: 8,
padding: 12,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
flexDirection: 'row',
alignItems: 'center',
},
gameImage: {
width: 120,
height: 80,
borderRadius: 6,
marginRight: 12,
backgroundColor: '#f0f0f0',
},
rightItemInfo: {
flex: 1,
justifyContent: 'center',
},
rightItemContent: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 6,
},
rightItemTitle: {
fontSize: 15,
fontWeight: '600',
color: '#333',
marginRight: 8,
flex: 1,
},
hotBadge: {
backgroundColor: '#ff4d4f',
paddingHorizontal: 6,
paddingVertical: 2,
borderRadius: 4,
},
hotText: {
color: '#fff',
fontSize: 10,
fontWeight: '600',
},
rightItemDesc: {
fontSize: 13,
color: '#999',
},
});