CheckoutModal.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. import { Image } from 'expo-image';
  2. import { useRouter } from 'expo-router';
  3. import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
  4. import {
  5. ActivityIndicator,
  6. Alert,
  7. Dimensions,
  8. Modal,
  9. ScrollView,
  10. StyleSheet,
  11. Text,
  12. TouchableOpacity,
  13. View
  14. } from 'react-native';
  15. import { applyOrder, getApplyResult, previewOrder } from '@/services/award';
  16. import { LotteryResultModal, LotteryResultModalRef } from './LotteryResultModal';
  17. const { width: SCREEN_WIDTH } = Dimensions.get('window');
  18. // 等级配置
  19. const LEVEL_MAP: Record<string, { title: string; color: string }> = {
  20. A: { title: '超神款', color: '#FF4444' },
  21. B: { title: '欧皇款', color: '#FF9600' },
  22. C: { title: '隐藏款', color: '#9B59B6' },
  23. D: { title: '普通款', color: '#666666' },
  24. };
  25. interface CheckoutModalProps {
  26. data: any;
  27. poolId: string;
  28. onSuccess: (param: { num: number; tradeNo: string }) => void;
  29. boxNumber?: string;
  30. }
  31. export interface CheckoutModalRef {
  32. show: (num: number, preview: any, boxNum?: string, seatNumbers?: number[], packFlag?: boolean) => void;
  33. showFreedom: () => void;
  34. close: () => void;
  35. }
  36. interface LotteryItem {
  37. id: string;
  38. name: string;
  39. cover: string;
  40. level: string;
  41. spu?: { marketPrice: number };
  42. }
  43. // 自由购买数量选项
  44. const FREEDOM_NUMS = [10, 20, 30, 40, 50];
  45. export const CheckoutModal = forwardRef<CheckoutModalRef, CheckoutModalProps>(
  46. ({ data, poolId, onSuccess, boxNumber }, ref) => {
  47. const router = useRouter();
  48. const lotteryResultRef = useRef<LotteryResultModalRef>(null);
  49. const [visible, setVisible] = useState(false);
  50. const [num, setNum] = useState(1);
  51. const [checked, setChecked] = useState(true);
  52. const [loading, setLoading] = useState(false);
  53. const [freedomNum, setFreedomNum] = useState(10);
  54. const [freedomSelectVisible, setFreedomSelectVisible] = useState(false);
  55. // 预览数据
  56. const [coin, setCoin] = useState<number | null>(null);
  57. const [couponAmount, setCouponAmount] = useState<number | null>(null);
  58. const [lastPrice, setLastPrice] = useState<number | null>(null);
  59. const [magic, setMagic] = useState<any>(null);
  60. const [cash, setCash] = useState<any>(null);
  61. const [cashChecked, setCashChecked] = useState(false);
  62. // 盒子相关
  63. const [boxNum, setBoxNum] = useState<string | undefined>(boxNumber);
  64. const [seatNumbers, setSeatNumbers] = useState<number[] | undefined>();
  65. const [packFlag, setPackFlag] = useState<boolean | undefined>();
  66. // 抽奖结果
  67. const [resultVisible, setResultVisible] = useState(false);
  68. const [resultLoading, setResultLoading] = useState(false);
  69. const [resultList, setResultList] = useState<LotteryItem[]>([]);
  70. // 设置预览数据
  71. const setPreviewData = (previewData: any) => {
  72. setCoin(previewData.magicAmount || null);
  73. setCouponAmount(previewData.couponAmount || null);
  74. setLastPrice(previewData.paymentAmount);
  75. setMagic(previewData.magic || null);
  76. if (previewData.cash && previewData.cash.balance > previewData.paymentAmount) {
  77. setCash(previewData.cash);
  78. setCashChecked(true);
  79. } else {
  80. setCash(previewData.cash || null);
  81. setCashChecked(false);
  82. }
  83. };
  84. useImperativeHandle(ref, () => ({
  85. show: (n: number, previewData: any = {}, bNum?: string, seats?: number[], pack?: boolean) => {
  86. setNum(n);
  87. setBoxNum(bNum);
  88. setSeatNumbers(seats);
  89. setPackFlag(pack || undefined);
  90. setPreviewData(previewData);
  91. setVisible(true);
  92. },
  93. showFreedom: () => {
  94. setFreedomNum(10);
  95. setFreedomSelectVisible(true);
  96. },
  97. close: () => {
  98. setVisible(false);
  99. setFreedomSelectVisible(false);
  100. setResultVisible(false);
  101. },
  102. }));
  103. const handleFreedomSelect = async (selectedNum: number) => {
  104. setFreedomSelectVisible(false);
  105. setLoading(true);
  106. try {
  107. const preview = await previewOrder(poolId, selectedNum);
  108. if (preview) {
  109. setNum(selectedNum);
  110. setFreedomNum(selectedNum);
  111. setPreviewData(preview);
  112. setVisible(true);
  113. }
  114. } catch (error: any) {
  115. Alert.alert('提示', error?.message || '获取订单信息失败');
  116. } finally {
  117. setLoading(false);
  118. }
  119. };
  120. const close = () => {
  121. setVisible(false);
  122. setFreedomSelectVisible(false);
  123. };
  124. const closeResult = () => {
  125. setResultVisible(false);
  126. setResultList([]);
  127. onSuccess({ tradeNo: '', num });
  128. };
  129. // 支付
  130. const pay = async () => {
  131. if (loading) return;
  132. if (!checked) {
  133. Alert.alert('提示', '请同意《宝箱服务协议》');
  134. return;
  135. }
  136. setLoading(true);
  137. try {
  138. const paymentType = 'WALLET';
  139. const payNum = packFlag ? 1 : num;
  140. const res = await applyOrder(poolId, payNum, paymentType, boxNum, seatNumbers, packFlag);
  141. if (res?.paySuccess || res?.bizTradeNo || res?.tradeNo) {
  142. const tradeNo = res.bizTradeNo || res.tradeNo;
  143. setVisible(false);
  144. // 10发以上使用全屏抽奖结果弹窗
  145. if (num >= 10) {
  146. lotteryResultRef.current?.show(tradeNo);
  147. // onSuccess 在 LotteryResultModal 关闭时调用
  148. } else {
  149. // 10发以下使用简单弹窗展示结果
  150. fetchLotteryResult(tradeNo);
  151. }
  152. } else {
  153. Alert.alert('提示', res?.message || '支付失败,请重试');
  154. }
  155. } catch (error: any) {
  156. Alert.alert('支付失败', error?.message || '请稍后重试');
  157. } finally {
  158. setLoading(false);
  159. }
  160. };
  161. // 获取抽奖结果(10发以下用弹窗)
  162. const fetchLotteryResult = async (tradeNo: string) => {
  163. setResultLoading(true);
  164. setResultVisible(true);
  165. setResultList([]);
  166. let attempts = 0;
  167. const maxAttempts = 5;
  168. const poll = async () => {
  169. try {
  170. const res = await getApplyResult(tradeNo);
  171. if (res?.inventoryList && res.inventoryList.length > 0) {
  172. setResultList(res.inventoryList);
  173. setResultLoading(false);
  174. // 不在这里调用 onSuccess,等用户关闭弹窗时再调用
  175. } else if (attempts < maxAttempts) {
  176. attempts++;
  177. setTimeout(poll, 1000);
  178. } else {
  179. setResultLoading(false);
  180. if (typeof window !== 'undefined') {
  181. window.alert('获取结果超时,请在仓库中查看');
  182. }
  183. }
  184. } catch {
  185. if (attempts < maxAttempts) {
  186. attempts++;
  187. setTimeout(poll, 1000);
  188. } else {
  189. setResultLoading(false);
  190. if (typeof window !== 'undefined') {
  191. window.alert('获取结果失败,请在仓库中查看');
  192. }
  193. }
  194. }
  195. };
  196. poll();
  197. };
  198. const displayPrice = lastPrice ?? (data?.price || 0) * num;
  199. return (
  200. <>
  201. {/* 10发以上的全屏抽奖结果弹窗 */}
  202. <LotteryResultModal
  203. ref={lotteryResultRef}
  204. onClose={() => {
  205. // 抽奖结果弹窗关闭后刷新数据
  206. onSuccess({ tradeNo: '', num });
  207. }}
  208. onGoStore={() => {
  209. onSuccess({ tradeNo: '', num });
  210. router.replace('/store' as any);
  211. }}
  212. />
  213. {/* 自由购买数量选择弹窗 */}
  214. <Modal visible={freedomSelectVisible} transparent animationType="fade" onRequestClose={() => setFreedomSelectVisible(false)}>
  215. <View style={styles.overlay}>
  216. <TouchableOpacity style={styles.mask} activeOpacity={1} onPress={() => setFreedomSelectVisible(false)} />
  217. <View style={styles.freedomContainer}>
  218. <View style={styles.header}>
  219. <Text style={styles.title}>购买多盒</Text>
  220. <TouchableOpacity onPress={() => setFreedomSelectVisible(false)} style={styles.closeBtn}>
  221. <Text style={styles.closeText}>×</Text>
  222. </TouchableOpacity>
  223. </View>
  224. <View style={styles.freedomContent}>
  225. <View style={styles.freedomBtnList}>
  226. {FREEDOM_NUMS.map((item) => (
  227. <TouchableOpacity
  228. key={item}
  229. style={[styles.freedomBtn, freedomNum === item && styles.freedomBtnActive]}
  230. onPress={() => setFreedomNum(item)}
  231. >
  232. <Text style={[styles.freedomBtnText, freedomNum === item && styles.freedomBtnTextActive]}>
  233. {item}<Text style={styles.freedomUnit}>盒</Text>
  234. </Text>
  235. {freedomNum === item && <Text style={styles.checkIcon}>✓</Text>}
  236. </TouchableOpacity>
  237. ))}
  238. </View>
  239. <TouchableOpacity
  240. style={[styles.freedomSubmitBtn, loading && styles.payBtnDisabled]}
  241. onPress={() => handleFreedomSelect(freedomNum)}
  242. disabled={loading}
  243. >
  244. {loading ? (
  245. <ActivityIndicator color="#fff" size="small" />
  246. ) : (
  247. <Text style={styles.freedomSubmitText}>确认 ¥{(data?.price || 0) * freedomNum}</Text>
  248. )}
  249. </TouchableOpacity>
  250. </View>
  251. </View>
  252. </View>
  253. </Modal>
  254. {/* 支付确认弹窗 */}
  255. <Modal visible={visible} transparent animationType="slide" onRequestClose={close}>
  256. <View style={styles.overlay}>
  257. <TouchableOpacity style={styles.mask} activeOpacity={1} onPress={close} />
  258. <View style={styles.container}>
  259. <View style={styles.header}>
  260. <Text style={styles.title}>{data?.name}</Text>
  261. <TouchableOpacity onPress={close} style={styles.closeBtn}>
  262. <Text style={styles.closeText}>×</Text>
  263. </TouchableOpacity>
  264. </View>
  265. <View style={styles.content}>
  266. <View style={styles.row}>
  267. <Text style={styles.label}>购买件数</Text>
  268. <Text style={styles.priceText}>¥{data?.price} x {num}</Text>
  269. </View>
  270. <View style={styles.row}>
  271. <Text style={styles.label}>优惠券</Text>
  272. <Text style={[styles.valueText, couponAmount ? styles.themeColor : {}]}>
  273. {couponAmount ? `已使用优惠¥${couponAmount}` : '暂无优惠券可选'}
  274. </Text>
  275. </View>
  276. {magic && magic.balance > 0 && (
  277. <View style={styles.row}>
  278. <View style={styles.rowLeft}>
  279. <Text style={styles.label}>果实</Text>
  280. <Text style={styles.balanceText}>(剩余:{magic.balance})</Text>
  281. </View>
  282. <Text style={styles.balanceText}>
  283. 已抵扣 <Text style={styles.themeColor}>¥{coin || 0}</Text>
  284. </Text>
  285. </View>
  286. )}
  287. {cash && (
  288. <View style={styles.row}>
  289. <View style={styles.rowLeft}>
  290. <Text style={styles.label}>钱包支付</Text>
  291. <Text style={styles.themeColor}>(余额:¥{cash.balance})</Text>
  292. </View>
  293. <TouchableOpacity
  294. style={[styles.radio, cashChecked && styles.radioChecked]}
  295. onPress={() => setCashChecked(!cashChecked)}
  296. >
  297. {cashChecked && <View style={styles.radioInner} />}
  298. </TouchableOpacity>
  299. </View>
  300. )}
  301. <View style={styles.agreementRow}>
  302. <View style={styles.agreementLeft}>
  303. <Text style={styles.agreementText}>
  304. 我已满18周岁,已阅读并同意<Text style={styles.link}>《宝箱服务协议》</Text>
  305. </Text>
  306. <Text style={styles.tips}>宝箱商品存在概率性,请谨慎消费</Text>
  307. </View>
  308. <TouchableOpacity
  309. style={[styles.radio, checked && styles.radioChecked]}
  310. onPress={() => setChecked(!checked)}
  311. >
  312. {checked && <View style={styles.radioInner} />}
  313. </TouchableOpacity>
  314. </View>
  315. </View>
  316. <View style={styles.footer}>
  317. <View style={styles.priceInfo}>
  318. <Text style={styles.totalLabel}>实付:</Text>
  319. <Text style={styles.totalPrice}>¥{displayPrice.toFixed(2)}</Text>
  320. </View>
  321. <TouchableOpacity
  322. style={[styles.payBtn, loading && styles.payBtnDisabled]}
  323. onPress={pay}
  324. disabled={loading}
  325. >
  326. {loading ? <ActivityIndicator color="#fff" size="small" /> : <Text style={styles.payBtnText}>立即支付</Text>}
  327. </TouchableOpacity>
  328. </View>
  329. </View>
  330. </View>
  331. </Modal>
  332. {/* 抽奖结果弹窗 */}
  333. <Modal visible={resultVisible} transparent animationType="fade" onRequestClose={closeResult}>
  334. <View style={styles.resultOverlay}>
  335. <View style={styles.resultContainer}>
  336. <View style={styles.resultHeader}>
  337. <Text style={styles.resultTitle}>🎉 恭喜您获得 🎉</Text>
  338. <TouchableOpacity onPress={closeResult} style={styles.resultCloseBtn}>
  339. <Text style={styles.closeText}>×</Text>
  340. </TouchableOpacity>
  341. </View>
  342. {resultLoading ? (
  343. <View style={styles.resultLoading}>
  344. <ActivityIndicator size="large" color="#ff9600" />
  345. <Text style={styles.resultLoadingText}>正在开启宝箱...</Text>
  346. </View>
  347. ) : (
  348. <ScrollView style={styles.resultScroll} showsVerticalScrollIndicator={false}>
  349. <View style={styles.resultList}>
  350. {resultList.map((item, index) => (
  351. <View key={item.id || index} style={styles.resultItem}>
  352. <View style={[styles.levelBadge, { backgroundColor: LEVEL_MAP[item.level]?.color || '#666' }]}>
  353. <Text style={styles.levelText}>{LEVEL_MAP[item.level]?.title || item.level}</Text>
  354. </View>
  355. <View style={styles.resultImageBox}>
  356. <Image source={{ uri: item.cover }} style={styles.resultImage} contentFit="contain" />
  357. </View>
  358. <Text style={styles.resultName} numberOfLines={2}>{item.name}</Text>
  359. {item.spu?.marketPrice && (
  360. <Text style={styles.resultPrice}>参考价:¥{item.spu.marketPrice}</Text>
  361. )}
  362. </View>
  363. ))}
  364. </View>
  365. </ScrollView>
  366. )}
  367. <View style={styles.resultFooter}>
  368. <TouchableOpacity style={styles.resultBtn} onPress={closeResult}>
  369. <Text style={styles.resultBtnText}>继续抽奖</Text>
  370. </TouchableOpacity>
  371. </View>
  372. </View>
  373. </View>
  374. </Modal>
  375. </>
  376. );
  377. }
  378. );
  379. const styles = StyleSheet.create({
  380. overlay: {
  381. flex: 1,
  382. backgroundColor: 'rgba(0,0,0,0.5)',
  383. justifyContent: 'flex-end',
  384. },
  385. mask: { flex: 1 },
  386. container: {
  387. backgroundColor: '#fff',
  388. borderTopLeftRadius: 20,
  389. borderTopRightRadius: 20,
  390. paddingBottom: 34,
  391. },
  392. header: {
  393. flexDirection: 'row',
  394. alignItems: 'center',
  395. justifyContent: 'center',
  396. padding: 15,
  397. borderBottomWidth: 1,
  398. borderBottomColor: '#eee',
  399. position: 'relative',
  400. },
  401. title: { fontSize: 16, fontWeight: '600', color: '#333' },
  402. closeBtn: { position: 'absolute', right: 15, top: 10 },
  403. closeText: { fontSize: 24, color: '#999' },
  404. content: { padding: 15 },
  405. row: {
  406. flexDirection: 'row',
  407. justifyContent: 'space-between',
  408. alignItems: 'center',
  409. paddingVertical: 10,
  410. },
  411. rowLeft: { flexDirection: 'row', alignItems: 'center', flex: 1 },
  412. label: { fontSize: 14, color: '#333' },
  413. priceText: { fontSize: 14, color: '#ff9600', fontWeight: '600' },
  414. valueText: { fontSize: 12, color: '#999' },
  415. themeColor: { color: '#ff9600' },
  416. balanceText: { fontSize: 12, color: '#999', marginLeft: 5 },
  417. radio: {
  418. width: 20,
  419. height: 20,
  420. borderRadius: 10,
  421. borderWidth: 2,
  422. borderColor: '#ddd',
  423. justifyContent: 'center',
  424. alignItems: 'center',
  425. },
  426. radioChecked: { borderColor: '#ff9600' },
  427. radioInner: { width: 10, height: 10, borderRadius: 5, backgroundColor: '#ff9600' },
  428. agreementRow: {
  429. flexDirection: 'row',
  430. justifyContent: 'space-between',
  431. alignItems: 'flex-start',
  432. paddingVertical: 10,
  433. marginTop: 10,
  434. },
  435. agreementLeft: { flex: 1, marginRight: 10 },
  436. agreementText: { fontSize: 12, color: '#333', lineHeight: 18 },
  437. link: { color: '#ff9600' },
  438. tips: { fontSize: 11, color: '#999', marginTop: 5 },
  439. footer: {
  440. flexDirection: 'row',
  441. alignItems: 'center',
  442. justifyContent: 'space-between',
  443. paddingHorizontal: 15,
  444. paddingTop: 15,
  445. borderTopWidth: 1,
  446. borderTopColor: '#eee',
  447. },
  448. priceInfo: { flexDirection: 'row', alignItems: 'center' },
  449. totalLabel: { fontSize: 14, color: '#333' },
  450. totalPrice: { fontSize: 20, color: '#ff9600', fontWeight: 'bold' },
  451. payBtn: {
  452. backgroundColor: '#ff9600',
  453. paddingHorizontal: 30,
  454. paddingVertical: 12,
  455. borderRadius: 25,
  456. },
  457. payBtnDisabled: { opacity: 0.6 },
  458. payBtnText: { color: '#fff', fontSize: 16, fontWeight: '600' },
  459. // 自由购买弹窗
  460. freedomContainer: {
  461. backgroundColor: '#fff',
  462. borderTopLeftRadius: 20,
  463. borderTopRightRadius: 20,
  464. paddingBottom: 34,
  465. },
  466. freedomContent: { padding: 20 },
  467. freedomBtnList: { flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'space-between' },
  468. freedomBtn: {
  469. width: '48%',
  470. height: 50,
  471. backgroundColor: '#fff',
  472. borderRadius: 8,
  473. justifyContent: 'center',
  474. alignItems: 'center',
  475. marginBottom: 12,
  476. borderWidth: 1,
  477. borderColor: '#eee',
  478. position: 'relative',
  479. },
  480. freedomBtnActive: { backgroundColor: '#F1423D', borderColor: '#F1423D' },
  481. freedomBtnText: { fontSize: 18, fontWeight: 'bold', color: '#333' },
  482. freedomBtnTextActive: { color: '#fff' },
  483. freedomUnit: { fontSize: 12, fontWeight: '500' },
  484. checkIcon: {
  485. position: 'absolute',
  486. bottom: 0,
  487. right: 0,
  488. backgroundColor: '#fff',
  489. color: '#F1423D',
  490. fontSize: 12,
  491. paddingHorizontal: 6,
  492. paddingVertical: 2,
  493. borderTopLeftRadius: 8,
  494. borderBottomRightRadius: 8,
  495. },
  496. freedomSubmitBtn: {
  497. backgroundColor: '#ff9600',
  498. height: 50,
  499. borderRadius: 25,
  500. justifyContent: 'center',
  501. alignItems: 'center',
  502. marginTop: 20,
  503. },
  504. freedomSubmitText: { color: '#fff', fontSize: 16, fontWeight: '600' },
  505. // 抽奖结果弹窗
  506. resultOverlay: {
  507. flex: 1,
  508. backgroundColor: 'rgba(0,0,0,0.7)',
  509. justifyContent: 'center',
  510. alignItems: 'center',
  511. padding: 20,
  512. },
  513. resultContainer: {
  514. width: '100%',
  515. maxHeight: '80%',
  516. backgroundColor: '#fff',
  517. borderRadius: 16,
  518. overflow: 'hidden',
  519. },
  520. resultHeader: {
  521. alignItems: 'center',
  522. padding: 20,
  523. backgroundColor: '#ff9600',
  524. position: 'relative',
  525. },
  526. resultTitle: {
  527. fontSize: 20,
  528. fontWeight: 'bold',
  529. color: '#fff',
  530. },
  531. resultCloseBtn: {
  532. position: 'absolute',
  533. right: 15,
  534. top: 15,
  535. width: 30,
  536. height: 30,
  537. backgroundColor: 'rgba(255,255,255,0.3)',
  538. borderRadius: 15,
  539. justifyContent: 'center',
  540. alignItems: 'center',
  541. },
  542. resultLoading: {
  543. padding: 60,
  544. alignItems: 'center',
  545. },
  546. resultLoadingText: {
  547. marginTop: 15,
  548. fontSize: 14,
  549. color: '#666',
  550. },
  551. resultScroll: {
  552. maxHeight: 400,
  553. },
  554. resultList: {
  555. flexDirection: 'row',
  556. flexWrap: 'wrap',
  557. padding: 10,
  558. justifyContent: 'space-between',
  559. },
  560. resultItem: {
  561. width: (SCREEN_WIDTH - 80) / 2,
  562. backgroundColor: '#f9f9f9',
  563. borderRadius: 10,
  564. padding: 10,
  565. marginBottom: 10,
  566. alignItems: 'center',
  567. },
  568. levelBadge: {
  569. paddingHorizontal: 10,
  570. paddingVertical: 3,
  571. borderRadius: 10,
  572. marginBottom: 8,
  573. },
  574. levelText: {
  575. color: '#fff',
  576. fontSize: 11,
  577. fontWeight: 'bold',
  578. },
  579. resultImageBox: {
  580. width: '100%',
  581. aspectRatio: 1,
  582. backgroundColor: '#fff',
  583. borderRadius: 8,
  584. overflow: 'hidden',
  585. },
  586. resultImage: {
  587. width: '100%',
  588. height: '100%',
  589. },
  590. resultName: {
  591. fontSize: 12,
  592. color: '#333',
  593. textAlign: 'center',
  594. marginTop: 8,
  595. lineHeight: 16,
  596. },
  597. resultPrice: {
  598. fontSize: 10,
  599. color: '#999',
  600. marginTop: 4,
  601. },
  602. resultFooter: {
  603. padding: 15,
  604. borderTopWidth: 1,
  605. borderTopColor: '#eee',
  606. },
  607. resultBtn: {
  608. backgroundColor: '#ff9600',
  609. height: 46,
  610. borderRadius: 23,
  611. justifyContent: 'center',
  612. alignItems: 'center',
  613. },
  614. resultBtnText: {
  615. color: '#fff',
  616. fontSize: 16,
  617. fontWeight: '600',
  618. },
  619. });