342 lines
8.3 KiB
Plaintext
342 lines
8.3 KiB
Plaintext
<template>
|
||
<view class="page-container">
|
||
<view class="card">
|
||
<view class="section-title">提现至</view>
|
||
|
||
<view class="bank-selector" @click="openBankSelector">
|
||
<view class="bank-info" v-if="selectedBank != null">
|
||
<text class="bank-name">{{ selectedBank.bank_name }}</text>
|
||
<text class="card-type">储蓄卡</text>
|
||
<text class="card-no">尾号 {{ getTailNumber(selectedBank.card_number) }}</text>
|
||
</view>
|
||
<view class="bank-info placeholder" v-else>
|
||
<text>请选择到账银行卡</text>
|
||
</view>
|
||
<text class="arrow">></text>
|
||
</view>
|
||
|
||
<view class="amount-section">
|
||
<text class="label">提现金额</text>
|
||
<view class="input-wrapper">
|
||
<text class="currency">¥</text>
|
||
<input
|
||
class="amount-input"
|
||
type="digit"
|
||
v-model="amount"
|
||
placeholder="请输入提现金额"
|
||
/>
|
||
</view>
|
||
<view class="balance-line">
|
||
<text class="balance-text">当前可提现余额 ¥{{ balance }}</text>
|
||
<text class="all-btn" @click="setAll">全部提现</text>
|
||
</view>
|
||
</view>
|
||
|
||
<button
|
||
class="submit-btn"
|
||
:disabled="isValid === false"
|
||
:loading="loading"
|
||
@click="submitWithdraw"
|
||
>
|
||
{{ loading ? '处理中...' : '确认提现' }}
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 简单弹窗选择银行卡 -->
|
||
<view v-if="showBankPopup" class="popup-mask" @click="showBankPopup = false">
|
||
<view class="popup-content" @click.stop>
|
||
<view class="popup-header">
|
||
<text class="popup-title">选择到账银行卡</text>
|
||
<text class="close-btn" @click="showBankPopup = false">×</text>
|
||
</view>
|
||
<scroll-view scroll-y="true" class="bank-list">
|
||
<view
|
||
v-for="(item, index) in bankCards"
|
||
:key="index"
|
||
class="bank-item"
|
||
@click="selectBank(item)"
|
||
>
|
||
<view class="bank-row">
|
||
<text class="bank-name-popup">{{ item.bank_name }}</text>
|
||
<text class="card-no-popup">({{ getTailNumber(item.card_number) }})</text>
|
||
</view>
|
||
<text v-if="selectedBank != null && selectedBank.id == item.id" class="check">✓</text>
|
||
</view>
|
||
<view class="add-card-btn" @click="navigateToAddCard">
|
||
<text>+ 添加银行卡</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||
|
||
type BankCard = {
|
||
id: string
|
||
bank_name: string
|
||
card_number: string
|
||
}
|
||
|
||
const amount = ref('')
|
||
const balance = ref(0.00)
|
||
const loading = ref(false)
|
||
const bankCards = ref<BankCard[]>([])
|
||
const selectedBank = ref<BankCard | null>(null)
|
||
const showBankPopup = ref(false)
|
||
|
||
const isValid = computed((): boolean => {
|
||
const val = parseFloat(amount.value)
|
||
// 检查 val 是否有效(替代 isNaN)
|
||
if (val == null || val <= 0) return false
|
||
if (val > balance.value) return false
|
||
if (selectedBank.value == null) return false
|
||
return true
|
||
})
|
||
|
||
const loadData = async (): Promise<void> => {
|
||
try {
|
||
const bal = await supabaseService.getUserBalanceNumber()
|
||
balance.value = bal
|
||
|
||
const res = await supabaseService.getUserBankCards()
|
||
const list: Array<BankCard> = []
|
||
for(let i: number = 0; i < res.length; i++) {
|
||
const item = res[i]
|
||
|
||
let id = ''
|
||
let bankName = ''
|
||
let cardNum = ''
|
||
|
||
if (item instanceof UTSJSONObject) {
|
||
id = item.getString('id') ?? ''
|
||
bankName = item.getString('bank_name') ?? ''
|
||
cardNum = item.getString('card_number') ?? ''
|
||
} else {
|
||
const itemObj = item as UTSJSONObject
|
||
id = itemObj.getString('id') ?? ''
|
||
bankName = itemObj.getString('bank_name') ?? ''
|
||
cardNum = itemObj.getString('card_number') ?? ''
|
||
}
|
||
|
||
if (id != '') {
|
||
const card: BankCard = {
|
||
id: id,
|
||
bank_name: bankName,
|
||
card_number: cardNum
|
||
} as BankCard
|
||
list.push(card)
|
||
}
|
||
}
|
||
|
||
bankCards.value = list
|
||
if (bankCards.value.length > 0) {
|
||
selectedBank.value = bankCards.value[0]
|
||
}
|
||
} catch (e) {
|
||
console.error(e)
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
loadData()
|
||
})
|
||
|
||
const getTailNumber = (cardNo: string | null): string => {
|
||
if (cardNo == null) return ''
|
||
if (cardNo.length <= 4) return cardNo
|
||
return cardNo.substring(cardNo.length - 4)
|
||
}
|
||
|
||
const setAll = () => {
|
||
amount.value = balance.value.toString()
|
||
}
|
||
|
||
const openBankSelector = () => {
|
||
showBankPopup.value = true
|
||
}
|
||
|
||
const selectBank = (bank: BankCard) => {
|
||
selectedBank.value = bank
|
||
showBankPopup.value = false
|
||
}
|
||
|
||
const navigateToAddCard = () => {
|
||
uni.navigateTo({
|
||
url: '/pages/mall/consumer/bank-cards/add'
|
||
})
|
||
showBankPopup.value = false
|
||
}
|
||
|
||
const submitWithdraw = async () => {
|
||
if (isValid.value === false) return
|
||
loading.value = true
|
||
|
||
try {
|
||
const val = parseFloat(amount.value)
|
||
const success = await supabaseService.withdrawBalance(val)
|
||
|
||
if (success) {
|
||
uni.showToast({
|
||
title: '提现申请已提交',
|
||
icon: 'success'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
} else {
|
||
uni.showToast({
|
||
title: '提现失败, ' + (val > balance.value ? '余额不足' : '请重试'),
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (e) {
|
||
uni.showToast({
|
||
title: '系统异常',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.page-container {
|
||
background-color: #f5f5f5;
|
||
flex: 1; /* Fixed 100vh issue */
|
||
padding: 20px;
|
||
}
|
||
.card {
|
||
background-color: #fff;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
}
|
||
.section-title {
|
||
font-size: 16px;
|
||
color: #333;
|
||
margin-bottom: 15px;
|
||
}
|
||
.bank-selector {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px 0;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
.bank-info {
|
||
display: flex;
|
||
align-items: center;
|
||
/* gap removed */
|
||
}
|
||
.bank-name {
|
||
margin-right: 10px;
|
||
font-weight: bold;
|
||
}
|
||
.card-type {
|
||
margin-right: 10px;
|
||
}
|
||
.placeholder {
|
||
color: #999;
|
||
}
|
||
.amount-section {
|
||
margin-top: 20px;
|
||
}
|
||
.label {
|
||
font-size: 14px;
|
||
color: #666;
|
||
margin-bottom: 10px;
|
||
/* display: block removed */
|
||
}
|
||
.input-wrapper {
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 1px solid #eee;
|
||
padding-bottom: 10px;
|
||
margin-bottom: 10px;
|
||
}
|
||
.currency {
|
||
font-size: 30px;
|
||
font-weight: bold;
|
||
margin-right: 10px;
|
||
}
|
||
.amount-input {
|
||
flex: 1;
|
||
font-size: 30px;
|
||
font-weight: bold;
|
||
height: 40px;
|
||
}
|
||
.balance-line {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 12px;
|
||
}
|
||
.balance-text {
|
||
color: #999;
|
||
}
|
||
.all-btn {
|
||
color: #5785e5;
|
||
}
|
||
.submit-btn {
|
||
margin-top: 40px;
|
||
background-color: #5785e5;
|
||
color: #fff;
|
||
border-radius: 25px;
|
||
}
|
||
.submit-btn:disabled {
|
||
background-color: #ccc;
|
||
}
|
||
|
||
.popup-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0,0,0,0.5);
|
||
z-index: 999;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: flex-end;
|
||
}
|
||
.popup-content {
|
||
background-color: #fff;
|
||
width: 100%;
|
||
border-top-left-radius: 16px;
|
||
border-top-right-radius: 16px;
|
||
padding: 20px;
|
||
min-height: 300px;
|
||
}
|
||
.popup-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
.popup-title {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
}
|
||
.close-btn {
|
||
font-size: 20px;
|
||
color: #999;
|
||
padding: 5px;
|
||
}
|
||
.bank-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px 0;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
}
|
||
.add-card-btn {
|
||
padding: 15px 0;
|
||
text-align: center;
|
||
color: #5785e5;
|
||
font-weight: bold;
|
||
}
|
||
</style>
|