import React, { forwardRef, useCallback, useImperativeHandle, useState } from 'react'; import { ActivityIndicator, Alert, Dimensions, ImageBackground, Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { Images } from '@/constants/images'; import { getUnavailableSeatNumbers, previewOrder } from '@/services/award'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); const ITEM_WIDTH = Math.floor((SCREEN_WIDTH - 28 - 12 * 4) / 5); // 等级配置 const LEVEL_MAP: Record = { A: { title: '超神款', color: '#FF4444' }, B: { title: '欧皇款', color: '#FF9600' }, C: { title: '隐藏款', color: '#9B59B6' }, D: { title: '普通款', color: '#00CCFF' }, }; interface BoxData { number: string; quantity: number; lastNumber: number; } interface NumChooseModalProps { poolId: string; onPay: (params: { preview: any; seatNumbers: number[]; boxNumber: string }) => void; } export interface NumChooseModalRef { show: (box: BoxData) => void; close: () => void; } export const NumChooseModal = forwardRef( ({ poolId, onPay }, ref) => { const [visible, setVisible] = useState(false); const [box, setBox] = useState(null); const [tabs, setTabs] = useState<{ title: string; value: number; data: number[] }[]>([]); const [currentTab, setCurrentTab] = useState<{ title: string; value: number; data: number[] } | null>(null); const [checkMap, setCheckMap] = useState>({}); const [useMap, setUseMap] = useState>({}); const [lockMap, setLockMap] = useState>({}); const [loading, setLoading] = useState(false); // 已选择的号码列表 const chooseData = Object.values(checkMap); // 初始化标签页 const handleTab = useCallback((boxData: BoxData) => { const totalData: number[] = []; for (let i = 1; i <= boxData.quantity; i++) { totalData.push(i); } const newTabs: { title: string; value: number; data: number[] }[] = []; const count = Math.floor(boxData.quantity / 100) + (boxData.quantity % 100 > 0 ? 1 : 0); for (let i = 0; i < count; i++) { let title = `${100 * i + 1}~${100 * i + 100}`; if (100 * (i + 1) > totalData.length) { title = `${100 * i + 1}~${totalData.length}`; } newTabs.push({ title, value: i + 1, data: totalData.slice(100 * i, 100 * (i + 1)), }); } setTabs(newTabs); setCurrentTab(newTabs[0] || null); }, []); // 获取不可用座位号 const getData = useCallback(async (boxData: BoxData, tab?: { data: number[] }) => { try { let startSeatNumber = 1; let endSeatNumber = 100; if (tab && tab.data.length > 0) { startSeatNumber = tab.data[0]; endSeatNumber = tab.data[tab.data.length - 1]; } const res = await getUnavailableSeatNumbers(poolId, boxData.number, startSeatNumber, endSeatNumber); if (res) { const nums: number[] = []; if (res.usedSeatNumbers) { const map: Record = {}; res.usedSeatNumbers.forEach((item: { seatNumber: number; level: string }) => { map[item.seatNumber] = item.level; nums.push(item.seatNumber); }); setUseMap(map); } if (res.applyedSeatNumbers) { const map: Record = {}; res.applyedSeatNumbers.forEach((item: number) => { map[item] = item; nums.push(item); }); setLockMap(map); } // 移除已被占用的选择 if (nums.length > 0) { setCheckMap((prev) => { const newMap = { ...prev }; nums.forEach((n) => delete newMap[n]); return newMap; }); } } } catch (error) { console.error('获取座位号失败:', error); } }, [poolId]); useImperativeHandle(ref, () => ({ show: (boxData: BoxData) => { setBox(boxData); setCheckMap({}); setUseMap({}); setLockMap({}); setVisible(true); handleTab(boxData); // 延迟获取数据,等待标签页初始化完成 setTimeout(() => { const totalData: number[] = []; for (let i = 1; i <= boxData.quantity; i++) { totalData.push(i); } const firstTabData = totalData.slice(0, 100); getData(boxData, { data: firstTabData }); }, 100); }, close: () => { setVisible(false); setBox(null); setTabs([]); setCurrentTab(null); setCheckMap({}); setUseMap({}); setLockMap({}); }, })); const close = () => { setVisible(false); }; // 切换标签页 const clickTab = (tab: { title: string; value: number; data: number[] }) => { setCurrentTab(tab); if (box) getData(box, tab); }; // 选择/取消选择号码 const choose = (item: number) => { if (useMap[item] || lockMap[item]) return; setCheckMap((prev) => { const newMap = { ...prev }; if (newMap[item]) { delete newMap[item]; } else { if (Object.keys(newMap).length >= 50) { Alert.alert('提示', '最多不超过50发'); return prev; } newMap[item] = item; } return newMap; }); }; // 删除已选择的号码 const deleteChoose = (item: number) => { setCheckMap((prev) => { const newMap = { ...prev }; delete newMap[item]; return newMap; }); }; // 预览订单 const preview = async () => { if (!box) return; if (chooseData.length <= 0) { Alert.alert('提示', '请选择号码'); return; } setLoading(true); try { const res = await previewOrder(poolId, chooseData.length, box.number, chooseData); if (res) { if (res.duplicateSeatNumbers && res.duplicateSeatNumbers.length > 0) { Alert.alert('提示', `${res.duplicateSeatNumbers.join(',')}号被占用`); // 移除被占用的号码 setCheckMap((prev) => { const newMap = { ...prev }; res.duplicateSeatNumbers.forEach((n: number) => delete newMap[n]); return newMap; }); getData(box); return; } onPay({ preview: res, seatNumbers: chooseData, boxNumber: box.number }); close(); } } catch (error: any) { Alert.alert('提示', error?.message || '获取订单信息失败'); } finally { setLoading(false); } }; return ( {/* 标题 */} 换盒 × {/* 标签页 */} {tabs.length > 1 && ( {tabs.map((tab) => ( clickTab(tab)} > {tab.title} ))} )} {/* 号码网格 */} {currentTab?.data.map((item) => { const isChecked = !!checkMap[item]; const isUsed = !!useMap[item]; const isLocked = !!lockMap[item]; return ( choose(item)} disabled={isUsed || isLocked} > {isUsed ? ( {item}号 {LEVEL_MAP[useMap[item]]?.title} ) : ( {item}号 )} {isChecked && ( )} {isLocked && ( 🔒 )} ); })} {/* 已选择的号码 */} {chooseData.map((item) => ( {item}号 deleteChoose(item)}> × ))} {/* 确认按钮 */} {loading ? ( ) : ( <> 确定选择 已选择({chooseData.length})发 )} ); } ); const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end', }, mask: { flex: 1 }, container: { borderTopLeftRadius: 15, borderTopRightRadius: 15, paddingTop: 15, paddingBottom: 34, maxHeight: '80%', }, titleSection: { alignItems: 'center', paddingVertical: 15, position: 'relative', }, title: { fontSize: 16, fontWeight: 'bold', color: '#fff', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 2, }, closeBtn: { position: 'absolute', right: 15, top: 10, width: 24, height: 24, backgroundColor: '#ebebeb', borderRadius: 12, justifyContent: 'center', alignItems: 'center', }, closeText: { fontSize: 18, color: '#a2a2a2', marginTop: -2 }, tabsScroll: { maxHeight: 40, marginHorizontal: 10, marginBottom: 10, }, tabs: { flexDirection: 'row', }, tabItem: { paddingHorizontal: 12, paddingVertical: 6, backgroundColor: 'rgba(255,255,255,0.2)', borderRadius: 15, marginRight: 8, }, tabItemActive: { backgroundColor: '#FFC900', }, tabText: { fontSize: 12, color: '#fff', }, tabTextActive: { color: '#000', fontWeight: 'bold', }, gridScroll: { height: 350, backgroundColor: '#fff', marginHorizontal: 10, borderRadius: 10, }, grid: { flexDirection: 'row', flexWrap: 'wrap', padding: 10, }, gridItem: { width: ITEM_WIDTH, height: ITEM_WIDTH / 2, backgroundColor: '#FFC900', borderWidth: 3, borderColor: '#000', borderRadius: 4, justifyContent: 'center', alignItems: 'center', margin: 4, position: 'relative', }, gridItemActive: {}, gridItemUsed: { backgroundColor: '#e8e8e8', }, gridItemLocked: { backgroundColor: 'rgba(98, 99, 115, 0.3)', borderWidth: 0, }, gridItemText: { fontSize: 12, color: '#000', fontWeight: 'bold', }, gridItemTextLocked: { color: 'rgba(255,255,255,0.3)', }, usedContent: { alignItems: 'center', }, usedNum: { fontSize: 10, color: '#000', opacity: 0.5, }, levelTitle: { fontSize: 10, fontWeight: 'bold', }, checkIcon: { position: 'absolute', right: 0, bottom: 0, width: 16, height: 14, backgroundColor: '#000', borderTopLeftRadius: 6, justifyContent: 'center', alignItems: 'center', }, checkIconText: { fontSize: 10, color: '#FFC900', }, lockIcon: { position: 'absolute', right: 0, bottom: 0, width: 16, height: 14, justifyContent: 'center', alignItems: 'center', }, lockIconText: { fontSize: 10, }, selectedSection: { height: 40, marginHorizontal: 10, marginTop: 10, }, selectedScroll: { flex: 1, }, selectedItem: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FFC900', borderWidth: 3, borderColor: '#000', borderRadius: 4, paddingHorizontal: 10, paddingVertical: 4, marginRight: 8, height: 32, }, selectedText: { fontSize: 12, color: '#000', fontWeight: 'bold', }, selectedClose: { marginLeft: 6, width: 16, height: 16, backgroundColor: 'rgba(255,255,255,0.3)', borderRadius: 8, justifyContent: 'center', alignItems: 'center', }, selectedCloseText: { fontSize: 12, color: '#000', }, btnBox: { paddingHorizontal: 20, paddingTop: 15, }, submitBtn: { backgroundColor: '#ff9600', height: 50, borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, submitBtnDisabled: { opacity: 0.6, }, submitText: { fontSize: 16, fontWeight: 'bold', color: '#fff', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1, }, submitSubText: { fontSize: 10, color: '#fff', marginTop: 2, }, });