| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- import { Image } from 'expo-image';
- import { useRouter } from 'expo-router';
- import React, { useCallback, useEffect, useState } from 'react';
- import {
- ActivityIndicator,
- FlatList,
- ImageBackground,
- RefreshControl,
- StatusBar,
- StyleSheet,
- Text,
- TextInput,
- TouchableOpacity,
- View,
- } from 'react-native';
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
- import { Images } from '@/constants/images';
- import { getFeedbackList, getPoolList, PoolItem } from '@/services/award';
- const typeList = [
- { label: '全部', value: '', type: 0, img: Images.box.type1, imgOn: Images.box.type1On },
- { label: '高保赏', value: 'UNLIMITED', type: 2, img: Images.box.type3, imgOn: Images.box.type3On },
- { label: '高爆赏', value: 'UNLIMITED', type: 1, img: Images.box.type2, imgOn: Images.box.type2On },
- { label: '擂台赏', value: 'YFS_PRO', type: 7, img: Images.box.type5, imgOn: Images.box.type5On },
- { label: '一番赏', value: 'YFS_PRO', type: 0, img: Images.box.type4, imgOn: Images.box.type4On },
- ];
- interface BarrageItem {
- id: string;
- content: string;
- nickname?: string;
- }
- export default function BoxScreen() {
- const router = useRouter();
- const insets = useSafeAreaInsets();
- const [keyword, setKeyword] = useState('');
- const [typeIndex, setTypeIndex] = useState(0);
- const [priceSort, setPriceSort] = useState(0);
- const [list, setList] = useState<PoolItem[]>([]);
- const [loading, setLoading] = useState(false);
- const [refreshing, setRefreshing] = useState(false);
- const [current, setCurrent] = useState(1);
- const [total, setTotal] = useState(0);
- const [hasMore, setHasMore] = useState(true);
- const [barrageList, setBarrageList] = useState<BarrageItem[]>([]);
- // 加载弹幕
- const loadBarrage = useCallback(async () => {
- try {
- const res = await getFeedbackList();
- if (res.data) {
- setBarrageList(res.data);
- }
- } catch (error) {
- console.error('加载弹幕失败:', error);
- }
- }, []);
- const loadData = useCallback(async (isRefresh = false) => {
- if (loading) return;
-
- const page = isRefresh ? 1 : current;
- if (!isRefresh && !hasMore) return;
- setLoading(true);
- try {
- const selectedType = typeList[typeIndex];
- const res = await getPoolList({
- current: page,
- size: 10,
- mode: selectedType.value || undefined,
- type: selectedType.type,
- keyword: keyword || undefined,
- priceSort: priceSort || undefined,
- });
- if (res.success && res.data) {
- const newList = isRefresh ? res.data : [...list, ...res.data];
- setList(newList);
- setTotal(res.count || 0);
- setCurrent(page + 1);
- setHasMore(newList.length < (res.count || 0));
- }
- } catch (error) {
- console.error('加载奖池列表失败:', error);
- }
- setLoading(false);
- setRefreshing(false);
- }, [current, hasMore, loading, typeIndex, list, keyword, priceSort]);
- useEffect(() => {
- loadData(true);
- loadBarrage();
- }, [typeIndex, priceSort]);
- const handleRefresh = () => {
- setRefreshing(true);
- loadData(true);
- };
- const handleLoadMore = () => {
- if (!loading && hasMore) {
- loadData(false);
- }
- };
- const handleTypeChange = (index: number) => {
- setTypeIndex(index);
- setList([]);
- setCurrent(1);
- setHasMore(true);
- };
- const handlePriceSort = () => {
- setPriceSort((prev) => (prev + 1) % 3);
- };
- const handleItemPress = (item: PoolItem) => {
- // 检查商品状态
- if (item.status !== undefined && item.status !== 1) return;
-
- console.log('点击商品:', item.id, 'mode:', item.mode, 'type:', item.type);
-
- // 根据类型跳转到不同页面 - 按照小程序逻辑
- if (item.type === 7) {
- // 擂台赏跳转到 boxInBox 页面
- router.push({ pathname: '/boxInBox', params: { poolId: item.id } } as any);
- } else if (item.mode === 'UNLIMITED') {
- // 高爆赏/高保赏
- router.push({ pathname: '/award-detail', params: { poolId: item.id } } as any);
- } else if (item.mode === 'YFS_PRO') {
- // 一番赏
- router.push({ pathname: '/award-detail-yfs', params: { poolId: item.id } } as any);
- } else {
- // 其他商品
- router.push(`/product/${item.id}` as any);
- }
- };
- const renderItem = ({ item }: { item: PoolItem }) => (
- <TouchableOpacity
- style={styles.itemContainer}
- onPress={() => handleItemPress(item)}
- activeOpacity={0.8}
- >
- <ImageBackground
- source={{ uri: Images.box.goodsItemBg }}
- style={styles.itemBg}
- resizeMode="stretch"
- >
- <Image
- source={{ uri: item.cover }}
- style={styles.itemImage}
- contentFit="cover"
- />
- <View style={styles.itemInfo}>
- <Text style={styles.itemName} numberOfLines={1}>{item.name}</Text>
- <Text style={styles.itemPrice}>
- <Text style={styles.priceUnit}>¥</Text>
- {item.price}起
- </Text>
- </View>
- </ImageBackground>
- </TouchableOpacity>
- );
- const renderHeader = () => (
- <View>
- {/* 占位空间 - 给顶部搜索栏留出空间 */}
- <View style={{ height: 53 }} />
- {/* 顶部主图 - 绝对定位,叠在背景上 */}
- <View style={styles.mainImageContainer}>
- <Image
- source={{ uri: Images.box.awardMainImg }}
- style={styles.mainImage}
- contentFit="contain"
- />
- </View>
- {/* 占位空间 - 主图高度 */}
- <View style={{ height: 300 }} />
- {/* 弹幕区域 */}
- {barrageList.length > 0 && (
- <View style={styles.barrageSection}>
- <View style={styles.barrageRow}>
- {barrageList.slice(0, 3).map((item, index) => (
- <View key={item.id || index} style={styles.barrageItem}>
- <Text style={styles.barrageText} numberOfLines={1}>{item.content}</Text>
- </View>
- ))}
- </View>
- <View style={[styles.barrageRow, { marginLeft: 25 }]}>
- {barrageList.slice(3, 6).map((item, index) => (
- <View key={item.id || index} style={styles.barrageItem}>
- <Text style={styles.barrageText} numberOfLines={1}>{item.content}</Text>
- </View>
- ))}
- </View>
- </View>
- )}
- {/* 分类筛选 */}
- <View style={styles.typeSection}>
- <View style={styles.typeList}>
- {typeList.map((item, index) => (
- <TouchableOpacity
- key={index}
- style={styles.typeItem}
- onPress={() => handleTypeChange(index)}
- >
- <Image
- source={{ uri: typeIndex === index ? item.imgOn : item.img }}
- style={styles.typeImage}
- contentFit="contain"
- />
- </TouchableOpacity>
- ))}
- </View>
- <TouchableOpacity style={styles.sortBtn} onPress={handlePriceSort}>
- <Image
- source={{
- uri: priceSort === 0
- ? Images.box.sortAmount
- : priceSort === 1
- ? Images.box.sortAmountOnT
- : Images.box.sortAmountOnB,
- }}
- style={styles.sortIcon}
- contentFit="contain"
- />
- </TouchableOpacity>
- </View>
- </View>
- );
- const renderFooter = () => {
- if (!loading) return null;
- return (
- <View style={styles.footer}>
- <ActivityIndicator size="small" color="#fff" />
- <Text style={styles.footerText}>加载中...</Text>
- </View>
- );
- };
- const renderEmpty = () => {
- if (loading) return null;
- return (
- <View style={styles.empty}>
- <Text style={styles.emptyText}>暂无数据</Text>
- </View>
- );
- };
- return (
- <View style={styles.container}>
- <StatusBar barStyle="light-content" />
- <ImageBackground
- source={{ uri: Images.common.indexBg }}
- style={styles.background}
- resizeMode="cover"
- >
- {/* 顶部搜索栏 */}
- <View style={[styles.header, { paddingTop: insets.top + 10 }]}>
- <Image
- source={{ uri: Images.home.portrait }}
- style={styles.logo}
- contentFit="contain"
- />
- <View style={styles.searchBar}>
- <Image
- source={{ uri: Images.home.search }}
- style={styles.searchIcon}
- contentFit="contain"
- />
- <TextInput
- style={styles.searchInput}
- value={keyword}
- onChangeText={setKeyword}
- placeholder="搜索"
- placeholderTextColor="rgba(255,255,255,0.5)"
- returnKeyType="search"
- onSubmitEditing={() => loadData(true)}
- />
- </View>
- </View>
- {/* 列表 */}
- <FlatList
- data={list}
- renderItem={renderItem}
- keyExtractor={(item) => item.id}
- ListHeaderComponent={renderHeader}
- ListFooterComponent={renderFooter}
- ListEmptyComponent={renderEmpty}
- contentContainerStyle={styles.listContent}
- showsVerticalScrollIndicator={false}
- refreshControl={
- <RefreshControl
- refreshing={refreshing}
- onRefresh={handleRefresh}
- tintColor="#fff"
- />
- }
- onEndReached={handleLoadMore}
- onEndReachedThreshold={0.3}
- />
- </ImageBackground>
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#1a1a2e',
- },
- background: {
- flex: 1,
- },
- header: {
- position: 'relative',
- zIndex: 11,
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 15,
- paddingBottom: 10,
- },
- logo: {
- width: 67,
- height: 25,
- marginRight: 20,
- },
- searchBar: {
- flex: 1,
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: 'rgba(255,255,255,0.38)',
- borderRadius: 180,
- paddingHorizontal: 15,
- height: 28,
- },
- searchIcon: {
- width: 15,
- height: 15,
- marginRight: 5,
- },
- searchInput: {
- flex: 1,
- color: '#fff',
- fontSize: 12,
- padding: 0,
- },
- mainImageContainer: {
- position: 'absolute',
- left: 0,
- top: 0,
- zIndex: 2,
- width: '100%',
- height: 395,
- },
- mainImage: {
- width: '100%',
- height: '100%',
- },
- typeSection: {
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 10,
- paddingVertical: 10,
- },
- typeList: {
- flex: 1,
- flexDirection: 'row',
- justifyContent: 'space-around',
- },
- typeItem: {
- width: 75,
- height: 30,
- },
- typeImage: {
- width: '100%',
- height: '100%',
- },
- sortBtn: {
- width: '10%',
- alignItems: 'center',
- },
- sortIcon: {
- width: 20,
- height: 20,
- },
- listContent: {
- paddingHorizontal: 10,
- paddingBottom: 100,
- },
- itemContainer: {
- marginBottom: 8,
- },
- itemBg: {
- width: '100%',
- height: 210,
- padding: 8,
- },
- itemImage: {
- width: '100%',
- height: 142,
- borderRadius: 8,
- },
- itemInfo: {
- paddingHorizontal: 15,
- paddingTop: 15,
- },
- itemName: {
- color: '#fff',
- fontSize: 14,
- },
- itemPrice: {
- color: '#ff0000',
- fontSize: 12,
- fontWeight: 'bold',
- marginTop: 5,
- },
- priceUnit: {
- fontSize: 12,
- marginRight: 2,
- },
- footer: {
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- paddingVertical: 15,
- },
- footerText: {
- color: 'rgba(255,255,255,0.6)',
- fontSize: 12,
- marginLeft: 8,
- },
- empty: {
- alignItems: 'center',
- paddingVertical: 50,
- },
- emptyText: {
- color: 'rgba(255,255,255,0.6)',
- fontSize: 14,
- },
- barrageSection: {
- marginVertical: 10,
- paddingHorizontal: 10,
- },
- barrageRow: {
- flexDirection: 'row',
- marginBottom: 5,
- },
- barrageItem: {
- backgroundColor: 'rgba(0,0,0,0.5)',
- borderRadius: 15,
- paddingHorizontal: 12,
- paddingVertical: 6,
- marginRight: 8,
- maxWidth: 150,
- },
- barrageText: {
- color: '#fff',
- fontSize: 12,
- },
- });
|