import { Image } from 'expo-image'; import { useRouter } from 'expo-router'; import React, { forwardRef, useImperativeHandle, useState } from 'react'; import { Alert, Modal, ScrollView, StyleSheet, Switch, Text, TextInput, TouchableOpacity, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { Address, getDefaultAddress } from '@/services/address'; import { GoodsDetail, previewSubmit, PreviewSubmitResult, submitOrder } from '@/services/mall'; interface CheckoutModalProps { data: GoodsDetail; goodsId: string; subjectId?: string; } export interface CheckoutModalRef { show: (preview: PreviewSubmitResult) => void; close: () => void; } export const CheckoutModal = forwardRef( ({ data, goodsId, subjectId }, ref) => { const router = useRouter(); const insets = useSafeAreaInsets(); const [visible, setVisible] = useState(false); const [preview, setPreview] = useState(null); const [address, setAddress] = useState
(null); const [quantity, setQuantity] = useState(1); const [phone, setPhone] = useState(''); const [checked, setChecked] = useState(true); const [useWallet, setUseWallet] = useState(false); const [payType, setPayType] = useState<'alipay' | 'wxpay'>('alipay'); const [submitting, setSubmitting] = useState(false); useImperativeHandle(ref, () => ({ show: (previewData: PreviewSubmitResult) => { setPreview(previewData); setVisible(true); loadAddress(); }, close: () => { setVisible(false); }, })); // 加载默认地址 const loadAddress = async () => { try { const addr = await getDefaultAddress(); setAddress(addr); } catch (error) { console.error('加载地址失败:', error); } }; // 计算最终价格 const lastPrice = preview ? data.sellType === 2 ? preview.depositAmount || 0 : preview.paymentAmount : data.price; // 增加数量 const increment = async () => { if (subjectId) return; // 秒杀不能改数量 if (quantity >= 5) return; const newQty = quantity + 1; setQuantity(newQty); // 重新获取价格 const newPreview = await previewSubmit({ goodsId, quantity: newQty, subjectId }); if (newPreview) setPreview(newPreview); }; // 减少数量 const decrement = async () => { if (quantity <= 1) return; const newQty = quantity - 1; setQuantity(newQty); const newPreview = await previewSubmit({ goodsId, quantity: newQty, subjectId }); if (newPreview) setPreview(newPreview); }; // 提交订单 const handlePay = async () => { if (!address) { Alert.alert('提示', '请选择收货地址'); return; } if (data.sellType === 2 && !phone) { Alert.alert('提示', '请填写尾款提醒手机号'); return; } if (!checked) { Alert.alert('提示', '请勾选协议'); return; } setSubmitting(true); try { const paymentType = useWallet ? 'WALLET' : payType === 'alipay' ? 'ALIPAY' : 'WXPAY'; const result = await submitOrder({ goodsId: data.id, paymentType, addressBookId: address.id, quantity, restNotifyMobile: phone, subjectId, }); if (result) { if (result.paySuccess) { setVisible(false); Alert.alert('成功', '支付成功', [ { text: '查看订单', onPress: () => router.push('/orders' as any) }, ]); } else { // 需要跳转支付 Alert.alert('提示', '请在支付页面完成支付'); } } } catch (error) { console.error('提交订单失败:', error); Alert.alert('错误', '提交订单失败'); } setSubmitting(false); }; // 选择地址 const selectAddress = () => { setVisible(false); router.push('/address?type=1' as any); }; return ( setVisible(false)} /> {/* 标题 */} 确认订单 setVisible(false)}> × {/* 商品信息 */} {data.name} ¥{data.subjectPrice || data.price} {data.sellType === 2 && ( 定金:¥{data.deposit} )} {/* 数量选择 */} - {quantity} + {/* 优惠券 */} 优惠券 -¥{preview?.couponAmount || 0} {/* 钱包支付 */} {preview?.cash && ( 使用钱包支付 (余额:¥{preview.cash.balance}) )} {/* 支付方式 */} {!useWallet && ( setPayType('alipay')} > 支付宝 setPayType('wxpay')} > 微信支付 )} {/* 尾款手机号 */} {data.sellType === 2 && ( * 尾款提醒手机号 )} {/* 收货地址 */} {address ? ( {address.contactName} {address.contactNo} {address.province}{address.city}{address.district}{address.address} ) : ( * 请填写收货地址 )} {/* 协议 */} 同意《平台服务协议》详情 {/* 支付按钮 */} ¥{lastPrice} 立即支付 ); } ); const styles = StyleSheet.create({ overlay: { flex: 1, justifyContent: 'flex-end', }, mask: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.5)', }, content: { backgroundColor: '#fff', borderTopLeftRadius: 15, borderTopRightRadius: 15, maxHeight: '80%', }, header: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingVertical: 15, borderBottomWidth: 1, borderBottomColor: '#eee', }, title: { fontSize: 16, fontWeight: '600', color: '#333', }, closeBtn: { position: 'absolute', right: 15, fontSize: 28, color: '#999', }, scrollContent: { paddingHorizontal: 15, }, goodsInfo: { flexDirection: 'row', paddingVertical: 15, borderBottomWidth: 1, borderBottomColor: '#eee', }, goodsImage: { width: 90, height: 90, borderRadius: 8, backgroundColor: '#f5f5f5', }, goodsDetail: { flex: 1, marginLeft: 12, justifyContent: 'space-between', }, goodsName: { fontSize: 14, color: '#333', lineHeight: 20, }, priceRow: { flexDirection: 'row', alignItems: 'center', }, price: { fontSize: 18, fontWeight: 'bold', color: '#ff6b00', }, deposit: { fontSize: 12, color: '#8b3dff', marginLeft: 10, backgroundColor: 'rgba(139,61,255,0.1)', paddingHorizontal: 8, paddingVertical: 2, borderRadius: 10, }, quantityRow: { flexDirection: 'row', alignItems: 'center', }, qtyBtn: { width: 28, height: 28, borderRadius: 14, backgroundColor: '#f5f5f5', justifyContent: 'center', alignItems: 'center', }, qtyBtnDisabled: { opacity: 0.3, }, qtyBtnText: { fontSize: 18, color: '#333', }, qtyNum: { fontSize: 16, color: '#333', marginHorizontal: 15, }, row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 15, borderBottomWidth: 1, borderBottomColor: '#eee', }, rowLabel: { fontSize: 14, color: '#333', }, rowValue: { fontSize: 14, color: '#ff6b00', }, balance: { color: '#ff6b00', }, payTypeSection: { paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#eee', }, payTypeItem: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10, }, payTypeName: { fontSize: 14, color: '#333', }, radio: { width: 20, height: 20, borderRadius: 10, borderWidth: 2, borderColor: '#ddd', }, radioActive: { borderColor: '#ff6b00', backgroundColor: '#ff6b00', }, phoneRow: { paddingVertical: 15, borderBottomWidth: 1, borderBottomColor: '#eee', }, phoneLabel: { fontSize: 14, color: '#333', marginBottom: 10, }, required: { color: '#dd524d', }, phoneInput: { height: 40, backgroundColor: '#f5f5f5', borderRadius: 8, paddingHorizontal: 12, fontSize: 14, }, addressRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 15, borderBottomWidth: 1, borderBottomColor: '#eee', }, addressInfo: { flex: 1, }, addressName: { fontSize: 14, color: '#333', fontWeight: '500', }, addressDetail: { fontSize: 12, color: '#666', marginTop: 4, }, noAddress: { fontSize: 14, color: '#999', }, arrow: { fontSize: 20, color: '#999', marginLeft: 10, }, agreementRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 15, }, agreementText: { fontSize: 14, color: '#666', }, payBtn: { marginHorizontal: 15, marginTop: 15, height: 50, backgroundColor: '#ff6b00', borderRadius: 25, justifyContent: 'center', alignItems: 'center', }, payBtnDisabled: { opacity: 0.6, }, payBtnText: { color: '#fff', fontSize: 16, fontWeight: '600', }, });