/* eslint-disable react-hooks/exhaustive-deps */ import CustomCheckBox from '@/components/ui/CheckBox'; import { USER_AGREEMENT_URL } from '@/constants/Agree'; // import { useAuth } from '@/contexts/AuthContext'; // import { UserApi } from '@/services'; import { log, logger } from '@/utils/logger'; import { captureMessage, captureMessageWithContext, capturePurchaseEvent, captureUserAction } from '@/utils/sentry.utils'; import { Toast as GlobalToast } from '@/utils/toast.utils'; import { Ionicons } from '@expo/vector-icons'; import { captureException } from '@sentry/react-native'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { LinearGradient } from 'expo-linear-gradient'; import React, { useEffect, useRef, useState } from 'react'; import { ActivityIndicator, Alert, Dimensions, Linking, Modal, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import Purchases, { CustomerInfo, PurchasesStoreProduct } from 'react-native-purchases'; const { height } = Dimensions.get('window'); interface MembershipModalProps { visible: boolean; onClose?: () => void; onPurchaseSuccess?: () => void; } interface MembershipPlan { id: string; fallbackTitle: string; subtitle: string; type: 'weekly' | 'quarterly' | 'lifetime'; recommended?: boolean; tag?: string; originalPrice?: string; } const DEFAULT_PLANS: MembershipPlan[] = [ { id: 'com.anonymous.digitalpilates.membership.lifetime', fallbackTitle: '终身会员', subtitle: '一次投入,终身健康陪伴', type: 'lifetime', recommended: true, tag: '限时特价', originalPrice: '¥898', }, { id: 'com.anonymous.digitalpilates.membership.quarter', fallbackTitle: '季度会员', subtitle: '3个月蜕变计划,见证身材变化', type: 'quarterly', originalPrice: '¥598', }, { id: 'com.anonymous.digitalpilates.membership.weekly', fallbackTitle: '周会员', subtitle: '7天体验,开启健康第一步', type: 'weekly', originalPrice: '¥128', }, ]; // 权限类型枚举 type PermissionType = 'exclusive' | 'limited' | 'unlimited'; // 权限配置接口 interface PermissionConfig { type: PermissionType; text: string; vipText?: string; // VIP用户的特殊文案,可选 } // 权益对比项接口 interface BenefitItem { title: string; description?: string; // 功能描述,可选 vip: PermissionConfig; regular: PermissionConfig; } // 权益对比配置 const BENEFIT_COMPARISON: BenefitItem[] = [ { title: 'AI拍照记录热量', description: '通过拍照识别食物并自动记录热量', vip: { type: 'unlimited', text: '无限次使用', vipText: '无限次使用' }, regular: { type: 'limited', text: '有限次使用', vipText: '每日3次' } }, { title: 'AI拍照识别包装', description: '识别食品包装上的营养成分信息', vip: { type: 'unlimited', text: '无限次使用', vipText: '无限次使用' }, regular: { type: 'limited', text: '有限次使用', vipText: '每日5次' } }, { title: '每日健康提醒', description: '根据个人目标提供个性化健康提醒', vip: { type: 'unlimited', text: '完全支持', vipText: '智能提醒' }, regular: { type: 'unlimited', text: '基础提醒', vipText: '基础提醒' } }, { title: 'AI教练对话', description: '与AI健康教练进行个性化对话咨询', vip: { type: 'unlimited', text: '无限次对话', vipText: '深度分析' }, regular: { type: 'limited', text: '有限次对话', vipText: '每日10次' } }, { title: '体态评估', description: '通过照片分析体态问题并提供改善建议', vip: { type: 'exclusive', text: '完全支持', vipText: '专业评估' }, regular: { type: 'exclusive', text: '不可使用', vipText: '不可使用' } } ]; const PLAN_STYLE_CONFIG: Record = { lifetime: { gradient: ['#FFF1DD', '#FFE8FA'] as const, accent: '#7B2CBF', }, quarterly: { gradient: ['#E5EAFE', '#F4E8FF'] as const, accent: '#5B5BE6', }, weekly: { gradient: ['#FFF6E5', '#FFE7CC'] as const, accent: '#FF9500', }, }; // 根据权限类型获取对应的图标 const getPermissionIcon = (type: PermissionType, isVip: boolean) => { switch (type) { case 'exclusive': return isVip ? ( ) : ( ); case 'limited': return isVip ? ( ) : ( ); case 'unlimited': return ( ); default: return ; } }; export function MembershipModal({ visible, onClose, onPurchaseSuccess }: MembershipModalProps) { const [selectedProduct, setSelectedProduct] = useState(null); const [loading, setLoading] = useState(false); const [restoring, setRestoring] = useState(false); // const { user } = useAuth() // const { refreshUserInfo } = useAuth(); const [products, setProducts] = useState([]); // 协议同意状态 - 只需要一个状态 const [agreementAccepted, setAgreementAccepted] = useState(false); // 保存监听器引用,用于移除监听器 const purchaseListenerRef = useRef<((customerInfo: CustomerInfo) => void) | null>(null); // 根据选中的产品生成tips内容 const getTipsContent = (product: PurchasesStoreProduct | null): string => { if (!product) return ''; const plan = DEFAULT_PLANS.find(item => item.id === product.identifier); if (!plan) { return ''; } switch (plan.type) { case 'lifetime': return '终身陪伴,见证您的每一次健康蜕变'; case 'quarterly': return '3个月科学计划,让健康成为生活习惯'; case 'weekly': return '7天体验期,感受专业健康指导的力量'; default: return ''; } }; // useEffect(() => { // if (user) { // captureMessage('用户已登录,开始初始化 Purchases'); // initPurchases(); // } // }, [user]); // 初始化只需要执行一次 // 单独的 useEffect 来处理购买监听器,依赖 visible 状态 useEffect(() => { if (visible) { setupPurchaseListener(); setAgreementAccepted(false); initPurchases(); } else { removePurchaseListener(); setProducts([]); setSelectedProduct(null); setAgreementAccepted(false); setLoading(false); setRestoring(false); } // 组件卸载时确保移除监听器 return () => { removePurchaseListener(); log.info('MembershipModal 购买监听器已清理'); }; }, [visible]); // 依赖 visible 状态 const initPurchases = async () => { capturePurchaseEvent('init', '开始获取会员产品套餐'); try { // 添加延迟,确保 RevenueCat SDK 完全初始化 await new Promise(resolve => setTimeout(resolve, 500)); const offerings = await Purchases.getOfferings(); log.info('获取产品套餐', { offerings }); logger.info('获取产品套餐成功', { currentOffering: offerings.current?.identifier || null, availablePackagesCount: offerings.current?.availablePackages.length || 0, allOfferingsCount: Object.keys(offerings.all).length, }); const packages = offerings.current?.availablePackages ?? []; if (packages.length === 0) { log.warn('没有找到可用的产品套餐', { hasCurrentOffering: offerings.current !== null, packagesLength: offerings.current?.availablePackages.length || 0, }); logger.info('没有找到可用的产品套餐', { hasCurrentOffering: offerings.current !== null, packagesLength: offerings.current?.availablePackages.length || 0, }); setProducts([]); setSelectedProduct(null); return; } const matchedProducts = packages .map(pkg => pkg.product) .filter(product => DEFAULT_PLANS.some(plan => plan.id === product.identifier)); const orderedProducts = DEFAULT_PLANS .map(plan => matchedProducts.find(product => product.identifier === plan.id)) .filter((product): product is PurchasesStoreProduct => Boolean(product)); const fallbackProducts = packages.map(pkg => pkg.product); const productsToUse = orderedProducts.length > 0 ? orderedProducts : fallbackProducts; log.info('productsToUse', productsToUse) setProducts(productsToUse); // 获取产品后,检查用户的购买记录并自动选中对应套餐 await checkAndSelectActivePlan(productsToUse); // 确保始终有一个选中的产品 setSelectedProduct(current => { // 如果已经有选中的产品,保持不变 if (current) return current; // 否则选择第一个可用产品 return productsToUse[0] ?? null; }); } catch (e: any) { // 安全地处理错误对象,避免循环引用 const errorData = { message: e?.message || '未知错误', code: e?.code || null, name: e?.name || 'Error', // 只包含基本的错误信息,避免可能的循环引用 }; log.error('获取产品套餐失败', { error: errorData }); captureException(e); capturePurchaseEvent('error', '获取产品套餐失败', errorData); // 设置空状态,避免界面卡在加载状态 setProducts([]); setSelectedProduct(null); } }; // 添加购买状态监听器 const setupPurchaseListener = () => { log.info('设置购买监听器', { visible }); // 如果已经有监听器,先移除 if (purchaseListenerRef.current) { removePurchaseListener(); } // 创建监听器函数 const listener = (customerInfo: CustomerInfo) => { log.info('购买状态变化监听器触发', { customerInfo, visible }); // 检查是否有有效的购买记录 const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0; const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0; const hasActiveSubscriptions = Object.keys(customerInfo.activeSubscriptions).length > 0; if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) { capturePurchaseEvent('success', '监听到购买状态变化', { hasActiveEntitlements, hasNonSubscriptionTransactions, hasActiveSubscriptions, activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length, nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length, activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length, modalVisible: visible }); log.info('检测到购买成功,准备刷新用户信息并关闭弹窗'); // 延迟一点时间,确保购买流程完全完成 setTimeout(async () => { // 刷新用户信息 // await refreshUserInfo(); // 调用购买成功回调 onPurchaseSuccess?.(); // 关闭弹窗 onClose?.(); // 显示成功提示 GlobalToast.show({ message: '会员开通成功', }); }, 1000); } }; // 保存监听器引用 purchaseListenerRef.current = listener; // 添加监听器 Purchases.addCustomerInfoUpdateListener(listener); log.info('购买监听器已添加'); }; // 移除购买状态监听器 const removePurchaseListener = () => { if (purchaseListenerRef.current) { log.info('移除购买监听器'); Purchases.removeCustomerInfoUpdateListener(purchaseListenerRef.current); purchaseListenerRef.current = null; log.info('购买监听器已移除'); } }; // 检查用户的购买记录并自动选中对应套餐 const checkAndSelectActivePlan = async (availableProducts: PurchasesStoreProduct[]) => { try { captureUserAction('开始检查用户购买记录'); // 获取用户的购买信息 const customerInfo = await Purchases.getCustomerInfo(); log.info('获取用户购买信息', { customerInfo }); // 记录详细的购买状态日志 captureMessageWithContext('获取用户购买信息成功', { activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length, nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length, activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length, originalAppUserId: customerInfo.originalAppUserId, firstSeen: customerInfo.firstSeen, originalPurchaseDate: customerInfo.originalPurchaseDate, latestExpirationDate: customerInfo.latestExpirationDate }); // 查找激活的产品ID let activePurchasedProductIds: string[] = []; // 检查权益 Object.keys(customerInfo.entitlements.active).forEach(key => { const entitlement = customerInfo.entitlements.active[key]; activePurchasedProductIds.push(entitlement.productIdentifier); log.debug(`激活的权益: ${key}, 产品ID: ${entitlement.productIdentifier}`); }); // 检查非订阅购买(如终身会员) customerInfo.nonSubscriptionTransactions.forEach(transaction => { activePurchasedProductIds.push(transaction.productIdentifier); log.debug(`非订阅购买: ${transaction.productIdentifier}, 购买时间: ${transaction.purchaseDate}`); }); // 检查订阅 Object.keys(customerInfo.activeSubscriptions).forEach(productId => { activePurchasedProductIds.push(productId); log.debug(`激活的订阅: ${productId}`); }); // 去重 activePurchasedProductIds = [...new Set(activePurchasedProductIds)]; captureMessageWithContext('用户激活的产品列表', { activePurchasedProductIds, activePurchasedProductCount: activePurchasedProductIds.length }); if (activePurchasedProductIds.length > 0) { // 尝试在可用产品中找到匹配的产品 let selectedProduct: PurchasesStoreProduct | null = null; // 优先级:终身会员 > 季度会员 > 周会员 const priorityOrder = DEFAULT_PLANS.map(plan => plan.id); // 按照优先级查找 for (const priorityProductId of priorityOrder) { if (activePurchasedProductIds.includes(priorityProductId)) { selectedProduct = availableProducts.find(product => product.identifier === priorityProductId) || null; if (selectedProduct) { log.info(`找到优先级最高的激活产品: ${priorityProductId}`); break; } } } // 如果按优先级没找到,尝试找到任何匹配的产品 if (!selectedProduct) { for (const productId of activePurchasedProductIds) { selectedProduct = availableProducts.find(product => product.identifier === productId) || null; if (selectedProduct) { log.info(`找到匹配的激活产品: ${productId}`); break; } } } if (selectedProduct) { setSelectedProduct(selectedProduct); captureMessageWithContext('自动选中用户已购买的套餐', { selectedProductId: selectedProduct.identifier, selectedProductTitle: selectedProduct.title, selectedProductPrice: selectedProduct.price, allActivePurchasedProductIds: activePurchasedProductIds }); } else { captureMessageWithContext('未找到匹配的可用产品', { activePurchasedProductIds, availableProductIds: availableProducts.map(p => p.identifier) }); log.warn('用户有激活的购买记录,但没有找到匹配的可用产品', { activePurchasedProductIds, availableProductIds: availableProducts.map(p => p.identifier) }); } } else { captureMessageWithContext('用户没有激活的购买记录', { hasEntitlements: Object.keys(customerInfo.entitlements.active).length > 0, hasNonSubscriptions: customerInfo.nonSubscriptionTransactions.length > 0, hasActiveSubscriptions: Object.keys(customerInfo.activeSubscriptions).length > 0 }); log.info('用户没有激活的购买记录,使用默认选择逻辑'); setSelectedProduct(availableProducts[0] ?? null); } } catch (error: any) { // 安全地处理错误对象,避免循环引用 const errorData = { message: error?.message || '未知错误', code: error?.code || null, name: error?.name || 'Error', }; log.error('检查用户购买记录失败', { error: errorData }); captureException(error); captureMessageWithContext('检查用户购买记录失败', errorData); } }; const handlePurchase = async () => { // 添加调试日志 log.info('handlePurchase 被调用', { loading, productsLength: products.length, selectedProductId: selectedProduct?.identifier, selectedProductTitle: selectedProduct?.title, agreementAccepted }); // 验证是否已同意协议 if (!agreementAccepted) { Alert.alert( '请阅读并同意相关协议', '购买前需要同意用户协议、会员协议和自动续费协议', [ { text: '确定', style: 'default', } ] ); return; } // 如果没有选中的产品但有可用产品,自动选择第一个 if (!selectedProduct && products.length > 0) { log.info('自动选择第一个可用产品', { firstProductId: products[0]?.identifier, firstProductTitle: products[0]?.title }); setSelectedProduct(products[0]); return; // 返回让用户确认选择后再点击 } // 验证是否选择了产品 if (!selectedProduct) { Alert.alert( '请选择会员套餐', '', [ { text: '确定', style: 'default', } ] ); return; } // 防止重复点击 if (loading) { return; } try { // 设置加载状态 setLoading(true); // 记录购买开始事件 capturePurchaseEvent('init', `开始购买: ${selectedProduct.identifier}`, { productIdentifier: selectedProduct.identifier, productTitle: selectedProduct.title, productPrice: selectedProduct.price }); // 执行购买 const { customerInfo, productIdentifier } = await Purchases.purchaseStoreProduct(selectedProduct); log.info('购买成功', { customerInfo, productIdentifier }); // 记录购买成功事件 capturePurchaseEvent('success', `购买成功: ${productIdentifier}`, { productIdentifier, hasActiveEntitlements: Object.keys(customerInfo.entitlements.active).length > 0, activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length, nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length, activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length }); // 购买成功后,监听器会自动处理后续逻辑(刷新用户信息、关闭弹窗等) log.info('购买流程完成,等待监听器处理后续逻辑'); } catch (error: any) { captureException(error); // 记录购买失败事件 capturePurchaseEvent('error', `购买失败: ${error.message || '未知错误'}`, { errorCode: error.code || null, errorMessage: error.message || '未知错误', productIdentifier: selectedProduct.identifier }); // 处理不同类型的购买错误 if (error.code === 1 || error.code === 'USER_CANCELLED') { // 用户取消购买 GlobalToast.show({ message: '购买已取消', }); } else if (error.code === 'ITEM_ALREADY_OWNED' || error.code === 'PRODUCT_ALREADY_PURCHASED') { // 商品已拥有 GlobalToast.show({ message: '您已拥有此商品', }); } else if (error.code === 'NETWORK_ERROR') { // 网络错误 GlobalToast.show({ message: '网络连接失败', }); } else if (error.code === 'PAYMENT_PENDING') { // 支付待处理 GlobalToast.show({ message: '支付正在处理中', }); } else if (error.code === 'INVALID_CREDENTIALS') { // 凭据无效 GlobalToast.show({ message: '账户验证失败', }); } else { // 其他错误 GlobalToast.show({ message: '购买失败', }); } } finally { // 确保在所有情况下都重置加载状态 setLoading(false); log.info('购买流程结束,加载状态已重置'); } }; const handleRestore = async () => { // 防止重复点击 if (restoring || loading) { return; } try { setRestoring(true); captureUserAction('开始恢复购买'); // 恢复购买 const customerInfo = await Purchases.restorePurchases(); log.info('恢复购买结果', { customerInfo }); captureMessageWithContext('恢复购买结果', { activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length, nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length, activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length, managementUrl: customerInfo.managementURL, originalAppUserId: customerInfo.originalAppUserId }); // 检查是否有有效的购买记录 const hasActiveEntitlements = Object.keys(customerInfo.entitlements.active).length > 0; const hasNonSubscriptionTransactions = customerInfo.nonSubscriptionTransactions.length > 0; const hasActiveSubscriptions = Object.keys(customerInfo.activeSubscriptions).length > 0; if (hasActiveEntitlements || hasNonSubscriptionTransactions || hasActiveSubscriptions) { // 检查具体的购买内容 let restoredProducts: string[] = []; // 检查权益 Object.keys(customerInfo.entitlements.active).forEach(key => { const entitlement = customerInfo.entitlements.active[key]; restoredProducts.push(entitlement.productIdentifier); }); // 检查非订阅购买(如终身会员) customerInfo.nonSubscriptionTransactions.forEach(transaction => { restoredProducts.push(transaction.productIdentifier); }); // 检查订阅 Object.keys(customerInfo.activeSubscriptions).forEach(productId => { restoredProducts.push(productId); }); log.info('恢复的产品', { restoredProducts }); capturePurchaseEvent('restore', '恢复购买成功', { restoredProducts, restoredProductsCount: restoredProducts.length }); try { // 调用后台服务接口进行票据匹配 captureUserAction('开始调用后台恢复购买接口'); // const restoreResponse = await UserApi.restorePurchase({ // customerInfo: { // originalAppUserId: customerInfo.originalAppUserId, // activeEntitlements: customerInfo.entitlements.active, // nonSubscriptionTransactions: customerInfo.nonSubscriptionTransactions, // activeSubscriptions: customerInfo.activeSubscriptions, // restoredProducts // } // }); // log.debug('后台恢复购买响应', { restoreResponse }); // captureMessageWithContext('后台恢复购买成功', { // responseData: restoreResponse, // restoredProductsCount: restoredProducts.length // }); // 刷新用户信息 // await refreshUserInfo(); // 调用购买成功回调 onPurchaseSuccess?.(); // 关闭弹窗 onClose?.(); GlobalToast.show({ message: '恢复购买成功', }); } catch (apiError: any) { // 安全地处理错误对象,避免循环引用 const errorData = { message: apiError?.message || '未知错误', code: apiError?.code || null, name: apiError?.name || 'Error', restoredProductsCount: restoredProducts.length }; log.error('后台恢复购买接口调用失败', { error: errorData }); captureException(apiError); captureMessageWithContext('后台恢复购买接口失败', errorData); // 即使后台接口失败,也显示恢复成功(因为 RevenueCat 已经确认有购买记录) // 但不关闭弹窗,让用户知道可能需要重试 GlobalToast.show({ message: '恢复购买部分失败', }); } } else { capturePurchaseEvent('restore', '没有找到购买记录', { hasActiveEntitlements, hasNonSubscriptionTransactions, hasActiveSubscriptions, activeEntitlementsCount: Object.keys(customerInfo.entitlements.active).length, nonSubscriptionTransactionsCount: customerInfo.nonSubscriptionTransactions.length, activeSubscriptionsCount: Object.keys(customerInfo.activeSubscriptions).length }); GlobalToast.show({ message: '没有找到购买记录', }); } } catch (error: any) { // 安全地处理错误对象,避免循环引用 const errorData = { message: error?.message || '未知错误', code: error?.code || null, name: error?.name || 'Error', }; log.error('恢复购买失败', { error: errorData }); captureException(error); // 记录恢复购买失败事件 capturePurchaseEvent('error', `恢复购买失败: ${errorData.message}`, errorData); // 处理特定的恢复购买错误 if (error.code === 'RESTORE_CANCELLED' || error.code === 'USER_CANCELLED') { GlobalToast.show({ message: '恢复购买已取消', }); } else if (error.code === 'NETWORK_ERROR') { GlobalToast.show({ message: '网络错误', }); } else if (error.code === 'INVALID_CREDENTIALS') { GlobalToast.show({ message: '账户验证失败', }); } else { GlobalToast.show({ message: '恢复购买失败', }); } } finally { // 确保在所有情况下都重置恢复状态 setRestoring(false); log.info('恢复购买流程结束,恢复状态已重置'); } }; const renderPlanCard = (product: PurchasesStoreProduct) => { const plan = DEFAULT_PLANS.find(p => p.id === product.identifier); if (!plan) { return null; } const isSelected = selectedProduct === product; const displayTitle = product.title || plan.fallbackTitle; const priceLabel = product.priceString || ''; const styleConfig = PLAN_STYLE_CONFIG[plan.type]; return ( !loading && product && setSelectedProduct(product)} disabled={loading} activeOpacity={loading ? 1 : 0.8} accessible={true} accessibilityLabel={`${displayTitle} ${priceLabel}`} accessibilityHint={loading ? '购买进行中,无法切换套餐' : `选择${displayTitle}套餐`} accessibilityState={{ disabled: loading, selected: isSelected }} > {plan.tag && ( {plan.tag} )} {displayTitle} {priceLabel || '--'} {plan.originalPrice && ( {plan.originalPrice} )} {plan.subtitle} ); }; return ( {/* 半透明背景 */} {/* 悬浮返回按钮 - 移到 ScrollView 外部以确保始终在最上层 */} {isLiquidGlassAvailable() ? ( ) : ( )} 会员套餐 灵活选择,跟随节奏稳步提升 {products.length === 0 ? ( 暂未获取到会员商品,请在 RevenueCat 中配置 iOS 产品并同步到当前 Offering。 ) : ( <> {products.map(renderPlanCard)} {selectedProduct && ( {getTipsContent(selectedProduct)} )} )} 权益对比 核心权益一目了然,选择更安心 权益 VIP 普通用户 {BENEFIT_COMPARISON.map((row, index) => ( {row.title} {row.description && ( {row.description} )} {/* VIP 权限列 */} {getPermissionIcon(row.vip.type, true)} {row.vip.vipText || row.vip.text} {/* 普通用户权限列 */} {getPermissionIcon(row.regular.type, false)} {row.regular.vipText || row.regular.text} ))} 开通即视为同意 { Linking.openURL(USER_AGREEMENT_URL); captureMessage('click user agreement'); }} > 《用户协议》 | { captureMessage('click membership agreement'); }} > 《会员协议》 | { captureMessage('click auto renewal agreement'); }} > 《自动续费协议》 {restoring ? ( 恢复中... ) : ( 恢复购买 )} {/* 悬浮购买按钮 */} {isLiquidGlassAvailable() ? ( {loading ? ( 正在处理购买... ) : ( 立即订阅 )} ) : ( {loading ? ( 正在处理购买... ) : ( 立即订阅 )} )} ); } const styles = StyleSheet.create({ overlay: { flex: 1, justifyContent: 'flex-end', }, backdrop: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0, 0, 0, 0.35)', }, modalContainer: { height: height * 0.92, backgroundColor: '#F5F6FA', borderTopLeftRadius: 28, borderTopRightRadius: 28, overflow: 'hidden', }, modalContent: { flex: 1, }, modalContentContainer: { paddingHorizontal: 20, paddingBottom: 100, // 增加底部内边距,避免内容被悬浮按钮遮挡 paddingTop: 16, }, floatingBackButton: { width: 40, height: 40, borderRadius: 20, alignItems: 'center', justifyContent: 'center', overflow: 'hidden', }, floatingBackButtonContainer: { position: 'absolute', top: 20, left: 20, zIndex: 10, // 确保按钮在最上层 }, fallbackBackButton: { backgroundColor: 'rgba(255, 255, 255, 0.9)', }, sectionCard: { backgroundColor: '#FFFFFF', borderRadius: 20, paddingHorizontal: 20, paddingVertical: 18, marginBottom: 20, shadowColor: '#1C1C1E', shadowOpacity: 0.06, shadowRadius: 12, shadowOffset: { width: 0, height: 6 }, elevation: 2, }, sectionTitleRow: { flexDirection: 'row', alignItems: 'center', }, sectionTitleBadge: { width: 28, height: 28, borderRadius: 14, alignItems: 'center', justifyContent: 'center', backgroundColor: '#F4EFFD', marginRight: 8, }, sectionTitle: { fontSize: 18, fontWeight: '700', color: '#2B2B2E', }, sectionSubtitle: { fontSize: 13, color: '#6B6B73', marginTop: 6, marginBottom: 16, }, configurationNotice: { borderRadius: 16, padding: 16, backgroundColor: '#FFF4E5', }, configurationText: { fontSize: 14, color: '#B86A04', textAlign: 'center', lineHeight: 20, }, plansContainer: { flexDirection: 'row', justifyContent: 'space-between', }, planCardWrapper: { flex: 1, marginHorizontal: 4, borderRadius: 18, overflow: 'hidden', borderWidth: 1, borderColor: 'rgba(123,44,191,0.08)', }, planCardWrapperSelected: { borderColor: '#7B2CBF', shadowColor: '#7B2CBF', shadowOpacity: 0.18, shadowRadius: 16, shadowOffset: { width: 0, height: 8 }, elevation: 4, }, planCardGradient: { flex: 1, paddingHorizontal: 16, paddingVertical: 18, minHeight: 170, justifyContent: 'space-between', }, planTag: { alignSelf: 'flex-start', backgroundColor: '#2F2F36', borderRadius: 14, paddingHorizontal: 12, paddingVertical: 4, marginBottom: 12, }, planTagText: { color: '#FFFFFF', fontSize: 11, fontWeight: '600', }, planCardTitle: { fontSize: 18, fontWeight: '700', color: '#241F1F', }, planCardPrice: { fontSize: 28, fontWeight: '700', marginTop: 12, }, planCardOriginalPrice: { fontSize: 13, color: '#8E8EA1', textDecorationLine: 'line-through', marginTop: 2, }, planCardDescription: { fontSize: 12, color: '#6C6C77', lineHeight: 17, }, planCardTopSection: { flex: 1, justifyContent: 'flex-start', }, planCardMiddleSection: { flex: 1, justifyContent: 'center', alignItems: 'flex-start', }, planCardBottomSection: { flex: 1, justifyContent: 'flex-end', }, tipsContainer: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#FEF4E6', borderRadius: 14, paddingHorizontal: 12, paddingVertical: 10, marginTop: 16, }, tipsText: { flex: 1, fontSize: 12, color: '#9B6200', marginLeft: 6, lineHeight: 16, }, comparisonTable: { borderRadius: 16, overflow: 'hidden', borderWidth: 1, borderColor: '#ECECF3', }, tableRow: { flexDirection: 'row', alignItems: 'center', paddingVertical: 14, paddingHorizontal: 16, backgroundColor: '#FFFFFF', }, tableHeader: { backgroundColor: '#F8F8FF', }, tableHeaderText: { fontSize: 12, fontWeight: '600', color: '#575764', textTransform: 'uppercase', letterSpacing: 0.4, }, tableCellText: { fontSize: 13, color: '#3E3E44', }, tableTitleCell: { flex: 1.5, justifyContent: 'center', }, tableVipCell: { flex: 0.8, alignItems: 'center', justifyContent: 'center', }, tableNormalCell: { flex: 0.8, alignItems: 'center', justifyContent: 'center', }, tableRowAlt: { backgroundColor: '#FBFBFF', }, bottomSection: { backgroundColor: '#FFFFFF', borderRadius: 20, paddingHorizontal: 20, paddingVertical: 20, marginBottom: 10, shadowColor: '#1C1C1E', shadowOpacity: 0.04, shadowRadius: 12, shadowOffset: { width: 0, height: 6 }, elevation: 1, }, purchaseButton: { borderRadius: 28, height: 52, justifyContent: 'center', alignItems: 'center', overflow: 'hidden', }, purchaseButtonContent: { flex: 1, width: '100%', justifyContent: 'center', alignItems: 'center', }, floatingPurchaseContainer: { position: 'absolute', bottom: 34, // 底部安全区域 left: 20, right: 20, }, fallbackPurchaseButton: { backgroundColor: '#151515', }, disabledButton: { backgroundColor: '#C6C6C8', }, purchaseButtonText: { color: '#FFFFFF', fontSize: 18, fontWeight: '700', }, loadingContainer: { flexDirection: 'row', alignItems: 'center', }, loadingSpinner: { marginRight: 8, }, agreementRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', flexWrap: 'nowrap', marginBottom: 16, }, agreementPrefix: { fontSize: 10, color: '#666672', marginRight: 4, }, agreementLink: { fontSize: 10, color: '#E91E63', textDecorationLine: 'underline', fontWeight: '500', marginHorizontal: 2, }, agreementSeparator: { fontSize: 10, color: '#A0A0B0', marginHorizontal: 2, }, restoreButton: { alignSelf: 'center', paddingVertical: 6, }, restoreButtonText: { color: '#6F6F7A', fontSize: 14, fontWeight: '500', }, disabledRestoreButton: { opacity: 0.5, }, restoreButtonContent: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, restoreButtonLoader: { marginRight: 8, }, disabledPlanCard: { opacity: 0.5, }, // 新增样式:权限相关 tableDescriptionText: { fontSize: 11, color: '#8E8E93', marginTop: 2, lineHeight: 14, }, permissionContainer: { alignItems: 'center', justifyContent: 'center', flexDirection: 'column', paddingVertical: 4, }, permissionText: { fontSize: 10, color: '#6B6B73', marginTop: 4, textAlign: 'center', lineHeight: 12, }, });