import { LEVEL_MAP } from '@/constants/config'; import { Images } from '@/constants/images'; import { countRecordsAfterLastLevel, getBuyRecord } from '@/services/award'; import { Image, ImageBackground } from 'expo-image'; import React, { forwardRef, useEffect, useImperativeHandle, useState } from 'react'; import { ActivityIndicator, Dimensions, FlatList, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; const { width, height } = Dimensions.get('window'); const TABS = [ { title: '全部', value: '' }, { title: '超神款', value: 'A' }, { title: '欧皇款', value: 'B' }, { title: '隐藏款', value: 'C' }, { title: '普通款', value: 'D' } ]; interface RecordModalProps { poolId: string; } export interface RecordModalRef { show: () => void; close: () => void; } export const RecordModal = forwardRef(({ poolId }, ref) => { const [visible, setVisible] = useState(false); const [data, setData] = useState([]); const [activeTab, setActiveTab] = useState(TABS[0]); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [lastId, setLastId] = useState(undefined); const [taggingA, setTaggingA] = useState(0); const [taggingB, setTaggingB] = useState(0); useImperativeHandle(ref, () => ({ show: () => { setVisible(true); refresh(); }, close: () => setVisible(false) })); const refresh = () => { setLastId(undefined); setData([]); loadData(true); loadStats(); }; const loadStats = async () => { try { const resA = await countRecordsAfterLastLevel({ levelEnumList: ['A'], poolId }); setTaggingA(resA.data || 0); // Check API structure: {data: number} const resB = await countRecordsAfterLastLevel({ levelEnumList: ['B'], poolId }); setTaggingB(resB.data || 0); } catch (e) { console.error('Failed to load stats', e); } }; const loadData = async (isRefresh = false) => { if (loading) return; setLoading(true); try { const currentLastId = isRefresh ? undefined : lastId; const res = await getBuyRecord(poolId, currentLastId, activeTab.value as any); if (res && res.length > 0) { setData(prev => isRefresh ? res : [...prev, ...res]); setLastId(res[res.length - 1].id); } } catch (e) { console.error('Failed to load records', e); } setLoading(false); setRefreshing(false); }; const handleTabChange = (tab: any) => { setActiveTab(tab); // Reset and reload setLastId(undefined); setData([]); // We can't immediately call loadData due to state update async nature, // but let's assume useEffect or immediate call with arg works. // Actually best to useEffect on activeTab change? // Or specific effect for tab // Let's do explicit reload in effect or here // setData is sync-ish in RN usually batching, let's use effect. }; useEffect(() => { if (visible) { setLastId(undefined); setData([]); loadData(true); } }, [activeTab]); const renderItem = ({ item }: { item: any }) => ( {item.nickname} {/* Level Icon - Assuming we have images for level text similar to legacy */} {/* Legacy: :src="LEVEL_MAP[item.level].titleText" */} {/* We don't have titleText in our config yet, need to map or use text */} {/* For now use Text with color */} {LEVEL_MAP[item.level]?.title} {item.name} ×{item.lastCount} ); return ( setVisible(false)}> setVisible(false)} /> {/* Header */} 购买记录 setVisible(false)}> {/* Close Icon or Text */} × {/* Stats Banner */} {taggingA} 发未出 超神款 {taggingB} 发未出 欧皇款 {/* Tabs */} {TABS.map(tab => ( handleTabChange(tab)} > {tab.title} ))} {/* List */} item.id || String(index)} style={styles.list} contentContainerStyle={{ paddingBottom: 20 }} onEndReached={() => loadData(false)} onEndReachedThreshold={0.5} ListEmptyComponent={ !loading ? 暂无记录 : null } ListFooterComponent={loading ? : null} /> ); }); const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end', }, mask: { flex: 1, }, container: { height: height * 0.7, paddingTop: 0, backgroundColor: '#fff', borderTopLeftRadius: 15, borderTopRightRadius: 15, overflow: 'hidden', }, header: { height: 50, justifyContent: 'center', alignItems: 'center', marginTop: 10, }, title: { fontSize: 18, fontWeight: 'bold', }, closeBtn: { position: 'absolute', right: 20, top: 15, width: 30, height: 30, alignItems: 'center', justifyContent: 'center', backgroundColor: '#eee', borderRadius: 15, }, statsBanner: { width: '90%', alignSelf: 'center', height: 50, flexDirection: 'row', justifyContent: 'space-around', alignItems: 'center', marginBottom: 10, }, statItem: { flexDirection: 'row', alignItems: 'center', }, statNum: { fontSize: 16, fontWeight: 'bold', color: '#fff', textShadowColor:'#000', textShadowRadius:1 }, statLabel: { fontSize: 12, color: '#ddd', marginHorizontal: 5 }, statLevel: { fontSize: 12, fontWeight: 'bold' }, tabs: { flexDirection: 'row', justifyContent: 'space-around', paddingHorizontal: 10, marginBottom: 10, backgroundColor: 'rgba(255,255,255,0.1)', paddingVertical: 5, }, tab: { paddingVertical: 5, paddingHorizontal: 10, borderRadius: 5, backgroundColor: '#eee', }, activeTab: { backgroundColor: '#FEC433', }, tabText: { fontSize: 12, color: '#666' }, activeTabText: { color: '#000', fontWeight: 'bold' }, list: { flex: 1, paddingHorizontal: 15, }, item: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FFF7E3', marginBottom: 10, padding: 5, borderRadius: 8, height: 60, }, avatar: { width: 40, height: 40, borderRadius: 20 }, nickname: { flex: 1, marginLeft: 10, fontSize: 12, color: '#333' }, levelTag: { borderWidth: 1, paddingHorizontal: 4, borderRadius: 4, marginHorizontal: 5 }, levelText: { fontSize: 10, fontWeight: 'bold' }, itemName: { width: 80, fontSize: 12, color: '#FEC433', marginHorizontal: 5 }, countText: { fontSize: 12, color: '#000', fontWeight: 'bold', marginRight: 5 }, itemImage: { width: 40, height: 40, borderRadius: 20, backgroundColor: '#fff' }, emptyText: { textAlign: 'center', marginTop: 20, color: '#999' } });