|
|
@@ -1,9 +1,8 @@
|
|
|
-import { Image } from 'expo-image';
|
|
|
import { useLocalSearchParams, useRouter } from 'expo-router';
|
|
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
import {
|
|
|
ActivityIndicator,
|
|
|
- Animated,
|
|
|
+ Alert,
|
|
|
Dimensions,
|
|
|
ImageBackground,
|
|
|
ScrollView,
|
|
|
@@ -11,707 +10,380 @@ import {
|
|
|
StyleSheet,
|
|
|
Text,
|
|
|
TouchableOpacity,
|
|
|
- View,
|
|
|
+ View
|
|
|
} from 'react-native';
|
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
|
|
|
+import { RecordModal, RecordModalRef } from '@/app/award-detail/components/RecordModal';
|
|
|
+import { BoxSelectionModal, BoxSelectionModalRef } from '@/components/award-detail-yfs/BoxSelectionModal';
|
|
|
+import FirstLast from '@/components/award-detail-yfs/FirstLast';
|
|
|
+import { NumSelectionModal, NumSelectionModalRef } from '@/components/award-detail-yfs/NumSelectionModal';
|
|
|
+import ProductListYfs from '@/components/award-detail-yfs/ProductListYfs';
|
|
|
+import ProductSwiper from '@/components/award-detail-yfs/ProductSwiper';
|
|
|
+import PurchaseBar from '@/components/award-detail-yfs/PurchaseBar';
|
|
|
+import { RuleModal, RuleModalRef } from '@/components/award-detail-yfs/RuleModal';
|
|
|
import { Images } from '@/constants/images';
|
|
|
-import { useAuth } from '@/contexts/AuthContext';
|
|
|
-import {
|
|
|
- getBoxDetail,
|
|
|
- getNextBox,
|
|
|
- getPoolDetail,
|
|
|
- getPreBox,
|
|
|
- lockBox,
|
|
|
- poolIn,
|
|
|
- poolOut,
|
|
|
- previewOrder,
|
|
|
- unlockBox,
|
|
|
-} from '@/services/award';
|
|
|
-
|
|
|
-import { CheckoutModal } from '../award-detail/components/CheckoutModal';
|
|
|
-import { RecordModal } from '../award-detail/components/RecordModal';
|
|
|
-import { RuleModal } from '../award-detail/components/RuleModal';
|
|
|
-import { BoxChooseModal } from './components/BoxChooseModal';
|
|
|
-import { NumChooseModal } from './components/NumChooseModal';
|
|
|
-import { ProductListYfs } from './components/ProductListYfs';
|
|
|
-
|
|
|
-const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
|
|
-
|
|
|
-interface PoolData {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- cover: string;
|
|
|
- price: number;
|
|
|
- specialPrice?: number;
|
|
|
- specialPriceFive?: number;
|
|
|
- restrictionQuantity?: number;
|
|
|
- luckGoodsList: ProductItem[];
|
|
|
- luckGoodsLevelProbabilityList?: any[];
|
|
|
-}
|
|
|
+import { getBoxDetail, getNextBox, getPoolDetail, getPreBox, poolIn, poolOut } from '@/services/award';
|
|
|
+import { CheckoutModal, CheckoutModalRef } from '../award-detail/components/CheckoutModal';
|
|
|
|
|
|
-interface ProductItem {
|
|
|
- id: string;
|
|
|
- name: string;
|
|
|
- cover: string;
|
|
|
- level: string;
|
|
|
- probability: number;
|
|
|
- price?: number;
|
|
|
- quantity?: number;
|
|
|
-}
|
|
|
+const { width } = Dimensions.get('window');
|
|
|
|
|
|
-interface BoxData {
|
|
|
- number: string;
|
|
|
- quantity: number;
|
|
|
- leftQuantity: number;
|
|
|
- leftQuantityA: number;
|
|
|
- leftQuantityB: number;
|
|
|
- leftQuantityC: number;
|
|
|
- leftQuantityD: number;
|
|
|
- lastNumber: number;
|
|
|
- lock?: { locker: string; leftTime: number };
|
|
|
- usedStat?: Record<string, { spuId: string; quantity: number }>;
|
|
|
- prizeList?: any[];
|
|
|
-}
|
|
|
+const styles = StyleSheet.create({
|
|
|
+ container: { flex: 1, backgroundColor: '#1a1a2e' },
|
|
|
+ background: { flex: 1 },
|
|
|
+ loading: { flex: 1, backgroundColor: '#1a1a2e', justifyContent: 'center', alignItems: 'center' },
|
|
|
+ nav: {
|
|
|
+ flexDirection: 'row',
|
|
|
+ alignItems: 'center',
|
|
|
+ justifyContent: 'space-between',
|
|
|
+ paddingHorizontal: 15,
|
|
|
+ height: 90,
|
|
|
+ zIndex: 100,
|
|
|
+ position: 'absolute',
|
|
|
+ top: 0,
|
|
|
+ left: 0,
|
|
|
+ right: 0,
|
|
|
+ },
|
|
|
+ backBtn: { width: 40 },
|
|
|
+ backText: { color: '#fff', fontSize: 24 },
|
|
|
+ navTitle: { color: '#fff', fontSize: 16, fontWeight: 'bold', maxWidth: '60%' },
|
|
|
+ placeholder: { width: 40 },
|
|
|
+ scrollView: { flex: 1 },
|
|
|
+
|
|
|
+ detailWrapper: {
|
|
|
+ position: 'relative',
|
|
|
+ minHeight: 800,
|
|
|
+ paddingBottom: 100,
|
|
|
+ },
|
|
|
+ mainGoodsSection: {
|
|
|
+ width: '100%',
|
|
|
+ height: 504,
|
|
|
+ position: 'relative',
|
|
|
+ zIndex: 1,
|
|
|
+ },
|
|
|
+ mainGoodsSectionBtext: {
|
|
|
+ width: '100%',
|
|
|
+ height: 74,
|
|
|
+ marginTop: -10,
|
|
|
+ zIndex: 2,
|
|
|
+ },
|
|
|
+ positionBgLeftBg: {
|
|
|
+ position: 'absolute',
|
|
|
+ left: 0,
|
|
|
+ top: 225,
|
|
|
+ width: 32,
|
|
|
+ height: 188,
|
|
|
+ zIndex: 2,
|
|
|
+ },
|
|
|
+ positionBgRightBg: {
|
|
|
+ position: 'absolute',
|
|
|
+ right: 0,
|
|
|
+ top: 225,
|
|
|
+ width: 32,
|
|
|
+ height: 188,
|
|
|
+ zIndex: 2,
|
|
|
+ },
|
|
|
+ positionBut: {
|
|
|
+ position: 'absolute',
|
|
|
+ zIndex: 10,
|
|
|
+ width: 35,
|
|
|
+ height: 34,
|
|
|
+ },
|
|
|
+ btnBg: {
|
|
|
+ width: '100%',
|
|
|
+ height: '100%',
|
|
|
+ justifyContent: 'center',
|
|
|
+ alignItems: 'center',
|
|
|
+ },
|
|
|
+ slantedL: {
|
|
|
+ color: '#fff',
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ transform: [{ rotate: '14deg' }],
|
|
|
+ marginTop: -2,
|
|
|
+ textShadowColor: '#000',
|
|
|
+ textShadowOffset: { width: 1, height: 1 },
|
|
|
+ textShadowRadius: 1,
|
|
|
+ },
|
|
|
+ slantedR: {
|
|
|
+ color: '#fff',
|
|
|
+ fontSize: 12,
|
|
|
+ fontWeight: 'bold',
|
|
|
+ transform: [{ rotate: '-16deg' }],
|
|
|
+ marginTop: -2,
|
|
|
+ textShadowColor: '#000',
|
|
|
+ textShadowOffset: { width: 1, height: 1 },
|
|
|
+ textShadowRadius: 1,
|
|
|
+ },
|
|
|
+ positionRule: {
|
|
|
+ top: 256,
|
|
|
+ left: 0,
|
|
|
+ },
|
|
|
+ positionStore: {
|
|
|
+ top: 256,
|
|
|
+ right: 0,
|
|
|
+ },
|
|
|
+ positionRecord: {
|
|
|
+ top: 300,
|
|
|
+ left: 0,
|
|
|
+ },
|
|
|
+ positionRefresh: {
|
|
|
+ top: 345,
|
|
|
+ right: 0,
|
|
|
+ },
|
|
|
+ bottomBar: {
|
|
|
+ position: 'absolute',
|
|
|
+ bottom: 0,
|
|
|
+ left: 0,
|
|
|
+ right: 0,
|
|
|
+ zIndex: 200,
|
|
|
+ }
|
|
|
+});
|
|
|
|
|
|
export default function AwardDetailYfsScreen() {
|
|
|
const { poolId } = useLocalSearchParams<{ poolId: string }>();
|
|
|
const router = useRouter();
|
|
|
const insets = useSafeAreaInsets();
|
|
|
- const { user } = useAuth();
|
|
|
-
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
- const [data, setData] = useState<PoolData | null>(null);
|
|
|
- const [products, setProducts] = useState<ProductItem[]>([]);
|
|
|
- const [box, setBox] = useState<BoxData | null>(null);
|
|
|
- const [boxNum, setBoxNum] = useState<string>('');
|
|
|
- const [currentIndex, setCurrentIndex] = useState(0);
|
|
|
- const [leftTime, setLeftTime] = useState(0);
|
|
|
- const [probability, setProbability] = useState<any[]>([]);
|
|
|
+ const [data, setData] = useState<any>(null);
|
|
|
const [scrollTop, setScrollTop] = useState(0);
|
|
|
+ const [currentBox, setCurrentBox] = useState<any>(null);
|
|
|
|
|
|
- const checkoutRef = useRef<any>(null);
|
|
|
- const recordRef = useRef<any>(null);
|
|
|
- const ruleRef = useRef<any>(null);
|
|
|
- const numChooseRef = useRef<any>(null);
|
|
|
- const boxChooseRef = useRef<any>(null);
|
|
|
- const floatAnim = useRef(new Animated.Value(0)).current;
|
|
|
- const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
|
+ const boxRef = useRef<BoxSelectionModalRef>(null);
|
|
|
+ const numRef = useRef<NumSelectionModalRef>(null);
|
|
|
+ const checkoutRef = useRef<CheckoutModalRef>(null);
|
|
|
+ const ruleRef = useRef<RuleModalRef>(null);
|
|
|
+ const recordRef = useRef<RecordModalRef>(null);
|
|
|
|
|
|
- useEffect(() => {
|
|
|
- Animated.loop(
|
|
|
- Animated.sequence([
|
|
|
- Animated.timing(floatAnim, { toValue: 10, duration: 1500, useNativeDriver: true }),
|
|
|
- Animated.timing(floatAnim, { toValue: -10, duration: 1500, useNativeDriver: true }),
|
|
|
- ])
|
|
|
- ).start();
|
|
|
- }, []);
|
|
|
+ const getSafePoolId = () => Array.isArray(poolId) ? poolId[0] : poolId;
|
|
|
+
|
|
|
+ const loadBox = async (boxNum?: string) => {
|
|
|
+ try {
|
|
|
+ const id = getSafePoolId();
|
|
|
+ const res = await getBoxDetail(id, boxNum);
|
|
|
+ if (res) setCurrentBox(res);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Failed to load box', error);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
const loadData = useCallback(async () => {
|
|
|
if (!poolId) return;
|
|
|
setLoading(true);
|
|
|
try {
|
|
|
- const detail = await getPoolDetail(poolId);
|
|
|
- if (detail) {
|
|
|
- setData(detail);
|
|
|
- setProducts(detail.luckGoodsList || []);
|
|
|
- }
|
|
|
+ const safePoolId = getSafePoolId();
|
|
|
+ const res = await getPoolDetail(safePoolId);
|
|
|
+ setData(res);
|
|
|
+ await loadBox(); // Initial box
|
|
|
} catch (error) {
|
|
|
- console.error('加载数据失败:', error);
|
|
|
+ console.error('Failed to load detail:', error);
|
|
|
}
|
|
|
setLoading(false);
|
|
|
}, [poolId]);
|
|
|
|
|
|
- const loadBox = useCallback(
|
|
|
- async (num?: string) => {
|
|
|
- if (!poolId) return;
|
|
|
- try {
|
|
|
- const res = await getBoxDetail(poolId, num);
|
|
|
- if (res) handleBoxResult(res);
|
|
|
- } catch (error) {
|
|
|
- console.error('加载盒子失败:', error);
|
|
|
- }
|
|
|
- },
|
|
|
- [poolId]
|
|
|
- );
|
|
|
-
|
|
|
- const handleBoxResult = (res: any) => {
|
|
|
- const map: Record<string, any> = {};
|
|
|
- if (res.usedStat) {
|
|
|
- res.usedStat.forEach((item: any) => {
|
|
|
- map[item.spuId] = item;
|
|
|
- });
|
|
|
- }
|
|
|
- res.usedStat = map;
|
|
|
- setBox(res);
|
|
|
- setBoxNum(res.number);
|
|
|
- lockTimeStart(res);
|
|
|
-
|
|
|
- if (res.leftQuantity <= 0) {
|
|
|
- setProbability([
|
|
|
- { level: 'A', probability: 0 },
|
|
|
- { level: 'B', probability: 0 },
|
|
|
- { level: 'C', probability: 0 },
|
|
|
- { level: 'D', probability: 0 },
|
|
|
- ]);
|
|
|
- } else {
|
|
|
- setProbability([
|
|
|
- { level: 'A', probability: ((res.leftQuantityA / res.leftQuantity) * 100).toFixed(2) },
|
|
|
- { level: 'B', probability: ((res.leftQuantityB / res.leftQuantity) * 100).toFixed(2) },
|
|
|
- { level: 'C', probability: ((res.leftQuantityC / res.leftQuantity) * 100).toFixed(2) },
|
|
|
- { level: 'D', probability: ((res.leftQuantityD / res.leftQuantity) * 100).toFixed(2) },
|
|
|
- ]);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const lockTimeStart = (boxData: BoxData) => {
|
|
|
- lockTimeEnd();
|
|
|
- if (boxData?.lock) {
|
|
|
- setLeftTime(boxData.lock.leftTime);
|
|
|
- timerRef.current = setInterval(() => {
|
|
|
- setLeftTime((prev) => {
|
|
|
- if (prev <= 1) {
|
|
|
- lockTimeEnd();
|
|
|
- loadBox();
|
|
|
- return 0;
|
|
|
- }
|
|
|
- return prev - 1;
|
|
|
- });
|
|
|
- }, 1000);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const lockTimeEnd = () => {
|
|
|
- if (timerRef.current) {
|
|
|
- clearInterval(timerRef.current);
|
|
|
- timerRef.current = null;
|
|
|
- }
|
|
|
- setLeftTime(0);
|
|
|
- };
|
|
|
-
|
|
|
useEffect(() => {
|
|
|
- loadData();
|
|
|
- loadBox();
|
|
|
- if (poolId) poolIn(poolId);
|
|
|
- return () => {
|
|
|
- if (poolId) poolOut(poolId);
|
|
|
- lockTimeEnd();
|
|
|
- };
|
|
|
- }, [poolId]);
|
|
|
-
|
|
|
- const handlePay = async (num: number) => {
|
|
|
- if (!poolId || !data || !box) return;
|
|
|
- try {
|
|
|
- const preview = await previewOrder(poolId, num, box.number);
|
|
|
- if (preview) checkoutRef.current?.show(num, preview, box.number);
|
|
|
- } catch (error) {
|
|
|
- console.error('预览订单失败:', error);
|
|
|
+ if (poolId) {
|
|
|
+ poolIn(poolId);
|
|
|
+ return () => {
|
|
|
+ poolOut(poolId);
|
|
|
+ };
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- const handleSuccess = () => {
|
|
|
- setTimeout(() => {
|
|
|
- loadData();
|
|
|
- loadBox(boxNum);
|
|
|
- }, 500);
|
|
|
- };
|
|
|
-
|
|
|
- // 处理多盒购买(号码选择后的回调)
|
|
|
- const handleNumPay = async (params: { preview: any; seatNumbers: number[]; boxNumber: string }) => {
|
|
|
- checkoutRef.current?.show(params.seatNumbers.length, params.preview, params.boxNumber, params.seatNumbers);
|
|
|
- };
|
|
|
-
|
|
|
- // 打开号码选择弹窗
|
|
|
- const handleShowNumChoose = () => {
|
|
|
- if (!box) return;
|
|
|
- numChooseRef.current?.show({
|
|
|
- number: box.number,
|
|
|
- quantity: box.quantity || box.leftQuantity,
|
|
|
- lastNumber: box.lastNumber,
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
- // 打开换盒弹窗
|
|
|
- const handleShowBoxChoose = () => {
|
|
|
- boxChooseRef.current?.show();
|
|
|
- };
|
|
|
+ }, [poolId]);
|
|
|
|
|
|
- // 选择盒子后的回调
|
|
|
- const handleChooseBox = (boxNumber: string) => {
|
|
|
- loadBox(boxNumber);
|
|
|
+ const handleBoxSelect = (box: any) => {
|
|
|
+ setCurrentBox(box);
|
|
|
};
|
|
|
|
|
|
- const handlePreBox = async () => {
|
|
|
- if (!poolId || !boxNum || parseInt(boxNum) <= 1) return;
|
|
|
- try {
|
|
|
- const res = await getPreBox(poolId, boxNum);
|
|
|
- if (res) handleBoxResult(res);
|
|
|
- } catch (error) {
|
|
|
- console.error('获取上一个盒子失败:', error);
|
|
|
- }
|
|
|
+ const handlePrevBox = async () => {
|
|
|
+ if (!currentBox) return;
|
|
|
+ try {
|
|
|
+ const res = await getPreBox(getSafePoolId(), currentBox.number);
|
|
|
+ if (res) setCurrentBox(res);
|
|
|
+ else Alert.alert('提示', '已经是第一盒了');
|
|
|
+ } catch (e) { Alert.alert('提示', '切换失败'); }
|
|
|
};
|
|
|
|
|
|
const handleNextBox = async () => {
|
|
|
- if (!poolId || !box || parseInt(boxNum) >= box.lastNumber) return;
|
|
|
- try {
|
|
|
- const res = await getNextBox(poolId, boxNum);
|
|
|
- if (res) handleBoxResult(res);
|
|
|
- } catch (error) {
|
|
|
- console.error('获取下一个盒子失败:', error);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const handleLock = async () => {
|
|
|
- if (!poolId || !boxNum) return;
|
|
|
- try {
|
|
|
- await lockBox(poolId, boxNum);
|
|
|
- loadBox(boxNum);
|
|
|
- } catch (error) {
|
|
|
- console.error('锁定失败:', error);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const handleUnlock = async () => {
|
|
|
- if (!poolId || !boxNum) return;
|
|
|
- try {
|
|
|
- await unlockBox(poolId, boxNum);
|
|
|
- loadBox(boxNum);
|
|
|
- } catch (error) {
|
|
|
- console.error('解锁失败:', error);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- const handlePrev = () => {
|
|
|
- if (currentIndex > 0) setCurrentIndex(currentIndex - 1);
|
|
|
- };
|
|
|
- const handleNext = () => {
|
|
|
- if (currentIndex < products.length - 1) setCurrentIndex(currentIndex + 1);
|
|
|
+ if (!currentBox) return;
|
|
|
+ try {
|
|
|
+ const res = await getNextBox(getSafePoolId(), currentBox.number);
|
|
|
+ if (res) setCurrentBox(res);
|
|
|
+ else Alert.alert('提示', '已经是最后一盒了');
|
|
|
+ } catch (e) { Alert.alert('提示', '切换失败'); }
|
|
|
};
|
|
|
|
|
|
- const getLevelName = (level: string) => {
|
|
|
- const map: Record<string, string> = { A: '超神款', B: '欧皇款', C: '隐藏款', D: '普通款' };
|
|
|
- return map[level] || level;
|
|
|
+ const handlePay = ({ previewRes, chooseNum, boxNum }: any) => {
|
|
|
+ checkoutRef.current?.show(chooseNum.length, previewRes, boxNum, chooseNum);
|
|
|
};
|
|
|
|
|
|
- const getLeftNum = (item: ProductItem) => {
|
|
|
- if (!box?.usedStat || !box.usedStat[item.id]?.quantity) {
|
|
|
- return item.quantity || 1;
|
|
|
- }
|
|
|
- return (item.quantity || 1) - box.usedStat[item.id].quantity;
|
|
|
- };
|
|
|
-
|
|
|
- const getProbability = (item: ProductItem) => {
|
|
|
- if (!box || box.leftQuantity <= 0) return '0';
|
|
|
- const leftNum = getLeftNum(item);
|
|
|
- return ((leftNum / box.leftQuantity) * 100).toFixed(2);
|
|
|
- };
|
|
|
+ useEffect(() => {
|
|
|
+ loadData();
|
|
|
+ }, [loadData]);
|
|
|
|
|
|
- const leftNum = box?.lock ? (leftTime / box.lock.leftTime) * 100 : 0;
|
|
|
- const headerBg = scrollTop > 0 ? '#333' : 'transparent';
|
|
|
+ const headerBg = scrollTop > 50 ? '#333' : 'transparent';
|
|
|
|
|
|
if (loading) {
|
|
|
return (
|
|
|
- <View style={styles.loadingContainer}>
|
|
|
- <ActivityIndicator size="large" color="#fff" />
|
|
|
- </View>
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (!data) {
|
|
|
- return (
|
|
|
- <View style={styles.loadingContainer}>
|
|
|
- <Text style={styles.errorText}>奖池不存在</Text>
|
|
|
- <TouchableOpacity style={styles.backBtn2} onPress={() => router.back()}>
|
|
|
- <Text style={styles.backBtn2Text}>返回</Text>
|
|
|
- </TouchableOpacity>
|
|
|
+ <View style={styles.loading}>
|
|
|
+ <ActivityIndicator color="#fff" />
|
|
|
</View>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- const currentProduct = products[currentIndex];
|
|
|
-
|
|
|
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, backgroundColor: headerBg }]}>
|
|
|
- <TouchableOpacity style={styles.backBtn} onPress={() => router.back()}>
|
|
|
- <Text style={styles.backText}>{'<'}</Text>
|
|
|
+
|
|
|
+ {/* Navigation Bar */}
|
|
|
+ <View style={[styles.nav, { paddingTop: insets.top, backgroundColor: headerBg }]}>
|
|
|
+ <TouchableOpacity onPress={() => router.back()} style={styles.backBtn}>
|
|
|
+ <Text style={styles.backText}>←</Text>
|
|
|
</TouchableOpacity>
|
|
|
- <Text style={styles.headerTitle} numberOfLines={1}>
|
|
|
- {data.name}
|
|
|
- </Text>
|
|
|
+ <Text style={styles.navTitle} numberOfLines={1}>{data?.name || '详情'}</Text>
|
|
|
<View style={styles.placeholder} />
|
|
|
</View>
|
|
|
|
|
|
<ScrollView
|
|
|
style={styles.scrollView}
|
|
|
- showsVerticalScrollIndicator={false}
|
|
|
onScroll={(e) => setScrollTop(e.nativeEvent.contentOffset.y)}
|
|
|
scrollEventThrottle={16}
|
|
|
+ showsVerticalScrollIndicator={false}
|
|
|
>
|
|
|
- {/* 主商品展示区域 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.mainGoodsSection }} style={styles.mainGoodsSection} resizeMode="cover">
|
|
|
- <View style={{ height: 72 + insets.top }} />
|
|
|
-
|
|
|
- {/* 商品轮播区域 */}
|
|
|
- <View style={styles.mainSwiper}>
|
|
|
- {currentProduct && (
|
|
|
- <>
|
|
|
- <Animated.View style={[styles.productImageBox, { transform: [{ translateY: floatAnim }] }]}>
|
|
|
- <Image source={{ uri: currentProduct.cover }} style={styles.productImage} contentFit="contain" />
|
|
|
- </Animated.View>
|
|
|
-
|
|
|
- {/* 等级信息 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.detailsBut }} style={styles.detailsBut} resizeMode="contain">
|
|
|
- <View style={styles.detailsText}>
|
|
|
- <Text style={styles.levelText}>{getLevelName(currentProduct.level)}</Text>
|
|
|
- <Text style={styles.probabilityText}>({getProbability(currentProduct)}%)</Text>
|
|
|
- </View>
|
|
|
- </ImageBackground>
|
|
|
-
|
|
|
- {/* 商品名称 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.nameBg }} style={styles.goodsNameBg} resizeMode="contain">
|
|
|
- <Text style={styles.goodsNameText} numberOfLines={6}>
|
|
|
- {currentProduct.name}
|
|
|
- </Text>
|
|
|
- </ImageBackground>
|
|
|
- </>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 左右切换按钮 */}
|
|
|
- {currentIndex > 0 && (
|
|
|
- <TouchableOpacity style={styles.prevBtn} onPress={handlePrev}>
|
|
|
- <Image source={{ uri: Images.box.detail.left }} style={styles.arrowImg} contentFit="contain" />
|
|
|
- </TouchableOpacity>
|
|
|
- )}
|
|
|
- {currentIndex < products.length - 1 && (
|
|
|
- <TouchableOpacity style={styles.nextBtn} onPress={handleNext}>
|
|
|
- <Image source={{ uri: Images.box.detail.right }} style={styles.arrowImg} contentFit="contain" />
|
|
|
+ <View style={styles.detailWrapper}>
|
|
|
+ {/* Main Goods Section */}
|
|
|
+ <ImageBackground
|
|
|
+ source={{ uri: Images.box.detail.mainGoodsSection }}
|
|
|
+ style={styles.mainGoodsSection}
|
|
|
+ resizeMode="stretch"
|
|
|
+ >
|
|
|
+ <View style={{ height: 180 }} />
|
|
|
+
|
|
|
+ <ProductSwiper
|
|
|
+ products={data?.luckGoodsList?.map((item: any) => ({
|
|
|
+ cover: item.spu.cover,
|
|
|
+ name: item.spu.name,
|
|
|
+ level: item.level,
|
|
|
+ quantity: item.quantity
|
|
|
+ })) || []}
|
|
|
+ />
|
|
|
+
|
|
|
+ <ImageBackground
|
|
|
+ source={{ uri: Images.box.detail.positionBgleftBg }}
|
|
|
+ style={styles.positionBgLeftBg}
|
|
|
+ resizeMode="contain"
|
|
|
+ />
|
|
|
+ <ImageBackground
|
|
|
+ source={{ uri: Images.box.detail.positionBgRightBg }}
|
|
|
+ style={styles.positionBgRightBg}
|
|
|
+ resizeMode="contain"
|
|
|
+ />
|
|
|
+ </ImageBackground>
|
|
|
+
|
|
|
+ {/* Bottom Text Graphic */}
|
|
|
+ <ImageBackground
|
|
|
+ source={{ uri: Images.box.detail.mainGoodsSectionBtext }}
|
|
|
+ style={styles.mainGoodsSectionBtext}
|
|
|
+ resizeMode="contain"
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* FirstLast (Box Info & Nav) */}
|
|
|
+ <FirstLast
|
|
|
+ data={data}
|
|
|
+ box={currentBox}
|
|
|
+ onPrevBox={handlePrevBox}
|
|
|
+ onNextBox={handleNextBox}
|
|
|
+ onChangeBox={() => boxRef.current?.show()}
|
|
|
+ onProductClick={(spu) => console.log(spu)}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* Product List (A/B/C/D) */}
|
|
|
+ <ProductListYfs
|
|
|
+ products={data?.luckGoodsList || []}
|
|
|
+ poolId={getSafePoolId()}
|
|
|
+ box={currentBox}
|
|
|
+ />
|
|
|
+
|
|
|
+ {/* Floating Buttons */}
|
|
|
+ <TouchableOpacity style={[styles.positionBut, styles.positionRule]} onPress={() => ruleRef.current?.show()}>
|
|
|
+ <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.btnBg} resizeMode="contain">
|
|
|
+ <Text style={styles.slantedL}>规则</Text>
|
|
|
+ </ImageBackground>
|
|
|
</TouchableOpacity>
|
|
|
- )}
|
|
|
- </View>
|
|
|
-
|
|
|
- {/* 左侧装饰 */}
|
|
|
- <Image source={{ uri: Images.box.detail.positionBgleftBg }} style={styles.positionBgleftBg} contentFit="contain" />
|
|
|
- {/* 右侧装饰 */}
|
|
|
- <Image source={{ uri: Images.box.detail.positionBgRightBg }} style={styles.positionBgRightBg} contentFit="contain" />
|
|
|
- </ImageBackground>
|
|
|
|
|
|
- {/* 底部装饰文字 */}
|
|
|
- <Image source={{ uri: Images.box.detail.mainGoodsSectionBtext }} style={styles.mainGoodsSectionBtext} contentFit="cover" />
|
|
|
-
|
|
|
- {/* 侧边按钮 - 规则 */}
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionRule]} onPress={() => ruleRef.current?.show()}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButText}>规则</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
-
|
|
|
- {/* 侧边按钮 - 记录 */}
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionRecord]} onPress={() => recordRef.current?.show()}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButText}>记录</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
+ <TouchableOpacity style={[styles.positionBut, styles.positionRecord]} onPress={() => recordRef.current?.show()}>
|
|
|
+ <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.btnBg} resizeMode="contain">
|
|
|
+ <Text style={styles.slantedL}>记录</Text>
|
|
|
+ </ImageBackground>
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
|
- {/* 侧边按钮 - 锁定/解锁 */}
|
|
|
- {box && !box.lock && (
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionLock]} onPress={handleLock}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButText}>锁定</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
- )}
|
|
|
- {box?.lock && user && box.lock.locker === (user.userId || user.id) && (
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionLock]} onPress={handleUnlock}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgLeft }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButText}>解锁</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 侧边按钮 - 仓库 */}
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionStore]} onPress={() => router.push('/store' as any)}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgRight }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButTextR}>仓库</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
+ <TouchableOpacity style={[styles.positionBut, styles.positionStore]} onPress={() => router.push('/store')}>
|
|
|
+ <ImageBackground source={{ uri: Images.box.detail.positionBgRight }} style={styles.btnBg} resizeMode="contain">
|
|
|
+ <Text style={styles.slantedR}>仓库</Text>
|
|
|
+ </ImageBackground>
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
|
- {/* 侧边按钮 - 刷新 */}
|
|
|
- <TouchableOpacity style={[styles.positionBut, styles.positionRefresh]} onPress={() => loadBox(boxNum)}>
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.positionBgRight }} style={styles.positionButBg} resizeMode="contain">
|
|
|
- <Text style={styles.positionButTextR}>刷新</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
+ <TouchableOpacity style={[styles.positionBut, styles.positionRefresh]} onPress={() => loadBox(currentBox?.number)}>
|
|
|
+ <ImageBackground source={{ uri: Images.box.detail.positionBgRight }} style={styles.btnBg} resizeMode="contain">
|
|
|
+ <Text style={styles.slantedR}>刷新</Text>
|
|
|
+ </ImageBackground>
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
|
- {/* 锁定倒计时 */}
|
|
|
- {box?.lock && (
|
|
|
- <View style={styles.lockTimeBox}>
|
|
|
- <Text style={styles.lockTimeLabel}>剩余时间:</Text>
|
|
|
- <View style={styles.lockTimeBarBox}>
|
|
|
- <View style={styles.lockTimeBar}>
|
|
|
- <View style={[styles.processBar, { width: `${leftNum}%` }]} />
|
|
|
- </View>
|
|
|
- <Text style={[styles.lockTimeText, { left: `${leftNum}%` }]}>{leftTime}</Text>
|
|
|
- </View>
|
|
|
</View>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 标题 */}
|
|
|
- <View style={styles.productTitleBox}>
|
|
|
- <Image source={{ uri: Images.box.detail.productTitle }} style={styles.productTitleImg} contentFit="contain" />
|
|
|
- </View>
|
|
|
-
|
|
|
- {/* 一番赏盒子信息区域 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.firstBoxBg }} style={styles.firstLastBox}>
|
|
|
- {/* 当前盒子剩余数量 */}
|
|
|
- {box && (
|
|
|
- <View style={styles.boxSizeRow}>
|
|
|
- <Text style={styles.boxSizeText}>当前盒子剩余:</Text>
|
|
|
- <Text style={styles.boxSizeText}>
|
|
|
- <Text style={styles.boxSizeNum}>{box.leftQuantity}</Text>
|
|
|
- /{box.quantity || '-'}发
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 抢先赏/最终赏/全局赏展示 */}
|
|
|
- {box?.prizeList && box.prizeList.length > 0 && (
|
|
|
- <View style={styles.prizeListRow}>
|
|
|
- {box.prizeList.map((item: any, index: number) => (
|
|
|
- <ImageBackground key={index} source={{ uri: Images.box.detail.firstItemBg }} style={styles.prizeItem}>
|
|
|
- <Image source={{ uri: item.cover }} style={styles.prizeImage} contentFit="contain" />
|
|
|
- <View style={[styles.prizeLevelTag, {
|
|
|
- backgroundColor: item.level === 'FIRST' ? 'rgba(91, 189, 208, 0.8)' :
|
|
|
- item.level === 'LAST' ? 'rgba(246, 44, 113, 0.8)' : 'rgba(44, 246, 74, 0.8)'
|
|
|
- }]}>
|
|
|
- <Text style={styles.prizeLevelText}>
|
|
|
- {item.level === 'FIRST' ? '抢先赏' : item.level === 'LAST' ? '最终赏' : '全局赏'}
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- </ImageBackground>
|
|
|
- ))}
|
|
|
- </View>
|
|
|
- )}
|
|
|
-
|
|
|
- {/* 换盒控制区 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.funBoxBg }} style={styles.funBox}>
|
|
|
- <TouchableOpacity style={styles.preBoxBtn} onPress={handlePreBox} disabled={!boxNum || parseInt(boxNum) <= 1}>
|
|
|
- <Text style={[styles.funBoxText, (!boxNum || parseInt(boxNum) <= 1) && styles.disabled]}>上一盒</Text>
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.changeBoxBtn} onPress={handleShowBoxChoose}>
|
|
|
- <Text style={styles.changeBoxText}>换盒({boxNum || '-'}/{box?.lastNumber || '-'})</Text>
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.nextBoxBtn} onPress={handleNextBox} disabled={!box || parseInt(boxNum) >= box.lastNumber}>
|
|
|
- <Text style={[styles.funBoxText, (!box || parseInt(boxNum) >= box.lastNumber) && styles.disabled]}>下一盒</Text>
|
|
|
- </TouchableOpacity>
|
|
|
- </ImageBackground>
|
|
|
- </ImageBackground>
|
|
|
-
|
|
|
- {/* 商品列表 */}
|
|
|
- <ProductListYfs products={products} levelList={probability} poolId={poolId!} price={data.price} box={box} />
|
|
|
-
|
|
|
- <View style={{ height: 150 }} />
|
|
|
+
|
|
|
+ <View style={{ height: 100 }} />
|
|
|
</ScrollView>
|
|
|
|
|
|
- {/* 底部购买栏 */}
|
|
|
- <ImageBackground source={{ uri: Images.box.detail.boxDetailBott }} style={[styles.bottomBar, { paddingBottom: insets.bottom + 10 }]} resizeMode="cover">
|
|
|
- <View style={styles.bottomBtns}>
|
|
|
- <TouchableOpacity style={styles.btnItem} onPress={() => handlePay(1)} activeOpacity={0.8}>
|
|
|
- <ImageBackground source={{ uri: Images.common.butBgV }} style={styles.btnBg} resizeMode="contain">
|
|
|
- <Text style={styles.btnText}>购买一盒</Text>
|
|
|
- <Text style={styles.btnPrice}>(¥{data.specialPrice || data.price})</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.btnItem} onPress={() => handlePay(5)} activeOpacity={0.8}>
|
|
|
- <ImageBackground source={{ uri: Images.common.butBgL }} style={styles.btnBg} resizeMode="contain">
|
|
|
- <Text style={styles.btnText}>购买五盒</Text>
|
|
|
- <Text style={styles.btnPrice}>(¥{data.specialPriceFive || data.price * 5})</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
- <TouchableOpacity style={styles.btnItem} onPress={handleShowNumChoose} activeOpacity={0.8}>
|
|
|
- <ImageBackground source={{ uri: Images.common.butBgH }} style={styles.btnBg} resizeMode="contain">
|
|
|
- <Text style={styles.btnText}>购买多盒</Text>
|
|
|
- </ImageBackground>
|
|
|
- </TouchableOpacity>
|
|
|
- </View>
|
|
|
- </ImageBackground>
|
|
|
- </ImageBackground>
|
|
|
+ <View style={styles.bottomBar}>
|
|
|
+ <PurchaseBar
|
|
|
+ price={data?.price || 0}
|
|
|
+ specialPrice={data?.specialPrice}
|
|
|
+ specialPriceFive={data?.specialPriceFive}
|
|
|
+ onBuy={(count) => {
|
|
|
+ if (!currentBox) {
|
|
|
+ boxRef.current?.show();
|
|
|
+ } else {
|
|
|
+ numRef.current?.show(currentBox);
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ onBuyMany={() => {
|
|
|
+ if (!currentBox) {
|
|
|
+ boxRef.current?.show();
|
|
|
+ } else {
|
|
|
+ numRef.current?.show(currentBox);
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
|
|
|
- <CheckoutModal ref={checkoutRef} data={data} poolId={poolId!} boxNumber={boxNum} onSuccess={handleSuccess} />
|
|
|
- <RecordModal ref={recordRef} poolId={poolId!} />
|
|
|
- <RuleModal ref={ruleRef} />
|
|
|
- <NumChooseModal ref={numChooseRef} poolId={poolId!} onPay={handleNumPay} />
|
|
|
- <BoxChooseModal ref={boxChooseRef} poolId={poolId!} onChoose={handleChooseBox} />
|
|
|
+ {/* Modals */}
|
|
|
+ <BoxSelectionModal
|
|
|
+ ref={boxRef}
|
|
|
+ poolId={getSafePoolId()}
|
|
|
+ onSelect={handleBoxSelect}
|
|
|
+ />
|
|
|
+ <NumSelectionModal
|
|
|
+ ref={numRef}
|
|
|
+ poolId={getSafePoolId()}
|
|
|
+ onPay={handlePay}
|
|
|
+ />
|
|
|
+ <CheckoutModal
|
|
|
+ ref={checkoutRef}
|
|
|
+ poolId={getSafePoolId()}
|
|
|
+ data={data}
|
|
|
+ onSuccess={(res) => {
|
|
|
+ console.log('Success', res);
|
|
|
+ loadData(); // Refresh
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <RuleModal ref={ruleRef} />
|
|
|
+ <RecordModal ref={recordRef} poolId={getSafePoolId()} />
|
|
|
+ </ImageBackground>
|
|
|
</View>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-const styles = StyleSheet.create({
|
|
|
- container: { flex: 1, backgroundColor: '#1a1a2e' },
|
|
|
- background: { flex: 1 },
|
|
|
- loadingContainer: { flex: 1, backgroundColor: '#1a1a2e', justifyContent: 'center', alignItems: 'center' },
|
|
|
- errorText: { color: '#999', fontSize: 16 },
|
|
|
- backBtn2: { marginTop: 20, backgroundColor: '#ff6600', paddingHorizontal: 20, paddingVertical: 10, borderRadius: 8 },
|
|
|
- backBtn2Text: { color: '#fff', fontSize: 14 },
|
|
|
|
|
|
- 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: 16, fontWeight: 'bold' },
|
|
|
- headerTitle: { color: '#fff', fontSize: 15, fontWeight: 'bold', flex: 1, textAlign: 'center', width: 250 },
|
|
|
- placeholder: { width: 40 },
|
|
|
|
|
|
- scrollView: { flex: 1 },
|
|
|
|
|
|
- mainGoodsSection: { width: SCREEN_WIDTH, height: 504, position: 'relative' },
|
|
|
- mainSwiper: { position: 'relative', width: '100%', height: 375, alignItems: 'center', justifyContent: 'center', marginTop: -50 },
|
|
|
- productImageBox: { width: 200, height: 300, justifyContent: 'center', alignItems: 'center' },
|
|
|
- productImage: { width: 200, height: 300 },
|
|
|
- detailsBut: { width: 120, height: 45, justifyContent: 'center', alignItems: 'center', marginTop: -30 },
|
|
|
- detailsText: { flexDirection: 'row', alignItems: 'center' },
|
|
|
- levelText: { fontSize: 14, color: '#FBC400', fontWeight: 'bold' },
|
|
|
- probabilityText: { fontSize: 10, color: '#FBC400', marginLeft: 3 },
|
|
|
- goodsNameBg: { position: 'absolute', left: 47, top: 53, width: 43, height: 100, paddingTop: 8, justifyContent: 'flex-start', alignItems: 'center' },
|
|
|
- goodsNameText: { fontSize: 12, fontWeight: 'bold', color: '#000', width: 20, textAlign: 'center' },
|
|
|
- prevBtn: { position: 'absolute', left: 35, top: '40%' },
|
|
|
- nextBtn: { position: 'absolute', right: 35, top: '40%' },
|
|
|
- arrowImg: { width: 33, height: 38 },
|
|
|
-
|
|
|
- positionBgleftBg: { position: 'absolute', left: 0, top: 225, width: 32, height: 188 },
|
|
|
- positionBgRightBg: { position: 'absolute', right: 0, top: 225, width: 32, height: 188 },
|
|
|
- mainGoodsSectionBtext: { width: SCREEN_WIDTH, height: 74, marginTop: -10 },
|
|
|
-
|
|
|
- positionBut: { position: 'absolute', zIndex: 10, width: 35, height: 34 },
|
|
|
- positionButBg: { width: 35, height: 34, justifyContent: 'center', alignItems: 'center' },
|
|
|
- positionButText: { fontSize: 12, fontWeight: 'bold', color: '#fff', transform: [{ rotate: '14deg' }], textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1 },
|
|
|
- positionButTextR: { fontSize: 12, fontWeight: 'bold', color: '#fff', transform: [{ rotate: '-16deg' }], textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1 },
|
|
|
- positionRule: { top: 256, left: 0 },
|
|
|
- positionRecord: { top: 300, left: 0 },
|
|
|
- positionLock: { top: 345, left: 0 },
|
|
|
- positionStore: { top: 256, right: 0 },
|
|
|
- positionRefresh: { top: 300, right: 0 },
|
|
|
-
|
|
|
- lockTimeBox: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#71ccff', padding: 10, marginHorizontal: 10, borderRadius: 8, marginTop: -60 },
|
|
|
- lockTimeLabel: { color: '#000', fontSize: 12 },
|
|
|
- lockTimeBarBox: { flex: 1, height: 30, position: 'relative', justifyContent: 'center' },
|
|
|
- lockTimeBar: { height: 8, backgroundColor: 'rgba(255,255,255,0.6)', borderRadius: 4, overflow: 'hidden' },
|
|
|
- processBar: { height: '100%', backgroundColor: '#209ae5', borderRadius: 4 },
|
|
|
- lockTimeText: { position: 'absolute', top: -5, fontSize: 10, backgroundColor: '#000', color: '#fff', paddingHorizontal: 4, borderRadius: 2, marginLeft: -13 },
|
|
|
-
|
|
|
- productTitleBox: { alignItems: 'center', marginTop: -20, marginBottom: 10 },
|
|
|
- productTitleImg: { width: 121, height: 29 },
|
|
|
-
|
|
|
- // 一番赏盒子信息区域
|
|
|
- firstLastBox: {
|
|
|
- marginHorizontal: 10,
|
|
|
- height: 193,
|
|
|
- marginBottom: 10,
|
|
|
- paddingTop: 20,
|
|
|
- },
|
|
|
- boxSizeRow: {
|
|
|
- flexDirection: 'row',
|
|
|
- alignItems: 'center',
|
|
|
- paddingLeft: 10,
|
|
|
- paddingTop: 12,
|
|
|
- },
|
|
|
- boxSizeText: {
|
|
|
- fontSize: 12,
|
|
|
- color: '#fff',
|
|
|
- textShadowColor: '#000',
|
|
|
- textShadowOffset: { width: 1, height: 1 },
|
|
|
- textShadowRadius: 2,
|
|
|
- },
|
|
|
- boxSizeNum: {
|
|
|
- fontSize: 16,
|
|
|
- fontWeight: 'bold',
|
|
|
- },
|
|
|
- prizeListRow: {
|
|
|
- flexDirection: 'row',
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- paddingTop: 20,
|
|
|
- minHeight: 66,
|
|
|
- },
|
|
|
- prizeItem: {
|
|
|
- width: 63,
|
|
|
- height: 65,
|
|
|
- marginHorizontal: 5,
|
|
|
- position: 'relative',
|
|
|
- },
|
|
|
- prizeImage: {
|
|
|
- width: 60,
|
|
|
- height: 50,
|
|
|
- },
|
|
|
- prizeLevelTag: {
|
|
|
- position: 'absolute',
|
|
|
- left: 0,
|
|
|
- bottom: -8,
|
|
|
- width: '100%',
|
|
|
- height: 22,
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- prizeLevelText: {
|
|
|
- fontSize: 10,
|
|
|
- color: '#fff',
|
|
|
- fontWeight: 'bold',
|
|
|
- },
|
|
|
- funBox: {
|
|
|
- width: 270,
|
|
|
- height: 38,
|
|
|
- flexDirection: 'row',
|
|
|
- alignItems: 'center',
|
|
|
- justifyContent: 'center',
|
|
|
- alignSelf: 'center',
|
|
|
- marginTop: 15,
|
|
|
- },
|
|
|
- preBoxBtn: {
|
|
|
- width: 90,
|
|
|
- height: 50,
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- nextBoxBtn: {
|
|
|
- width: 90,
|
|
|
- height: 50,
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- changeBoxBtn: {
|
|
|
- width: 110,
|
|
|
- height: 60,
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- funBoxText: {
|
|
|
- fontSize: 12,
|
|
|
- color: '#fff',
|
|
|
- fontWeight: '500',
|
|
|
- },
|
|
|
- changeBoxText: {
|
|
|
- fontSize: 12,
|
|
|
- color: '#000',
|
|
|
- fontWeight: '500',
|
|
|
- },
|
|
|
- disabled: { opacity: 0.3 },
|
|
|
-
|
|
|
- bottomBar: { position: 'absolute', bottom: 0, left: 0, right: 0, height: 69, paddingHorizontal: 5 },
|
|
|
- bottomBtns: { flexDirection: 'row', height: 64, alignItems: 'center', justifyContent: 'space-around' },
|
|
|
- btnItem: { flex: 1, marginHorizontal: 6 },
|
|
|
- btnBg: { width: '100%', height: 54, justifyContent: 'center', alignItems: 'center' },
|
|
|
- btnText: { fontSize: 14, fontWeight: 'bold', color: '#fff' },
|
|
|
- btnPrice: { fontSize: 9, color: '#fff' },
|
|
|
-});
|
|
|
+
|