expo-duooomi-app/app/pointList.tsx

182 lines
6.6 KiB
TypeScript
Raw Permalink 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 { Ionicons } from '@expo/vector-icons'
import { LinearGradient } from 'expo-linear-gradient'
import { Stack, useRouter } from 'expo-router'
import React, { useEffect, useState } from 'react'
import { Dimensions, ScrollView } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { Block, Img, Text } from '@/@share/components'
import { useUserBalance } from '@/hooks/core'
import { cn } from '@/utils/cn'
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 className="ml-[-8px] size-[40px] items-center justify-center" opacity={0.7} onClick={() => router.back()}>
<Ionicons color="white" name="chevron-back" size={24} />
</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] tracking-tighter text-fg">{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}
opacity={0.9}
style={{ width: OPTION_WIDTH }}
className={cn(
'relative items-center justify-center overflow-hidden rounded-[16px] border-[2px] bg-[#FFFFFF33] py-[12px]',
isSelected ? 'border-[#FFE500]' : 'border-transparent',
)}
onClick={() => setSelectedOption(item.id)}
>
<Img
className="absolute inset-0"
source={require('@/assets/images/itemBg.png')}
style={{ resizeMode: 'cover' }}
/>
<Block className="mb-[8px]">
<Ionicons color="#FFD700" name="flash" size={24} />
</Block>
<Text className="mb-[16px] text-[24px] font-[700] text-white">{item.points}</Text>
<Block className="border-[#000000]">
<LinearGradient
colors={['#393939', '#060606']}
end={{ x: 0, y: 1 }}
start={{ x: 0, y: 0 }}
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}
opacity={0.8}
className={cn(
'flex-row items-center gap-[12px] rounded-full px-[20px] py-[16px]',
isSelected ? 'bg-white' : 'bg-[#FFFFFF33]',
)}
onClick={() => setPaymentMethod(method.id)}
>
<Block
className={cn(
'h-[20px] w-[20px] items-center justify-center rounded-full',
isSelected ? 'bg-black' : 'border-[2px] border-white/50',
)}
>
{isSelected && <Ionicons color="white" name="checkmark" size={14} />}
</Block>
<Text className={cn('font-700 text-[16px]', isSelected ? 'text-black' : 'text-white')}>
{method.label}
</Text>
</Block>
)
})}
</Block>
</Block>
)
const renderBottomBar = () => (
<Block
className="absolute inset-x-0 bottom-0 border-t border-white/5 bg-[#1C1E22]/95 px-[16px]"
style={{ paddingBottom: insets.bottom + 12, paddingTop: 16 }}
>
<Block
className="h-[52px] items-center justify-center rounded-full bg-accent shadow-lg shadow-[#FFE500]/20"
opacity={0.8}
onClick={handlePay}
>
<Text className="text-[16px] font-[900] text-black"></Text>
</Block>
<Block className="mt-[12px] flex-row justify-center">
<Text className="text-[11px] text-[#888888]"> </Text>
<Block opacity={0.6} onClick={() => console.log('Open agreement')}>
<Text className="text-[11px] text-accent underline"></Text>
</Block>
</Block>
</Block>
)
return (
<Block className="flex-1 bg-[#21221D]">
<Stack.Screen options={{ headerShown: false }} />
{renderHeader()}
<ScrollView contentContainerStyle={{ flexGrow: 1, paddingBottom: 200 }} showsVerticalScrollIndicator={false}>
{renderMyPoints()}
{renderOptions()}
{renderPaymentMethods()}
</ScrollView>
{renderBottomBar()}
</Block>
)
}