import { Images } from '@/constants/images'; import Service from '@/services/weal'; import { useRouter } from 'expo-router'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Alert, Dimensions, Image, ImageBackground, ScrollView, StatusBar, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { WishMaterialModal, WishMaterialModalRef } from './components/WishMaterialModal'; import { WishRecordModal, WishRecordModalRef } from './components/WishRecordModal'; import { WishRuleModal, WishRuleModalRef } from './components/WishRuleModal'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); const CDN_BASE = 'https://cdn.acetoys.cn/kai_xin_ma_te/supermart'; const wishImages = { bg: `${CDN_BASE}/common/wishBg.png`, title: `${CDN_BASE}/welfare/wishTitle.png`, rule: `${CDN_BASE}/welfare/wishRule.png`, addBg: `${CDN_BASE}/welfare/addBg.png`, addLiBg: `${CDN_BASE}/welfare/addLiBg.png`, addClose: `${CDN_BASE}/welfare/addClose.png`, addSectionBg: `${CDN_BASE}/welfare/toys/addSectionBg.png`, add: `${CDN_BASE}/welfare/add.png`, left: `${CDN_BASE}/box/detail/left.png`, right: `${CDN_BASE}/box/detail/right.png`, progressBar: `${CDN_BASE}/welfare/toys/wishProgressBar.png`, }; interface WishItem { id: string; name: string; spu: { cover: string }; quantity: number; completeQuantity: number; } interface GoodsItem { id: string; spu: { cover: string }; magicAmount: number; } export default function WishScreen() { const router = useRouter(); const insets = useSafeAreaInsets(); const [tableData, setTableData] = useState([]); const [active, setActive] = useState(0); const [goodsList, setGoodsList] = useState([]); const [progress, setProgress] = useState(0); const [magicAmount, setMagicAmount] = useState(0); // Modals const ruleRef = useRef(null); const recordRef = useRef(null); const materialRef = useRef(null); const loadData = useCallback(async () => { try { const data = await Service.getWishList(); // Ensure data is array const list = Array.isArray(data) ? data : (data?.records || []); if (list && list.length > 0) { setTableData(list); } } catch (error) { console.error('加载祈愿数据失败:', error); } }, []); useEffect(() => { loadData(); }, [loadData]); // Update progress when active item changes or goodsList changes useEffect(() => { const updateProgress = async () => { if (tableData.length > 0 && tableData[active]) { const current = tableData[active]; if (goodsList.length === 0) { setProgress(0); setMagicAmount(0); } else { // Call preview to get progress const inventoryIds = goodsList.map(g => g.id); try { const res = await Service.wishPreview({ substituteGoodsId: current.id, inventoryIds }); if (res) { let p = (res.progress || 0) * 100; if (p > 100) p = 100; setProgress(p); setMagicAmount(res.magicAmount || 0); } } catch (e) { console.error(e); } } } else { setProgress(0); setMagicAmount(0); } }; updateProgress(); }, [tableData, active, goodsList]); const handlePrev = () => { if (active > 0) { setActive(active - 1); setGoodsList([]); } }; const handleNext = () => { if (active < tableData.length - 1) { setActive(active + 1); setGoodsList([]); } }; const currentItem = tableData[active]; const serverRemaining = currentItem ? currentItem.quantity - currentItem.completeQuantity : 0; // Visual remaining doesn't update until submit in this flow const remaining = serverRemaining; const removeGoods = (item: GoodsItem) => { setGoodsList(goodsList.filter((g) => g.id !== item.id)); }; const openMaterialModal = () => { if (!currentItem) return; materialRef.current?.show((selected) => { // Add selected items to goodsList setGoodsList(prev => { const newIds = new Set(prev.map(i => i.id)); const toAdd = selected.filter(i => !newIds.has(i.id)); return [...prev, ...toAdd]; }); }); }; const submitWish = async () => { try { const inventoryIds = goodsList.map(g => g.id); console.log('Submitting Wish:', { substituteGoodsId: currentItem.id, inventoryIds }); const res = await Service.wishSubmit({ substituteGoodsId: currentItem.id, inventoryIds }); console.log('Submit Res:', res); // Fix: Use loose equality since backend returns string "0" if (res.code == 0 || res.success) { Alert.alert('成功', '点亮成功!'); setGoodsList([]); loadData(); } else { Alert.alert('提交失败', `Code: ${res.code}, Msg: ${res.msg}`); } } catch (error) { console.error('LightUp Error:', error); Alert.alert('执行异常', String(error)); } }; const handleLightUp = async () => { if (!currentItem) return; if (progress < 100) { if (goodsList.length === 0) { Alert.alert('提示', '请先添加材料'); return; } Alert.alert('提示', '进度未满100%,无法点亮'); return; } Alert.alert( '确认点亮', '是否确认消耗所选材料进行点亮?', [ { text: '取消', style: 'cancel' }, { text: '确认', onPress: () => { submitWish(); } } ] ); }; return ( {/* 头部导航 */} router.back()}> 祈愿 {/* 规则按钮 */} ruleRef.current?.show()}> {/* 标题图片 */} {/* 卡片轮播区域 */} {/* 左箭头 */} {active > 0 && ( )} {/* 卡片 */} {currentItem ? ( {/* 仅剩标签 */} 仅剩:{remaining} {currentItem.name} ) : ( 暂无祈愿商品 )} {/* 右箭头 */} {active < tableData.length - 1 && ( )} {/* 指示点 */} {tableData.length > 1 && ( {tableData.map((_, i) => ( ))} )} {/* 进度条区域 */} 进度: {/* Show Magic Return if 100% and magicAmount > 0 */} {progress >= 100 && magicAmount > 0 && ( 返还: {magicAmount}果实 )} {progress > 0 ? progress.toFixed(1) : progress}% {/* 材料添加区域 */} 材料添加 {/* 添加按钮 */} 添加 {/* 已添加的材料列表 */} {goodsList.map((item, index) => ( removeGoods(item)}> ))} {/* 底部按钮 */} {/* Record Link (Optional, if design has it outside standard flow, adding it near bottom or top) Wait, original design had "Rule" on left, maybe "Record" is not prominently displayed or inside Rule? Let's stick to current design. */} = 100 || goodsList.length > 0 ? Images.common.butBgL : Images.common.butBgHui }} style={styles.bottomBtnBg} resizeMode="contain" > {progress >= 100 ? '点亮心愿' : (goodsList.length > 0 ? '确认添加' : '等待参与')} *本活动最终解释权归本平台所有 ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#1a1a2e' }, background: { flex: 1 }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 10, paddingBottom: 10, position: 'absolute', top: 0, left: 0, right: 0, zIndex: 100, }, backBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center' }, backText: { color: '#fff', fontSize: 20 }, title: { color: '#fff', fontSize: 16, fontWeight: 'bold' }, placeholder: { width: 40 }, scrollView: { flex: 1 }, // 规则按钮 ruleBtn: { position: 'absolute', left: 0, zIndex: 99 }, ruleBtnImg: { width: 39, height: 20 }, // 标题 titleBox: { alignItems: 'center', marginBottom: 10 }, titleImg: { width: 288, height: 86 }, // 轮播区域 swiperBox: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingHorizontal: 20, minHeight: 350, position: 'relative', }, prevBtn: { position: 'absolute', left: 15, zIndex: 10 }, nextBtn: { position: 'absolute', right: 15, zIndex: 10 }, arrowImg: { width: 51, height: 49 }, // 卡片 cardBox: { alignItems: 'center' }, card: { alignItems: 'center' }, imgBox: { width: 215, height: 284, backgroundColor: '#FFF7D8', borderWidth: 4.5, borderColor: '#000', justifyContent: 'center', alignItems: 'center', position: 'relative', }, spuImage: { width: 200, height: 260 }, emptyText: { color: '#999', fontSize: 14 }, remainingBox: { position: 'absolute', bottom: '15%', backgroundColor: '#fff', borderWidth: 3, borderColor: '#000', paddingHorizontal: 15, paddingVertical: 5, }, remainingText: { fontSize: 13, fontWeight: 'bold', color: '#000' }, cardName: { color: '#D0D0D0', fontSize: 12, marginTop: 13, textAlign: 'center', maxWidth: 200, }, // 指示点 dotsBox: { flexDirection: 'row', justifyContent: 'center', marginTop: 10 }, dot: { width: 8, height: 8, borderRadius: 4, backgroundColor: '#666', marginHorizontal: 3 }, dotActive: { backgroundColor: '#fff' }, // 进度条 progressSection: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 5, paddingHorizontal: 20, position: 'relative', zIndex: 20, }, progressLabelBox: { width: 40 }, progressLabel: { color: '#fff', fontSize: 12 }, magicBubble: { position: 'absolute', right: 0, top: -24, zIndex: 10, backgroundColor: '#f44336', borderRadius: 4, paddingHorizontal: 8, paddingVertical: 3, }, magicText: { color: '#fff', fontSize: 10 }, progressBar: { flex: 1, height: 14, backgroundColor: '#FFEABE', borderWidth: 2, borderColor: '#000', marginHorizontal: 5, position: 'relative', }, progressFill: { height: '100%', backgroundColor: '#FFAD00', borderRightWidth: 2, borderRightColor: '#000', }, progressText: { color: '#fff', fontSize: 12, width: 40, textAlign: 'right' }, // 材料添加区域 addSection: { width: SCREEN_WIDTH - 20, height: 124, marginHorizontal: 10, marginTop: 10, paddingTop: 10, }, addTitle: { color: '#fff', fontSize: 14, textAlign: 'center', fontWeight: '400', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1, marginBottom: 7, }, addMain: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 25, }, addBtn: { marginRight: 10 }, addBtnBg: { width: 42, height: 42, justifyContent: 'center', alignItems: 'center', paddingTop: 5, }, addIcon: { width: 16, height: 16 }, addBtnText: { fontSize: 12, color: '#000', fontWeight: '500' }, addCenter: { flex: 1, height: 60, backgroundColor: '#FFFBEA', paddingTop: 7, paddingLeft: 20, }, addScrollContent: { paddingRight: 10 }, addItem: { position: 'relative', marginRight: 10 }, addItemBg: { width: 42, height: 42, justifyContent: 'center', alignItems: 'center' }, addItemImg: { width: '80%', height: '80%' }, closeBtn: { position: 'absolute', right: 0, top: 0, width: 13, height: 13 }, closeIcon: { width: '100%', height: '100%' }, // 底部按钮 bottomContainer: { alignItems: 'center', marginTop: -10 }, bottomBtn: { alignItems: 'center' }, bottomBtnBg: { width: 160, height: 60, justifyContent: 'center', alignItems: 'center', paddingTop: 4, }, bottomBtnText: { color: '#fff', fontSize: 15, fontWeight: 'bold', textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1, }, bottomTip: { color: 'rgba(255,255,255,0.67)', fontSize: 10, textAlign: 'center', marginTop: 5, }, });