| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- import { Images } from '@/constants/images';
- import ServiceWallet from '@/services/wallet';
- import { Ionicons } from '@expo/vector-icons';
- import { Stack, useRouter } from 'expo-router';
- import React, { useEffect, useState } from 'react';
- import {
- Dimensions,
- ImageBackground,
- ScrollView,
- StatusBar,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- } from 'react-native';
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
- const { width: SCREEN_WIDTH } = Dimensions.get('window');
- interface CouponItem {
- id: string;
- name: string;
- amount: number;
- fullAmount: number; // For "Man X Keyong"
- status: number; // 1: Valid
- endTime: string;
- scene?: string; // LUCK, MALL, TRADE
- }
- export default function CouponScreen() {
- const router = useRouter();
- const insets = useSafeAreaInsets();
- const [list, setList] = useState<CouponItem[]>([]);
- const [loading, setLoading] = useState(false);
- useEffect(() => {
- loadData();
- }, []);
- const loadData = async () => {
- try {
- setLoading(true);
- const res = await ServiceWallet.coupons();
- // Ensure res is array or fetch from res.records if paginated
- const data = Array.isArray(res) ? res : (res?.records || []);
- setList(data);
- } catch (e) {
- console.error(e);
- } finally {
- setLoading(false);
- }
- };
- const handleUse = (item: CouponItem) => {
- // Original behavior: goAward() -> /pages/award/index (Box Screen)
- // We replicate this 1:1, or maintain specific routing if mall requires it.
- // Given request for 1:1, we prioritize Box screen.
- if (item.scene === 'MALL') {
- router.push('/(tabs)/store' as any);
- } else {
- router.push('/(tabs)/box' as any);
- }
- };
- const formatTime = (time: string) => {
- return time ? time.slice(0, 10) : '';
- };
- return (
- <View style={styles.container}>
- <Stack.Screen options={{ headerShown: false }} />
- <StatusBar barStyle="light-content" />
- <View style={[styles.header, { paddingTop: insets.top }]}>
- <TouchableOpacity style={styles.backBtn} onPress={() => router.back()}>
- <Ionicons name="chevron-back" size={24} color="#fff" />
- </TouchableOpacity>
- <Text style={styles.title}>优惠券</Text>
- </View>
- <ImageBackground
- source={{ uri: Images.common.commonBg }}
- style={styles.background}
- resizeMode="cover"
- >
- <ScrollView
- style={styles.scrollView}
- contentContainerStyle={{ paddingTop: insets.top + 50, paddingBottom: 50, paddingHorizontal: 10 }}
- >
- {list.length > 0 ? (
- list.map((item, index) => (
- <TouchableOpacity
- key={index}
- activeOpacity={0.8}
- onPress={() => handleUse(item)}
- >
- <ImageBackground
- source={{ uri: Images.mine.couponBg }}
- style={[styles.couponItem, item.status !== 1 && styles.grayscale]}
- resizeMode="stretch"
- >
- <View style={styles.left}>
- <View style={styles.amountBox}>
- <Text style={styles.symbol}>¥</Text>
- <Text style={styles.amount}>{item.amount}</Text>
- </View>
- <Text style={styles.fullAmount}>满{item.fullAmount}可用</Text>
- </View>
-
- <View style={styles.divider} />
-
- <View style={styles.right}>
- <View style={styles.info}>
- <Text style={styles.name}>{item.name}</Text>
- <Text style={styles.time}>{formatTime(item.endTime)}过期</Text>
- </View>
- <View>
- <ImageBackground
- source={{ uri: Images.mine.couponItemButBg }}
- style={styles.useBtn}
- resizeMode="contain"
- >
- <Text style={styles.useText}>立即使用</Text>
- </ImageBackground>
- </View>
- </View>
- </ImageBackground>
- </TouchableOpacity>
- ))
- ) : (
- <View style={styles.emptyBox}>
- <Text style={styles.emptyText}>暂无优惠券</Text>
- </View>
- )}
- </ScrollView>
- </ImageBackground>
- </View>
- );
- }
- const styles = StyleSheet.create({
- container: {
- flex: 1,
- backgroundColor: '#1a1a2e',
- },
- header: {
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- zIndex: 100,
- alignItems: 'center',
- paddingBottom: 10,
- },
- backBtn: {
- position: 'absolute',
- left: 10,
- bottom: 10,
- zIndex: 101,
- },
- title: {
- color: '#fff',
- fontSize: 16,
- fontWeight: 'bold',
- },
- background: {
- flex: 1,
- width: '100%',
- height: '100%',
- },
- scrollView: {
- flex: 1,
- },
- couponItem: {
- width: '100%',
- height: 92, // 184rpx / 2 = 92
- flexDirection: 'row',
- alignItems: 'center',
- marginBottom: 10,
- paddingHorizontal: 15, // Adjust padding based on image visual
- },
- grayscale: {
- opacity: 0.6,
- // React Native doesn't support grayscale prop directly on ImageBackground without filter or tintColor tricks
- // but opactity is a good enough approximation for invalid state
- },
- left: {
- width: 80,
- alignItems: 'center',
- justifyContent: 'center',
- },
- amountBox: {
- flexDirection: 'row',
- alignItems: 'baseline',
- },
- symbol: {
- fontSize: 12,
- color: '#404040',
- },
- amount: {
- fontSize: 30,
- fontWeight: 'bold',
- color: '#404040',
- },
- fullAmount: {
- fontSize: 12,
- color: '#8F8F8F',
- marginTop: 2,
- },
- divider: {
- width: 1,
- height: 24,
- backgroundColor: '#C3B3DF',
- marginHorizontal: 16,
- // opacity: 0, // Removed to match original design
- },
- right: {
- flex: 1,
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- paddingLeft: 10,
- },
- info: {
- justifyContent: 'center',
- },
- name: {
- fontSize: 15,
- fontWeight: 'bold',
- color: '#404040',
- marginBottom: 5,
- },
- time: {
- fontSize: 12,
- color: '#8F8F8F',
- },
- useBtn: {
- width: 87, // 174rpx / 2
- height: 37, // 74rpx / 2
- justifyContent: 'center',
- alignItems: 'center',
- },
- useText: {
- color: '#fff',
- fontSize: 12,
- },
- emptyBox: {
- marginTop: 100,
- alignItems: 'center',
- },
- emptyText: {
- color: '#999',
- fontSize: 14,
- },
- });
|