| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- import { Image } from 'expo-image';
- import React, { forwardRef, useImperativeHandle, useMemo, useState } from 'react';
- import { Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
- interface PrizeItem {
- id: string;
- name: string;
- cover: string;
- level: string;
- price?: number;
- probability?: number;
- }
- interface GoodsItem {
- id: string;
- name: string;
- cover: string;
- level: string;
- price?: number;
- probability?: number;
- }
- interface DetailsPopupProps {}
- export interface DetailsPopupRef {
- open: (item: GoodsItem, prizes: PrizeItem[]) => void;
- close: () => void;
- }
- export const DetailsPopup = forwardRef<DetailsPopupRef, DetailsPopupProps>((_, ref) => {
- const [visible, setVisible] = useState(false);
- const [info, setInfo] = useState<GoodsItem | null>(null);
- const [prizes, setPrizes] = useState<PrizeItem[]>([]);
- // 按等级分类奖品
- const { cellAList, cellBList, cellCList, cellDList, cellAprobability, cellBprobability, cellCprobability, cellDprobability } = useMemo(() => {
- const result = {
- cellAList: [] as PrizeItem[],
- cellBList: [] as PrizeItem[],
- cellCList: [] as PrizeItem[],
- cellDList: [] as PrizeItem[],
- cellAprobability: 0,
- cellBprobability: 0,
- cellCprobability: 0,
- cellDprobability: 0,
- };
- prizes.forEach((item) => {
- switch (item.level) {
- case 'A':
- result.cellAList.push(item);
- result.cellAprobability += item.probability || 0;
- break;
- case 'B':
- result.cellBList.push(item);
- result.cellBprobability += item.probability || 0;
- break;
- case 'C':
- result.cellCList.push(item);
- result.cellCprobability += item.probability || 0;
- break;
- case 'D':
- result.cellDList.push(item);
- result.cellDprobability += item.probability || 0;
- break;
- }
- });
- return result;
- }, [prizes]);
- useImperativeHandle(ref, () => ({
- open: (item: GoodsItem, prizeList: PrizeItem[]) => {
- setInfo(item);
- setPrizes(prizeList);
- setVisible(true);
- },
- close: () => setVisible(false),
- }));
- const isGuaranteed = info?.level === 'NESTED_BOX_GUARANTEED';
- return (
- <Modal visible={visible} transparent animationType="fade" onRequestClose={() => setVisible(false)}>
- <View style={styles.overlay}>
- <View style={styles.container}>
- {/* 顶部标题区 */}
- <View style={styles.header}>
- <Text style={styles.title} numberOfLines={1}>{info?.name}</Text>
- <View style={[styles.levelBadge, isGuaranteed ? styles.levelD : styles.levelAll]}>
- <Text style={styles.levelText}>{isGuaranteed ? 'D赏' : '全局赏'}</Text>
- </View>
- </View>
- {/* 主图展示区 */}
- <View style={styles.mainContent}>
- <View style={styles.productImage}>
- <Image source={{ uri: info?.cover }} style={styles.image} contentFit="cover" />
- </View>
- <View style={styles.priceInfo}>
- <View style={styles.priceItem}>
- <Text style={styles.label}>指导价:</Text>
- <Text style={styles.value}>¥ {info?.price || 0}</Text>
- </View>
- <View style={styles.priceItem}>
- <Text style={styles.label}>概率:</Text>
- <Text style={styles.value}>{((info?.probability || 0) * 100).toFixed(2)}%</Text>
- </View>
- <Text style={styles.tips}>1~3抽完赠机送</Text>
- </View>
- </View>
- {/* 奖品列表 - 仅非保底款显示 */}
- {!isGuaranteed && (
- <ScrollView style={styles.prizeScroll} showsVerticalScrollIndicator={false}>
- {/* A赏 */}
- {cellAList.length > 0 && (
- <View style={styles.prizeSection}>
- <View style={styles.sectionHeader}>
- <View style={[styles.levelTag, styles.levelTagA]}>
- <Text style={styles.levelTagText}>A赏</Text>
- </View>
- <Text style={styles.probability}>概率{(cellAprobability * 100).toFixed(2)}%</Text>
- </View>
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.prizeList}>
- {cellAList.map((item, index) => (
- <View key={item.id || index} style={styles.prizeItem}>
- <View style={styles.prizeImageBox}>
- <Image source={{ uri: item.cover }} style={styles.prizeImage} contentFit="cover" />
- <View style={styles.itemPrice}>
- <Text style={styles.itemPriceText}>¥{item.price || 0}</Text>
- </View>
- </View>
- <Text style={styles.prizeName} numberOfLines={1}>{item.name}</Text>
- </View>
- ))}
- </ScrollView>
- </View>
- )}
- {/* B赏 */}
- {cellBList.length > 0 && (
- <View style={styles.prizeSection}>
- <View style={styles.sectionHeader}>
- <View style={[styles.levelTag, styles.levelTagB]}>
- <Text style={styles.levelTagText}>B赏</Text>
- </View>
- <Text style={styles.probability}>概率{(cellBprobability * 100).toFixed(2)}%</Text>
- </View>
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.prizeList}>
- {cellBList.map((item, index) => (
- <View key={item.id || index} style={styles.prizeItem}>
- <View style={styles.prizeImageBox}>
- <Image source={{ uri: item.cover }} style={styles.prizeImage} contentFit="cover" />
- <View style={styles.itemPrice}>
- <Text style={styles.itemPriceText}>¥{item.price || 0}</Text>
- </View>
- </View>
- <Text style={styles.prizeName} numberOfLines={1}>{item.name}</Text>
- </View>
- ))}
- </ScrollView>
- </View>
- )}
- {/* C赏 */}
- {cellCList.length > 0 && (
- <View style={styles.prizeSection}>
- <View style={styles.sectionHeader}>
- <View style={[styles.levelTag, styles.levelTagB]}>
- <Text style={styles.levelTagText}>C赏</Text>
- </View>
- <Text style={styles.probability}>概率{(cellCprobability * 100).toFixed(2)}%</Text>
- </View>
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.prizeList}>
- {cellCList.map((item, index) => (
- <View key={item.id || index} style={styles.prizeItem}>
- <View style={styles.prizeImageBox}>
- <Image source={{ uri: item.cover }} style={styles.prizeImage} contentFit="cover" />
- <View style={styles.itemPrice}>
- <Text style={styles.itemPriceText}>¥{item.price || 0}</Text>
- </View>
- </View>
- <Text style={styles.prizeName} numberOfLines={1}>{item.name}</Text>
- </View>
- ))}
- </ScrollView>
- </View>
- )}
- {/* D赏 */}
- {cellDList.length > 0 && (
- <View style={styles.prizeSection}>
- <View style={styles.sectionHeader}>
- <View style={[styles.levelTag, styles.levelTagB]}>
- <Text style={styles.levelTagText}>D赏</Text>
- </View>
- <Text style={styles.probability}>概率{(cellDprobability * 100).toFixed(2)}%</Text>
- </View>
- <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.prizeList}>
- {cellDList.map((item, index) => (
- <View key={item.id || index} style={styles.prizeItem}>
- <View style={styles.prizeImageBox}>
- <Image source={{ uri: item.cover }} style={styles.prizeImage} contentFit="cover" />
- <View style={styles.itemPrice}>
- <Text style={styles.itemPriceText}>¥{item.price || 0}</Text>
- </View>
- </View>
- <Text style={styles.prizeName} numberOfLines={1}>{item.name}</Text>
- </View>
- ))}
- </ScrollView>
- </View>
- )}
- </ScrollView>
- )}
- {/* 关闭按钮 */}
- <TouchableOpacity style={styles.closeBtn} onPress={() => setVisible(false)}>
- <Text style={styles.closeBtnText}>×</Text>
- </TouchableOpacity>
- </View>
- </View>
- </Modal>
- );
- });
- const styles = StyleSheet.create({
- overlay: {
- flex: 1,
- backgroundColor: 'rgba(0,0,0,0.5)',
- justifyContent: 'center',
- alignItems: 'center',
- padding: 20,
- },
- container: {
- width: '100%',
- maxWidth: 310,
- backgroundColor: '#ffb300',
- borderRadius: 10,
- overflow: 'hidden',
- maxHeight: '80%',
- },
- header: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- padding: 15,
- },
- title: {
- fontSize: 18,
- fontWeight: 'bold',
- color: '#000',
- flex: 1,
- marginRight: 10,
- },
- levelBadge: {
- paddingHorizontal: 12,
- paddingVertical: 4,
- borderRadius: 2,
- },
- levelD: {
- backgroundColor: '#6340FF',
- borderWidth: 1,
- borderColor: '#A2BBFF',
- },
- levelAll: {
- backgroundColor: '#A3E100',
- borderWidth: 1,
- borderColor: '#EAFFB1',
- },
- levelText: {
- fontSize: 12,
- fontWeight: 'bold',
- color: '#fff',
- },
- mainContent: {
- backgroundColor: '#fff',
- padding: 15,
- flexDirection: 'row',
- },
- productImage: {
- width: 100,
- height: 100,
- borderRadius: 6,
- borderWidth: 2,
- borderColor: '#E0E0E0',
- overflow: 'hidden',
- },
- image: {
- width: '100%',
- height: '100%',
- },
- priceInfo: {
- flex: 1,
- paddingLeft: 15,
- justifyContent: 'center',
- },
- priceItem: {
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 8,
- },
- label: {
- fontSize: 14,
- color: '#333',
- },
- value: {
- fontSize: 14,
- color: '#333',
- fontWeight: 'bold',
- },
- tips: {
- fontSize: 13,
- color: '#FF9500',
- marginTop: 4,
- },
- prizeScroll: {
- maxHeight: 325,
- backgroundColor: '#fff',
- },
- prizeSection: {
- marginTop: 15,
- },
- sectionHeader: {
- flexDirection: 'row',
- alignItems: 'center',
- paddingHorizontal: 15,
- marginBottom: 10,
- },
- levelTag: {
- paddingHorizontal: 8,
- paddingVertical: 3,
- borderRadius: 3,
- borderWidth: 1.5,
- borderColor: '#000',
- marginRight: 10,
- },
- levelTagA: {
- backgroundColor: '#FFD700',
- },
- levelTagB: {
- backgroundColor: '#FFA500',
- },
- levelTagText: {
- fontSize: 14,
- fontWeight: 'bold',
- color: '#000',
- },
- probability: {
- fontSize: 14,
- color: '#000',
- },
- prizeList: {
- paddingHorizontal: 15,
- paddingBottom: 15,
- },
- prizeItem: {
- marginRight: 10,
- alignItems: 'center',
- },
- prizeImageBox: {
- width: 80,
- height: 80,
- borderRadius: 6,
- borderWidth: 2,
- borderColor: '#000',
- overflow: 'hidden',
- position: 'relative',
- },
- prizeImage: {
- width: '100%',
- height: '100%',
- },
- itemPrice: {
- position: 'absolute',
- bottom: 0,
- left: 0,
- right: 0,
- backgroundColor: 'rgba(0,0,0,0.5)',
- paddingVertical: 2,
- },
- itemPriceText: {
- fontSize: 10,
- color: '#fff',
- textAlign: 'center',
- },
- prizeName: {
- marginTop: 5,
- fontSize: 12,
- color: '#000',
- textAlign: 'center',
- width: 80,
- },
- closeBtn: {
- position: 'absolute',
- right: 10,
- top: 10,
- width: 24,
- height: 24,
- justifyContent: 'center',
- alignItems: 'center',
- },
- closeBtnText: {
- fontSize: 20,
- color: '#333',
- fontWeight: 'bold',
- },
- });
|