| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987 |
- import { Image } from "expo-image";
- import { useRouter } from "expo-router";
- import React, {
- forwardRef,
- useImperativeHandle,
- useRef,
- useState,
- } from "react";
- import {
- ActivityIndicator,
- Alert,
- AppState,
- Dimensions,
- Modal,
- ScrollView,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
- } from "react-native";
- import { applyOrder, getApplyResult, previewOrder } from "@/services/award";
- import Alipay from "expo-native-alipay";
- import {
- LotteryResultModal,
- LotteryResultModalRef,
- } from "./LotteryResultModal";
- const { width: SCREEN_WIDTH } = Dimensions.get("window");
- // 等级配置
- const LEVEL_MAP: Record<string, { title: string; color: string }> = {
- A: { title: "超神款", color: "#FF4444" },
- B: { title: "欧皇款", color: "#FF9600" },
- C: { title: "隐藏款", color: "#9B59B6" },
- D: { title: "普通款", color: "#666666" },
- };
- interface CheckoutModalProps {
- data: any;
- poolId: string;
- onSuccess: (param: { num: number; tradeNo: string }) => void;
- boxNumber?: string;
- }
- export interface CheckoutModalRef {
- show: (
- num: number,
- preview: any,
- boxNum?: string,
- seatNumbers?: number[],
- packFlag?: boolean,
- ) => void;
- showFreedom: () => void;
- close: () => void;
- }
- interface LotteryItem {
- id: string;
- name: string;
- cover: string;
- level: string;
- spu?: { marketPrice: number };
- }
- // 自由购买数量选项
- const FREEDOM_NUMS = [10, 20, 30, 40, 50];
- export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
- ({ data, poolId, onSuccess, boxNumber }, ref) => {
- const router = useRouter();
- const lotteryResultRef = useRef<LotteryResultModalRef>(null);
- const [visible, setVisible] = useState(false);
- const [num, setNum] = useState(1);
- const [checked, setChecked] = useState(true);
- const [loading, setLoading] = useState(false);
- const [freedomNum, setFreedomNum] = useState(10);
- const [freedomSelectVisible, setFreedomSelectVisible] = useState(false);
- // 预览数据
- const [coin, setCoin] = useState<number | null>(null);
- const [couponAmount, setCouponAmount] = useState<number | null>(null);
- const [lastPrice, setLastPrice] = useState<number | null>(null);
- const [magic, setMagic] = useState<any>(null);
- const [cash, setCash] = useState<any>(null);
- const [cashChecked, setCashChecked] = useState(false);
- // 盒子相关
- const [boxNum, setBoxNum] = useState<string | undefined>(boxNumber);
- const [seatNumbers, setSeatNumbers] = useState<number[] | undefined>();
- const [packFlag, setPackFlag] = useState<boolean | undefined>();
- // 抽奖结果
- const [resultVisible, setResultVisible] = useState(false);
- const [resultLoading, setResultLoading] = useState(false);
- const [resultList, setResultList] = useState<LotteryItem[]>([]);
- // 设置预览数据
- const setPreviewData = (previewData: any) => {
- setCoin(previewData.magicAmount || null);
- setCouponAmount(previewData.couponAmount || null);
- setLastPrice(previewData.paymentAmount);
- setMagic(previewData.magic || null);
- if (
- previewData.cash &&
- previewData.cash.balance > previewData.paymentAmount
- ) {
- setCash(previewData.cash);
- setCashChecked(true);
- } else {
- setCash(previewData.cash || null);
- setCashChecked(false);
- }
- };
- // ... (Imports handled via separate edit or assume existing)
- const [payConfig, setPayConfig] = useState<any>(null);
- const [paymentMethod, setPaymentMethod] = useState<"ALIPAY">("ALIPAY");
- // ...
- useImperativeHandle(ref, () => ({
- show: (
- n: number,
- previewData: any = {},
- bNum?: string,
- seats?: number[],
- pack?: boolean,
- ) => {
- setNum(n);
- setBoxNum(bNum);
- setSeatNumbers(seats);
- setPackFlag(pack || undefined);
- setPreviewData(previewData);
- setVisible(true);
- fetchPayConfig();
- },
- showFreedom: () => {
- setFreedomNum(10);
- setFreedomSelectVisible(true);
- },
- close: () => {
- setVisible(false);
- setFreedomSelectVisible(false);
- setResultVisible(false);
- },
- }));
- const handleFreedomSelect = async (selectedNum: number) => {
- setFreedomSelectVisible(false);
- setLoading(true);
- try {
- const preview = await previewOrder(poolId, selectedNum);
- if (preview) {
- setNum(selectedNum);
- setFreedomNum(selectedNum);
- setPreviewData(preview);
- setVisible(true);
- fetchPayConfig();
- }
- } catch (error: any) {
- Alert.alert("提示", error?.message || "获取订单信息失败");
- } finally {
- setLoading(false);
- }
- };
- const close = () => {
- setVisible(false);
- setFreedomSelectVisible(false);
- };
- const closeResult = () => {
- setResultVisible(false);
- setResultList([]);
- onSuccess({ tradeNo: "", num });
- };
- const fetchPayConfig = async () => {
- try {
- const res = await import("@/services/user").then((m) =>
- m.getParamConfig("wxpay_alipay"),
- );
- if (res && res.data) {
- setPayConfig(JSON.parse(res.data));
- }
- } catch (e) {
- console.log("Fetch Pay Config Error", e);
- }
- };
- // ...
- const pay = async () => {
- if (loading) return;
- if (!checked) {
- Alert.alert("提示", "请同意《宝箱服务协议》");
- return;
- }
- setLoading(true);
- try {
- let paymentType = "";
- // Prioritize Wallet if checked
- if (cashChecked) {
- paymentType = "WALLET";
- } else {
- // 默认只支持支付宝
- paymentType = "ALIPAY_APP";
- }
- const payNum = packFlag ? 1 : num;
- console.log("Submit Order Params:", {
- poolId,
- quantity: payNum,
- paymentType,
- boxNum,
- seatNumbers,
- packFlag,
- payConfig,
- });
- const res = await applyOrder(
- poolId,
- payNum,
- paymentType,
- boxNum,
- seatNumbers,
- packFlag,
- );
- console.log("Apply Order Result:", res);
- if (!res) {
- Alert.alert("提示", "订单创建失败");
- return;
- }
- if (res.paySuccess) {
- // Direct Success (Wallet)
- handleSuccess(res.bizTradeNo || res.tradeNo);
- } else if (res.payInfo) {
- // Handle Native Payment
- handleNativePay(
- res.payInfo,
- paymentType,
- res.bizTradeNo || res.tradeNo,
- );
- } else {
- Alert.alert("提示", res?.message || "支付失败,请重试");
- }
- } catch (error: any) {
- Alert.alert("支付失败", error?.message || "请稍后重试");
- } finally {
- setLoading(false);
- }
- };
- const handleSuccess = (tradeNo: string) => {
- setVisible(false);
- router.push({
- pathname: "/lottery" as any,
- params: { tradeNo, num, poolId },
- });
- onSuccess({ tradeNo, num });
- };
- const handleNativePay = async (
- payInfo: string,
- type: string,
- tradeNo: string,
- ) => {
- if (type === "ALIPAY" || type.includes("ALIPAY")) {
- let appStateSub: any = null;
- let isResolved = false;
- try {
- Alipay.setAlipayScheme("alipay2021005175632205");
- // Watch for app returning to foreground as a fallback
- appStateSub = AppState.addEventListener(
- "change",
- async (nextAppState) => {
- if (nextAppState === "active" && !isResolved) {
- // 瞬间回退没有拿到原生支付结果时,立即开始每秒轮询后端
- let attempts = 0;
- const pollInterval = setInterval(async () => {
- if (isResolved) {
- clearInterval(pollInterval);
- return;
- }
- attempts++;
- try {
- const res = await getApplyResult(tradeNo);
- if (
- res?.paySuccess ||
- (res?.inventoryList && res.inventoryList.length > 0)
- ) {
- isResolved = true;
- clearInterval(pollInterval);
- handleSuccess(tradeNo);
- }
- } catch (e) {
- console.log("Fallback poll failed", e);
- }
- if (attempts >= 5) {
- // 5秒后放弃
- clearInterval(pollInterval);
- }
- }, 1000);
- }
- },
- );
- const result = await Alipay.pay(payInfo);
- if (isResolved) return; // if fallback already worked
- isResolved = true;
- console.log("Alipay Result:", result);
- const status = result?.resultStatus;
- if (status === "9000") {
- handleSuccess(tradeNo);
- } else if (status === "6001") {
- Alert.alert("提示", "用户取消支付");
- } else {
- // Also poll server just in case the status is wrong but it succeeded
- try {
- const res = await getApplyResult(tradeNo);
- if (
- res?.paySuccess ||
- (res?.inventoryList && res.inventoryList.length > 0)
- ) {
- handleSuccess(tradeNo);
- return;
- }
- } catch (ignore) {}
- Alert.alert("支付中断", `状态码: ${status}`);
- }
- } catch (e: any) {
- isResolved = true;
- console.log("Alipay Error:", e);
- Alert.alert("支付异常", e.message || "调用支付宝失败");
- } finally {
- if (appStateSub) {
- appStateSub.remove();
- }
- }
- } else {
- Alert.alert("提示", "微信支付暂未实现");
- }
- };
- // 获取抽奖结果(10发以下用弹窗)
- const fetchLotteryResult = async (tradeNo: string) => {
- setResultLoading(true);
- setResultVisible(true);
- setResultList([]);
- let attempts = 0;
- const maxAttempts = 5;
- const poll = async () => {
- try {
- const res = await getApplyResult(tradeNo);
- if (res?.inventoryList && res.inventoryList.length > 0) {
- setResultList(res.inventoryList);
- setResultLoading(false);
- // 不在这里调用 onSuccess,等用户关闭弹窗时再调用
- } else if (attempts < maxAttempts) {
- attempts++;
- setTimeout(poll, 1000);
- } else {
- setResultLoading(false);
- Alert.alert("提示", "获取结果超时,请在仓库中查看");
- }
- } catch {
- if (attempts < maxAttempts) {
- attempts++;
- setTimeout(poll, 1000);
- } else {
- setResultLoading(false);
- Alert.alert("提示", "获取结果失败,请在仓库中查看");
- }
- }
- };
- poll();
- };
- const displayPrice = lastPrice ?? (data?.price || 0) * num;
- return (
- <>
- {/* 10发以上的全屏抽奖结果弹窗 */}
- <LotteryResultModal
- ref={lotteryResultRef}
- onClose={() => {
- // 抽奖结果弹窗关闭后刷新数据
- onSuccess({ tradeNo: "", num });
- }}
- onGoStore={() => {
- onSuccess({ tradeNo: "", num });
- router.replace("/store" as any);
- }}
- />
- {/* 自由购买数量选择弹窗 */}
- <Modal
- visible={freedomSelectVisible}
- transparent
- animationType="fade"
- onRequestClose={() => setFreedomSelectVisible(false)}
- >
- <View style={styles.overlay}>
- <TouchableOpacity
- style={styles.mask}
- activeOpacity={1}
- onPress={() => setFreedomSelectVisible(false)}
- />
- <View style={styles.freedomContainer}>
- <View style={styles.header}>
- <Text style={styles.title}>购买多盒</Text>
- <TouchableOpacity
- onPress={() => setFreedomSelectVisible(false)}
- style={styles.closeBtn}
- >
- <Text style={styles.closeText}>×</Text>
- </TouchableOpacity>
- </View>
- <View style={styles.freedomContent}>
- <View style={styles.freedomBtnList}>
- {FREEDOM_NUMS.map((item) => (
- <TouchableOpacity
- key={item}
- style={[
- styles.freedomBtn,
- freedomNum === item && styles.freedomBtnActive,
- ]}
- onPress={() => setFreedomNum(item)}
- >
- <Text
- style={[
- styles.freedomBtnText,
- freedomNum === item && styles.freedomBtnTextActive,
- ]}
- >
- {item}
- <Text style={styles.freedomUnit}>盒</Text>
- </Text>
- {freedomNum === item && (
- <Text style={styles.checkIcon}>✓</Text>
- )}
- </TouchableOpacity>
- ))}
- </View>
- <TouchableOpacity
- style={[
- styles.freedomSubmitBtn,
- loading && styles.payBtnDisabled,
- ]}
- onPress={() => handleFreedomSelect(freedomNum)}
- disabled={loading}
- >
- {loading ? (
- <ActivityIndicator color="#fff" size="small" />
- ) : (
- <Text style={styles.freedomSubmitText}>
- 确认 ¥{(data?.price || 0) * freedomNum}
- </Text>
- )}
- </TouchableOpacity>
- </View>
- </View>
- </View>
- </Modal>
- {/* 支付确认弹窗 */}
- <Modal
- visible={visible}
- transparent
- animationType="slide"
- onRequestClose={close}
- >
- <View style={styles.overlay}>
- <TouchableOpacity
- style={styles.mask}
- activeOpacity={1}
- onPress={close}
- />
- <View style={styles.container}>
- <View style={styles.header}>
- <Text style={styles.title}>{data?.name}</Text>
- <TouchableOpacity onPress={close} style={styles.closeBtn}>
- <Text style={styles.closeText}>×</Text>
- </TouchableOpacity>
- </View>
- <View style={styles.content}>
- <View style={styles.row}>
- <Text style={styles.label}>购买件数</Text>
- <Text style={styles.priceText}>
- ¥{data?.price} x {num}
- </Text>
- </View>
- <View style={styles.row}>
- <Text style={styles.label}>优惠券</Text>
- <Text
- style={[
- styles.valueText,
- couponAmount ? styles.themeColor : {},
- ]}
- >
- {couponAmount
- ? `已使用优惠¥${couponAmount}`
- : "暂无优惠券可选"}
- </Text>
- </View>
- {magic && magic.balance > 0 && (
- <View style={styles.row}>
- <View style={styles.rowLeft}>
- <Text style={styles.label}>果实</Text>
- <Text style={styles.balanceText}>
- (剩余:{magic.balance})
- </Text>
- </View>
- <Text style={styles.balanceText}>
- 已抵扣 <Text style={styles.themeColor}>¥{coin || 0}</Text>
- </Text>
- </View>
- )}
- {cash && (
- <View style={styles.row}>
- <View style={styles.rowLeft}>
- <Text style={styles.label}>钱包支付</Text>
- <Text style={styles.themeColor}>
- (余额:¥{cash.balance})
- </Text>
- </View>
- <TouchableOpacity
- style={[styles.radio, cashChecked && styles.radioChecked]}
- onPress={() => setCashChecked(!cashChecked)}
- >
- {cashChecked && <View style={styles.radioInner} />}
- </TouchableOpacity>
- </View>
- )}
- {/* Payment Methods Section */}
- {(!cashChecked ||
- (cash &&
- cash.balance < (lastPrice || (data?.price || 0) * num))) &&
- payConfig ? (
- <View style={styles.paymentSection}>
- <Text style={styles.sectionTitle}>支付方式</Text>
- {payConfig?.alipay?.enabled ? (
- <TouchableOpacity
- style={styles.payOption}
- onPress={() => setPaymentMethod("ALIPAY")}
- >
- <View style={styles.rowLeft}>
- <Text style={styles.payLabel}>支付宝支付</Text>
- </View>
- <View
- style={[
- styles.radio,
- paymentMethod === "ALIPAY" && styles.radioChecked,
- ]}
- >
- {paymentMethod === "ALIPAY" ? (
- <View style={styles.radioInner} />
- ) : null}
- </View>
- </TouchableOpacity>
- ) : null}
- </View>
- ) : null}
- <View style={styles.agreementRow}>
- <View style={styles.agreementLeft}>
- <Text style={styles.agreementText}>
- 我已满18周岁,已阅读并同意
- <Text style={styles.link}>《宝箱服务协议》</Text>
- </Text>
- <Text style={styles.tips}>
- 宝箱商品存在概率性,请谨慎消费
- </Text>
- </View>
- <TouchableOpacity
- style={[styles.radio, checked && styles.radioChecked]}
- onPress={() => setChecked(!checked)}
- >
- {checked && <View style={styles.radioInner} />}
- </TouchableOpacity>
- </View>
- </View>
- <View style={styles.footer}>
- <View style={styles.priceInfo}>
- <Text style={styles.totalLabel}>实付:</Text>
- <Text style={styles.totalPrice}>
- ¥{displayPrice.toFixed(2)}
- </Text>
- </View>
- <TouchableOpacity
- style={[styles.payBtn, loading && styles.payBtnDisabled]}
- onPress={pay}
- disabled={loading}
- >
- {loading ? (
- <ActivityIndicator color="#fff" size="small" />
- ) : (
- <Text style={styles.payBtnText}>立即支付</Text>
- )}
- </TouchableOpacity>
- </View>
- </View>
- </View>
- </Modal>
- {/* 抽奖结果弹窗 */}
- <Modal
- visible={resultVisible}
- transparent
- animationType="fade"
- onRequestClose={closeResult}
- >
- <View style={styles.resultOverlay}>
- <View style={styles.resultContainer}>
- <View style={styles.resultHeader}>
- <Text style={styles.resultTitle}>🎉 恭喜您获得 🎉</Text>
- <TouchableOpacity
- onPress={closeResult}
- style={styles.resultCloseBtn}
- >
- <Text style={styles.closeText}>×</Text>
- </TouchableOpacity>
- </View>
- {resultLoading ? (
- <View style={styles.resultLoading}>
- <ActivityIndicator size="large" color="#ff9600" />
- <Text style={styles.resultLoadingText}>正在开启宝箱...</Text>
- </View>
- ) : (
- <ScrollView
- style={styles.resultScroll}
- showsVerticalScrollIndicator={false}
- >
- <View style={styles.resultList}>
- {resultList.map((item, index) => (
- <View key={item.id || index} style={styles.resultItem}>
- <View
- style={[
- styles.levelBadge,
- {
- backgroundColor:
- LEVEL_MAP[item.level]?.color || "#666",
- },
- ]}
- >
- <Text style={styles.levelText}>
- {LEVEL_MAP[item.level]?.title || item.level}
- </Text>
- </View>
- <View style={styles.resultImageBox}>
- <Image
- source={{ uri: item.cover }}
- style={styles.resultImage}
- contentFit="contain"
- />
- </View>
- <Text style={styles.resultName} numberOfLines={2}>
- {item.name}
- </Text>
- {item.spu?.marketPrice && (
- <Text style={styles.resultPrice}>
- 参考价:¥{item.spu.marketPrice}
- </Text>
- )}
- </View>
- ))}
- </View>
- </ScrollView>
- )}
- <View style={styles.resultFooter}>
- <TouchableOpacity
- style={styles.resultBtn}
- onPress={closeResult}
- >
- <Text style={styles.resultBtnText}>继续抽奖</Text>
- </TouchableOpacity>
- </View>
- </View>
- </View>
- </Modal>
- </>
- );
- },
- );
- const styles = StyleSheet.create({
- overlay: {
- flex: 1,
- backgroundColor: "rgba(0,0,0,0.5)",
- justifyContent: "flex-end",
- },
- mask: { flex: 1 },
- container: {
- backgroundColor: "#fff",
- borderTopLeftRadius: 20,
- borderTopRightRadius: 20,
- paddingBottom: 34,
- },
- header: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "center",
- padding: 15,
- borderBottomWidth: 1,
- borderBottomColor: "#eee",
- position: "relative",
- },
- title: { fontSize: 16, fontWeight: "600", color: "#333" },
- closeBtn: { position: "absolute", right: 15, top: 10 },
- closeText: { fontSize: 24, color: "#999" },
- content: { padding: 15 },
- row: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
- paddingVertical: 10,
- },
- rowLeft: { flexDirection: "row", alignItems: "center", flex: 1 },
- label: { fontSize: 14, color: "#333" },
- priceText: { fontSize: 14, color: "#ff9600", fontWeight: "600" },
- valueText: { fontSize: 12, color: "#999" },
- themeColor: { color: "#ff9600" },
- balanceText: { fontSize: 12, color: "#999", marginLeft: 5 },
- radio: {
- width: 20,
- height: 20,
- borderRadius: 10,
- borderWidth: 2,
- borderColor: "#ddd",
- justifyContent: "center",
- alignItems: "center",
- },
- radioChecked: { borderColor: "#ff9600" },
- radioInner: {
- width: 10,
- height: 10,
- borderRadius: 5,
- backgroundColor: "#ff9600",
- },
- agreementRow: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "flex-start",
- paddingVertical: 10,
- marginTop: 10,
- },
- agreementLeft: { flex: 1, marginRight: 10 },
- agreementText: { fontSize: 12, color: "#333", lineHeight: 18 },
- link: { color: "#ff9600" },
- tips: { fontSize: 11, color: "#999", marginTop: 5 },
- footer: {
- flexDirection: "row",
- alignItems: "center",
- justifyContent: "space-between",
- paddingHorizontal: 15,
- paddingTop: 15,
- borderTopWidth: 1,
- borderTopColor: "#eee",
- },
- priceInfo: { flexDirection: "row", alignItems: "center" },
- totalLabel: { fontSize: 14, color: "#333" },
- totalPrice: { fontSize: 20, color: "#ff9600", fontWeight: "bold" },
- payBtn: {
- backgroundColor: "#ff9600",
- paddingHorizontal: 30,
- paddingVertical: 12,
- borderRadius: 25,
- },
- payBtnDisabled: { opacity: 0.6 },
- payBtnText: { color: "#fff", fontSize: 16, fontWeight: "600" },
- // 自由购买弹窗
- freedomContainer: {
- backgroundColor: "#fff",
- borderTopLeftRadius: 20,
- borderTopRightRadius: 20,
- paddingBottom: 34,
- },
- freedomContent: { padding: 20 },
- freedomBtnList: {
- flexDirection: "row",
- flexWrap: "wrap",
- justifyContent: "space-between",
- },
- freedomBtn: {
- width: "48%",
- height: 50,
- backgroundColor: "#fff",
- borderRadius: 8,
- justifyContent: "center",
- alignItems: "center",
- marginBottom: 12,
- borderWidth: 1,
- borderColor: "#eee",
- position: "relative",
- },
- freedomBtnActive: { backgroundColor: "#F1423D", borderColor: "#F1423D" },
- freedomBtnText: { fontSize: 18, fontWeight: "bold", color: "#333" },
- freedomBtnTextActive: { color: "#fff" },
- freedomUnit: { fontSize: 12, fontWeight: "500" },
- checkIcon: {
- position: "absolute",
- bottom: 0,
- right: 0,
- backgroundColor: "#fff",
- color: "#F1423D",
- fontSize: 12,
- paddingHorizontal: 6,
- paddingVertical: 2,
- borderTopLeftRadius: 8,
- borderBottomRightRadius: 8,
- },
- freedomSubmitBtn: {
- backgroundColor: "#ff9600",
- height: 50,
- borderRadius: 25,
- justifyContent: "center",
- alignItems: "center",
- marginTop: 20,
- },
- freedomSubmitText: { color: "#fff", fontSize: 16, fontWeight: "600" },
- // 抽奖结果弹窗
- resultOverlay: {
- flex: 1,
- backgroundColor: "rgba(0,0,0,0.7)",
- justifyContent: "center",
- alignItems: "center",
- padding: 20,
- },
- resultContainer: {
- width: "100%",
- maxHeight: "80%",
- backgroundColor: "#fff",
- borderRadius: 16,
- overflow: "hidden",
- },
- resultHeader: {
- alignItems: "center",
- padding: 20,
- backgroundColor: "#ff9600",
- position: "relative",
- },
- resultTitle: {
- fontSize: 20,
- fontWeight: "bold",
- color: "#fff",
- },
- resultCloseBtn: {
- position: "absolute",
- right: 15,
- top: 15,
- width: 30,
- height: 30,
- backgroundColor: "rgba(255,255,255,0.3)",
- borderRadius: 15,
- justifyContent: "center",
- alignItems: "center",
- },
- resultLoading: {
- padding: 60,
- alignItems: "center",
- },
- resultLoadingText: {
- marginTop: 15,
- fontSize: 14,
- color: "#666",
- },
- resultScroll: {
- maxHeight: 400,
- },
- resultList: {
- flexDirection: "row",
- flexWrap: "wrap",
- padding: 10,
- justifyContent: "space-between",
- },
- resultItem: {
- width: (SCREEN_WIDTH - 80) / 2,
- backgroundColor: "#f9f9f9",
- borderRadius: 10,
- padding: 10,
- marginBottom: 10,
- alignItems: "center",
- },
- levelBadge: {
- paddingHorizontal: 10,
- paddingVertical: 3,
- borderRadius: 10,
- marginBottom: 8,
- },
- levelText: {
- color: "#fff",
- fontSize: 11,
- fontWeight: "bold",
- },
- resultImageBox: {
- width: "100%",
- aspectRatio: 1,
- backgroundColor: "#fff",
- borderRadius: 8,
- overflow: "hidden",
- },
- resultImage: {
- width: "100%",
- height: "100%",
- },
- resultName: {
- fontSize: 12,
- color: "#333",
- textAlign: "center",
- marginTop: 8,
- lineHeight: 16,
- },
- resultPrice: {
- fontSize: 10,
- color: "#999",
- marginTop: 4,
- },
- resultFooter: {
- padding: 15,
- borderTopWidth: 1,
- borderTopColor: "#eee",
- },
- resultBtn: {
- backgroundColor: "#ff9600",
- height: 46,
- borderRadius: 23,
- justifyContent: "center",
- alignItems: "center",
- },
- resultBtnText: {
- color: "#fff",
- fontSize: 16,
- fontWeight: "600",
- },
- paymentSection: {
- marginTop: 20,
- borderTopWidth: 1,
- borderTopColor: "#f0f0f0",
- paddingTop: 10,
- },
- sectionTitle: {
- fontSize: 14,
- fontWeight: "bold",
- marginBottom: 10,
- color: "#333",
- },
- payOption: {
- flexDirection: "row",
- justifyContent: "space-between",
- alignItems: "center",
- paddingVertical: 12,
- borderBottomWidth: 1,
- borderBottomColor: "#f9f9f9",
- },
- payLabel: {
- fontSize: 14,
- color: "#333",
- marginLeft: 10,
- },
- });
|