withdraw.tsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { Ionicons } from '@expo/vector-icons';
  2. import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
  3. import React, { useEffect, useState } from 'react';
  4. import { ActivityIndicator, Alert, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
  5. import services from '../../services/api';
  6. export default function WithdrawScreen() {
  7. const router = useRouter();
  8. const params = useLocalSearchParams();
  9. const [balance, setBalance] = useState('0.00');
  10. const [money, setMoney] = useState('');
  11. const [bankInfo, setBankInfo] = useState<any>(null);
  12. const [rate, setRate] = useState(0);
  13. const [agree, setAgree] = useState(false);
  14. const [loading, setLoading] = useState(false);
  15. // Uniapp had type='ALIPAY' default
  16. const type = (params.type as string) || 'ALIPAY';
  17. useEffect(() => {
  18. fetchData();
  19. fetchRate();
  20. fetchWithdrawInfo();
  21. }, []);
  22. const fetchData = async () => {
  23. try {
  24. const res = await services.wallet.withdrawPre(type, 'MAGIC_PROMOTION');
  25. if (res && res.avaliableWithdraw) {
  26. setBalance(res.avaliableWithdraw.amount);
  27. }
  28. } catch (error) {
  29. console.error('Fetch withdraw pre failed', error);
  30. }
  31. };
  32. const fetchRate = async () => {
  33. try {
  34. await services.user.getParamConfig('wallet_withdraw_rate').then((res: any) => {
  35. setRate(res.data ? Number(res.data) : 0);
  36. });
  37. } catch (error) {
  38. console.error('Fetch rate failed', error);
  39. }
  40. };
  41. const fetchWithdrawInfo = async () => {
  42. try {
  43. const res = await services.wallet.getWithdraw();
  44. setBankInfo(res);
  45. } catch (error) {
  46. console.error('Fetch bank info failed', error);
  47. }
  48. };
  49. const handleWithdraw = async () => {
  50. if (!agree) {
  51. Alert.alert('提示', '请您先阅读并同意提现协议');
  52. return;
  53. }
  54. if (!bankInfo || !bankInfo.bankNum) {
  55. Alert.alert('提示', '请新增银行卡信息');
  56. return;
  57. }
  58. const amount = parseFloat(money);
  59. if (!money || isNaN(amount) || amount <= 0 || amount > parseFloat(balance)) {
  60. Alert.alert('提示', '请输入正确的金额');
  61. return;
  62. }
  63. setLoading(true);
  64. try {
  65. const success = await services.wallet.withdraw({
  66. money,
  67. ...bankInfo,
  68. walletType: 'MAGIC_PROMOTION',
  69. accountType: '3'
  70. });
  71. if (success) {
  72. Alert.alert('成功', '提现申请已提交', [
  73. { text: '确定', onPress: () => router.push('/wallet/withdraw_record') }
  74. ]);
  75. setMoney('');
  76. }
  77. } catch (error) {
  78. console.error('Withdraw failed', error);
  79. } finally {
  80. setLoading(false);
  81. }
  82. };
  83. const realMoney = money ? (parseFloat(money) * (1 - rate)).toFixed(2) : '0.00';
  84. return (
  85. <ScrollView style={styles.container}>
  86. <Stack.Screen options={{ title: '提现', headerStyle: { backgroundColor: '#fff' }, headerShadowVisible: false }} />
  87. <View style={styles.groupContainer}>
  88. {/* Bank Card Section */}
  89. <TouchableOpacity style={styles.formGroup} onPress={() => router.push('/cardInfo')}>
  90. <Text style={styles.label}>提现至</Text>
  91. <View style={styles.rightContent}>
  92. <Text style={styles.valueText}>
  93. {bankInfo && bankInfo.bankName
  94. ? `${bankInfo.bankName}尾号(${bankInfo.bankNum.slice(-4)})`
  95. : '暂无银行卡信息'}
  96. </Text>
  97. <Ionicons name="chevron-forward" size={16} color="#8799a3" />
  98. </View>
  99. </TouchableOpacity>
  100. </View>
  101. <View style={styles.groupContainer}>
  102. <View style={styles.formGroup}>
  103. <Text style={styles.label}>可提现金额:{balance}元</Text>
  104. <TouchableOpacity style={styles.ruleBtn}>
  105. <Ionicons name="help-circle" size={16} color="#5b5b5b" />
  106. <Text style={styles.ruleText}>提现规则</Text>
  107. </TouchableOpacity>
  108. </View>
  109. <View style={styles.formGroupInput}>
  110. <Text style={styles.currencySymbol}>¥</Text>
  111. <TextInput
  112. style={styles.input}
  113. value={money}
  114. onChangeText={setMoney}
  115. keyboardType="decimal-pad"
  116. placeholder="请输入提现金额"
  117. />
  118. <TouchableOpacity onPress={() => setMoney(balance)}>
  119. <Text style={styles.withdrawAll}>全部提现</Text>
  120. </TouchableOpacity>
  121. </View>
  122. <View style={[styles.formGroup, { backgroundColor: '#f8f8f8' }]}>
  123. <Text style={styles.label}>税率:</Text>
  124. <Text style={styles.valueText}>{(rate * 100).toFixed(0)}%</Text>
  125. </View>
  126. <View style={[styles.formGroup, { backgroundColor: '#f8f8f8' }]}>
  127. <Text style={styles.label}>实际到账金额:</Text>
  128. <Text style={styles.valueText}>{realMoney}</Text>
  129. </View>
  130. </View>
  131. <TouchableOpacity style={styles.recordLink} onPress={() => router.push('/wallet/withdraw_record')}>
  132. <Text style={styles.recordLinkText}>提现记录</Text>
  133. </TouchableOpacity>
  134. <TouchableOpacity style={styles.agreement} onPress={() => setAgree(!agree)}>
  135. <Ionicons
  136. name={agree ? "radio-button-on" : "radio-button-off"}
  137. size={20}
  138. color={agree ? "#8b3dff" : "#8799a3"}
  139. />
  140. <Text style={styles.agreementText}>
  141. 我已阅读并同意 <Text style={styles.linkText}>《提现协议》</Text>
  142. </Text>
  143. </TouchableOpacity>
  144. <View style={styles.footer}>
  145. <TouchableOpacity
  146. style={[styles.confirmBtn, loading && styles.disabledBtn]}
  147. onPress={handleWithdraw}
  148. disabled={loading}
  149. >
  150. {loading ? <ActivityIndicator color="#fff" /> : <Text style={styles.confirmBtnText}>确认提现</Text>}
  151. </TouchableOpacity>
  152. </View>
  153. </ScrollView>
  154. );
  155. }
  156. const styles = StyleSheet.create({
  157. container: {
  158. flex: 1,
  159. backgroundColor: '#fff',
  160. },
  161. groupContainer: {
  162. paddingHorizontal: 12,
  163. paddingTop: 18,
  164. },
  165. formGroup: {
  166. flexDirection: 'row',
  167. justifyContent: 'space-between',
  168. alignItems: 'center',
  169. paddingVertical: 15,
  170. backgroundColor: '#fff',
  171. // borderBottomWidth: 1,
  172. // borderBottomColor: '#eee',
  173. },
  174. formGroupInput: {
  175. flexDirection: 'row',
  176. alignItems: 'center',
  177. paddingVertical: 15,
  178. borderBottomWidth: 1,
  179. borderBottomColor: '#eee',
  180. },
  181. label: {
  182. fontSize: 15,
  183. color: '#333',
  184. },
  185. rightContent: {
  186. flexDirection: 'row',
  187. alignItems: 'center',
  188. },
  189. valueText: {
  190. fontSize: 15,
  191. color: '#666',
  192. marginRight: 5,
  193. },
  194. ruleBtn: {
  195. flexDirection: 'row',
  196. alignItems: 'center',
  197. },
  198. ruleText: {
  199. fontSize: 14,
  200. color: '#5b5b5b',
  201. marginLeft: 4,
  202. },
  203. currencySymbol: {
  204. fontSize: 24,
  205. fontWeight: 'bold',
  206. color: '#333',
  207. width: 30,
  208. },
  209. input: {
  210. flex: 1,
  211. fontSize: 20,
  212. paddingHorizontal: 10,
  213. },
  214. withdrawAll: {
  215. color: '#ff9600',
  216. fontSize: 14,
  217. },
  218. recordLink: {
  219. alignItems: 'center',
  220. marginTop: 18,
  221. },
  222. recordLinkText: {
  223. fontSize: 12,
  224. color: '#999',
  225. },
  226. agreement: {
  227. flexDirection: 'row',
  228. justifyContent: 'center',
  229. alignItems: 'center',
  230. marginTop: 20,
  231. },
  232. agreementText: {
  233. fontSize: 12,
  234. color: '#333',
  235. marginLeft: 4,
  236. },
  237. linkText: {
  238. color: '#8b3dff',
  239. },
  240. footer: {
  241. paddingHorizontal: 26,
  242. paddingTop: 6,
  243. marginTop: 20,
  244. backgroundColor: '#f6f6f6', // Optional visual separation if needed, or transparent
  245. paddingBottom: 40,
  246. },
  247. confirmBtn: {
  248. backgroundColor: '#8b3dff', // Theme color
  249. height: 50,
  250. borderRadius: 25,
  251. justifyContent: 'center',
  252. alignItems: 'center',
  253. },
  254. disabledBtn: {
  255. opacity: 0.7,
  256. },
  257. confirmBtnText: {
  258. color: '#fff',
  259. fontSize: 16,
  260. fontWeight: 'bold',
  261. },
  262. });