withdrawal.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <script setup lang="ts">
  2. import { ref } from 'vue'
  3. import { getBankcardList, netWithdraw } from '@/api/pay'
  4. import type { bankcardListResponse, netWithdrawResponse } from '@/api/types/pay'
  5. import { getImageUrl } from '@/utils/imageUtil'
  6. import BankCardSelector from './bankCard-select.vue';
  7. // 当前选择的银行卡
  8. const bankCard = ref<bankcardListResponse>({ id: '', bankName: '', bankCardNumber: '', bankImage: '' })
  9. // 当前可提现余额
  10. const availableBalance = ref('0')
  11. // 提现金额输入框的值
  12. const withdrawalAmount = ref('')
  13. // 银行卡数据
  14. const bankCards = ref<bankcardListResponse[]>([])
  15. // 银行卡数量
  16. const bankCardNumber = ref(0)
  17. // 默认选中的卡ID
  18. const selectedCardId = ref('')
  19. // 输入框焦点
  20. const focus = ref(false)
  21. // 域名
  22. const domain = ref('')
  23. // 页面加载时
  24. onLoad((query?: Record<string, string>) => {
  25. if (query?.m?.length) availableBalance.value = query.m
  26. if (query?.bankNumber?.length) bankCardNumber.value = parseInt(query.bankNumber)
  27. fetchBankCards()
  28. // #ifdef H5
  29. domain.value = extractDomain(window.location.href)
  30. // #endif
  31. // #ifndef H5
  32. domain.value = extractDomain(import.meta.env.VITE_SERVER_BASEURL)
  33. // #endif
  34. })
  35. onReady(() => {
  36. focus.value = true
  37. })
  38. // 获取银行卡列表
  39. function fetchBankCards() {
  40. getBankcardList().then((res: any) => {
  41. if (res.data.code === 200) {
  42. bankCards.value = res.data.result.map((item: bankcardListResponse) => ({
  43. ...item,
  44. logo: item.bankImage,
  45. last4: item.bankCardNumber?.slice(-4) || ''
  46. }))
  47. // 默认选中第一张卡
  48. if (res.data.result && res.data.result.length > 0) {
  49. bankCard.value = res.data.result[0]
  50. }
  51. }
  52. }).catch((error: any) => {
  53. console.error('获取银行卡列表失败:', error)
  54. })
  55. }
  56. // 选择/修改银行卡
  57. const bcSelector = ref()
  58. function selectBankCard() {
  59. if (bankCardNumber.value === 0) {
  60. uni.navigateTo({ url: '/pages-B/payment/bankCard-edit' })
  61. } else {
  62. bcSelector.value?.open()
  63. }
  64. }
  65. // 选择事件回调
  66. function handleCardSelect(card: bankcardListResponse) {
  67. console.log('您选择了:', card)
  68. bankCard.value = card
  69. uni.showToast({
  70. title: `已选择: ${card.bankName}`,
  71. icon: 'none'
  72. })
  73. }
  74. // 全部提现操作
  75. function allWithdraw() {
  76. withdrawalAmount.value = availableBalance.value
  77. console.log('已设置全部提现:', withdrawalAmount.value)
  78. }
  79. // 申请提现
  80. async function applyWithdrawal() {
  81. const amount = parseFloat(withdrawalAmount.value)
  82. const balance = parseFloat(availableBalance.value)
  83. if (amount > balance) {
  84. uni.showToast({
  85. title: '提现金额不能超过可提现余额',
  86. icon: 'none'
  87. })
  88. return
  89. }
  90. if (!bankCard.value.id) {
  91. uni.showToast({
  92. title: '请先添加银行卡',
  93. icon: 'none'
  94. })
  95. return
  96. }
  97. const params: netWithdrawResponse = {
  98. applyAmount: withdrawalAmount.value,
  99. bankCardId: bankCard.value.id,
  100. // callBackUrl: `${domain.value}/pages-B/payment/withdrawal?m=${availableBalance.value}`
  101. }
  102. console.log('提交提现申请:', params)
  103. // 提现
  104. uni.showLoading({ title: '提交中' })
  105. netWithdraw(params).then((res: any) => {
  106. if (res.data.code === 200) {
  107. uni.showToast({ title: '提交成功', icon: 'success' });
  108. setTimeout(() => {
  109. uni.hideLoading();
  110. uni.switchTab({
  111. url: '/pages/income/income'
  112. });
  113. }, 1500);
  114. } else {
  115. uni.$u.toast(res.data.message);
  116. }
  117. // else if (res.data.code === 99 && res.data.message) {
  118. // // 白名单接口请求失败-跳转三方链接
  119. // toThirdPartyLink(res.data.message);
  120. // }
  121. }).finally(() => {
  122. uni.hideLoading();
  123. });
  124. }
  125. // 跳转三方链接
  126. function toThirdPartyLink(url: string) {
  127. // #ifdef H5
  128. window.location.href = url
  129. // #endif
  130. // #ifndef H5
  131. uni.navigateTo({
  132. url: `/pages-B/payment/web-view?webUrl=${encodeURIComponent(url)}`,
  133. })
  134. // #endif
  135. }
  136. // 取主域名
  137. function extractDomain(url: string): string {
  138. const regex = /^(https?:\/\/[^\/]+)/
  139. const match = url.match(regex)
  140. return match ? match[1] : ''
  141. }
  142. </script>
  143. <template>
  144. <view class="page-container new-content">
  145. <up-navbar title="提现" :auto-back="true" bgColor="#ffffff" :placeholder="true" :title-style="{ fontWeight: 'bold' }"></up-navbar>
  146. <view class="form-card">
  147. <view class="section-item" @click="selectBankCard">
  148. <view class="label">提现至</view>
  149. <view class="content">
  150. <view class="bank-info">
  151. <image
  152. class="bank-icon"
  153. :src="getImageUrl('@local/mine/icon-bankadd.png')"
  154. mode="heightFix"
  155. />
  156. <text class="bank-name">{{ bankCard.bankName || bankCardNumber === 0 ? '添加银行卡' : '更换银行卡' }} <text v-if="bankCard.bankCardNumber">({{ bankCard.bankCardNumber.slice(-4) }})</text></text>
  157. </view>
  158. <u-icon name="arrow-right" color="#C7C6CA" size="32rpx"></u-icon>
  159. </view>
  160. </view>
  161. <view class="section-input">
  162. <view class="label">提现金额</view>
  163. <view class="input-row">
  164. <text class="currency-symbol">¥</text>
  165. <u--input
  166. type="digit"
  167. border="none"
  168. fontSize="52rpx"
  169. placeholder="请输入提现金额"
  170. :focus="focus"
  171. :customStyle="{
  172. padding: '0 10rpx'
  173. }"
  174. v-model="withdrawalAmount"
  175. ></u--input>
  176. </view>
  177. <view class="balance-info">
  178. <text>当前可提现余额{{ availableBalance }}元,</text>
  179. <text class="all-withdraw-btn" @click="allWithdraw">全部提现</text>
  180. </view>
  181. <template v-if="withdrawalAmount && parseFloat(withdrawalAmount) > 0">
  182. <view class="tips-info">
  183. <text class="l">到账金额</text>
  184. <text class="r">{{ (parseFloat(withdrawalAmount) - 1).toFixed(2) }}</text>
  185. </view>
  186. <view class="tips-info">
  187. <text class="l">手续费</text>
  188. <text class="r">1元</text>
  189. </view>
  190. </template>
  191. </view>
  192. </view>
  193. <view class="footer-bar">
  194. <u-button
  195. :customStyle="{
  196. color: '#fff',
  197. background: 'linear-gradient( 90deg, #FF8F83 0%, #FF3F51 100%)',
  198. borderRadius: '8rpx',
  199. }"
  200. :disabled="!withdrawalAmount || parseFloat(withdrawalAmount) <= 1"
  201. @click="applyWithdrawal"
  202. >申请提现</u-button>
  203. </view>
  204. <!-- 选择器组件 -->
  205. <BankCardSelector
  206. ref="bcSelector"
  207. subtitle="请留意各银行到账时间"
  208. :dataList="bankCards"
  209. v-model="selectedCardId"
  210. @select="handleCardSelect"
  211. />
  212. </view>
  213. </template>
  214. <style lang="scss" scoped>
  215. .page-container {
  216. min-height: 100vh;
  217. background-color: #f8f8fa;
  218. display: flex;
  219. flex-direction: column;
  220. .form-card {
  221. flex: 1;
  222. display: flex;
  223. flex-direction: column;
  224. }
  225. }
  226. // 提现至:银行卡选择区域
  227. .section-item {
  228. padding: 20rpx 32rpx;
  229. // margin-bottom: 20rpx;
  230. .label {
  231. font-weight: 500;
  232. font-size: 32rpx;
  233. color: #333333;
  234. }
  235. .content {
  236. padding-top: 20rpx;
  237. display: flex;
  238. align-items: center;
  239. justify-content: space-between;
  240. }
  241. .bank-info {
  242. display: flex;
  243. align-items: center;
  244. margin-right: 15rpx;
  245. .bank-icon {
  246. width: 48rpx;
  247. height: 48rpx;
  248. margin-right: 10rpx;
  249. display: block;
  250. }
  251. .bank-name {
  252. font-size: 30rpx;
  253. line-height: 30rpx;
  254. }
  255. }
  256. }
  257. // 提现金额输入区域
  258. .section-input {
  259. flex: 1;
  260. padding: 24rpx 32rpx;
  261. background: #FFFFFF;
  262. box-shadow: 0 4rpx 10rpx 0 #DAE3F4;
  263. border-radius: 12rpx 12rpx 0 0;
  264. .label {
  265. font-weight: 400;
  266. font-size: 30rpx;
  267. color: #333333;
  268. margin-bottom: 20rpx;
  269. }
  270. .input-row {
  271. display: flex;
  272. align-items: center;
  273. padding: 20rpx 0;
  274. border-bottom: 1rpx solid #E1E1E1;
  275. .currency-symbol {
  276. font-size: 52rpx;
  277. font-weight: bold;
  278. color: #333;
  279. padding-right: 10rpx;
  280. }
  281. ::v-deep .u-input__content__field-wrapper__field {
  282. height: 60rpx;
  283. }
  284. // .withdrawal-input {
  285. // font-size: 52rpx;
  286. // padding: 0 10rpx;
  287. // flex: 1;
  288. // }
  289. }
  290. .balance-info {
  291. font-weight: 400;
  292. font-size: 28rpx;
  293. color: rgba(51,51,51,0.6);
  294. margin: 20rpx 0;
  295. .all-withdraw-btn {
  296. color: #0879FF;
  297. margin-left: 10rpx;
  298. &:active {
  299. opacity: 0.8;
  300. }
  301. }
  302. }
  303. .tips-info {
  304. margin-bottom: 20rpx;
  305. display: flex;
  306. align-items: center;
  307. justify-content: space-between;
  308. .l {
  309. font-weight: 400;
  310. font-size: 28rpx;
  311. color: #666666;
  312. }
  313. .r {
  314. font-weight: 500;
  315. font-size: 28rpx;
  316. color: #262F48;
  317. }
  318. }
  319. }
  320. // 底部按钮
  321. .footer-bar {
  322. ::v-deep .u-button {
  323. width: 100%;
  324. height: 76rpx;
  325. }
  326. }
  327. </style>