expo-duooomi-app/app/pointList.tsx

166 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect } from 'react'
import { ScrollView, Dimensions } from 'react-native'
import { Block, Text, Img } from '@/@share/components'
import { Ionicons } from '@expo/vector-icons'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { cn } from '@/utils/cn'
import { LinearGradient } from 'expo-linear-gradient'
import { Stack, useRouter } from 'expo-router'
import { useUserBalance } from '@/hooks/core'
const { width: SCREEN_WIDTH } = Dimensions.get('window')
const OPTION_WIDTH = (SCREEN_WIDTH - 32 - 15) / 2
const RECHARGE_OPTIONS = [
{ id: 1, points: '500', price: '$10.00' },
{ id: 2, points: '1,000', price: '$20.00' },
{ id: 3, points: '2,500', price: '$50.00' },
{ id: 4, points: '5,000', price: '$100.00' },
]
const PAYMENT_METHODS = [
{ id: 'alipay', label: '支付宝' },
{ id: 'wechat', label: '微信' },
] as const
export default function ChargePage() {
const router = useRouter()
const insets = useSafeAreaInsets()
const [selectedOption, setSelectedOption] = useState(RECHARGE_OPTIONS[0].id)
const [paymentMethod, setPaymentMethod] = useState<'alipay' | 'wechat'>('alipay')
const balanceRes = useUserBalance()
useEffect(() => {
balanceRes.load()
}, [])
const handlePay = () => {
console.log('支付功能仅在移动端可用')
}
const renderHeader = () => (
<Block className="flex-row items-center justify-between px-[16px]" style={{ paddingTop: insets.top + 12, paddingBottom: 12 }}>
<Block onClick={() => router.back()} opacity={0.7} className="-ml-[8px] h-[40px] w-[40px] items-center justify-center">
<Ionicons name="chevron-back" size={24} color="white" />
</Block>
<Text className="text-[16px] font-[700] text-white"></Text>
<Block className="w-[32px]" />
</Block>
)
const renderMyPoints = () => (
<Block className="mt-[12px] items-center justify-center py-[24px]">
<Text className="text-[14px] font-[400] text-[#B0B0B0]"></Text>
<Text className="mt-[4px] text-[40px] font-[900] italic tracking-tighter text-[#F5F5F5]">{balanceRes?.balance}</Text>
</Block>
)
const renderOptions = () => (
<Block className="mt-[16px] px-[16px]">
<Text className="mb-[16px] text-[14px] font-[700] text-white"></Text>
<Block className="flex-row flex-wrap justify-between gap-y-[16px]">
{RECHARGE_OPTIONS.map((item) => {
const isSelected = selectedOption === item.id
return (
<Block
key={item.id}
onClick={() => setSelectedOption(item.id)}
opacity={0.9}
className={cn(
'relative items-center justify-center overflow-hidden rounded-[16px] border-[2px] bg-[#FFFFFF33] py-[12px]',
isSelected ? 'border-[#FFE500]' : 'border-transparent',
)}
style={{ width: OPTION_WIDTH }}
>
<Img source={require('@/assets/images/itemBg.png')} className="absolute inset-0" style={{ resizeMode: 'cover' }} />
<Block className="mb-[8px]">
<Ionicons name="flash" size={24} color="#FFD700" />
</Block>
<Text className="mb-[16px] text-[24px] font-[700] text-white">{item.points}</Text>
<Block className="border-[#000000]">
<LinearGradient
colors={['#393939', '#060606']}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
style={{
borderRadius: 100,
width: 120,
height: 32,
alignItems: 'center',
justifyContent: 'center',
borderColor: '#000000',
overflow: 'hidden',
borderWidth: 1,
}}
>
<Text className="text-[12px] font-[600] text-white"> {item.price}</Text>
</LinearGradient>
</Block>
</Block>
)
})}
</Block>
</Block>
)
const renderPaymentMethods = () => (
<Block className="mt-[32px] px-[16px]">
<Text className="font-700 mb-[16px] text-[14px] text-white">:</Text>
<Block className="gap-[12px]">
{PAYMENT_METHODS.map((method) => {
const isSelected = paymentMethod === method.id
return (
<Block
key={method.id}
onClick={() => setPaymentMethod(method.id)}
opacity={0.8}
className={cn('flex-row items-center gap-[12px] rounded-full px-[20px] py-[16px]', isSelected ? 'bg-white' : 'bg-[#FFFFFF33]')}
>
<Block
className={cn('h-[20px] w-[20px] items-center justify-center rounded-full', isSelected ? 'bg-black' : 'border-[2px] border-white/50')}
>
{isSelected && <Ionicons name="checkmark" size={14} color="white" />}
</Block>
<Text className={cn('font-700 text-[16px]', isSelected ? 'text-black' : 'text-white')}>{method.label}</Text>
</Block>
)
})}
</Block>
</Block>
)
const renderBottomBar = () => (
<Block
className="absolute bottom-0 left-0 right-0 border-t-[1px] border-white/5 bg-[#1C1E22]/95 px-[16px]"
style={{ paddingBottom: insets.bottom + 12, paddingTop: 16 }}
>
<Block
onClick={handlePay}
opacity={0.8}
className="h-[52px] items-center justify-center rounded-full bg-[#FFE500] shadow-lg shadow-[#FFE500]/20"
>
<Text className="text-[16px] font-[900] italic text-black"></Text>
</Block>
<Block className="mt-[12px] flex-row justify-center">
<Text className="text-[11px] text-[#888888]"> </Text>
<Block onClick={() => console.log('Open agreement')} opacity={0.6}>
<Text className="text-[11px] text-[#FFE500] underline"></Text>
</Block>
</Block>
</Block>
)
return (
<Block className="flex-1 bg-[#21221D]">
<Stack.Screen options={{ headerShown: false }} />
{renderHeader()}
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={{ flexGrow: 1, paddingBottom: 200 }}>
{renderMyPoints()}
{renderOptions()}
{renderPaymentMethods()}
</ScrollView>
{renderBottomBar()}
</Block>
)
}