consumer模块完成95%,在和商家端对接聊天购物闭环
This commit is contained in:
334
pages/mall/consumer/withdraw.uvue
Normal file
334
pages/mall/consumer/withdraw.uvue
Normal file
@@ -0,0 +1,334 @@
|
||||
<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">
|
||||
<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"
|
||||
: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?.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)
|
||||
if (isNaN(val) || val <= 0) return false
|
||||
if (val > balance.value) return false
|
||||
if (selectedBank.value == null) return false
|
||||
return true
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
|
||||
const loadData = async () => {
|
||||
try {
|
||||
const bal = await supabaseService.getUserBalance()
|
||||
balance.value = bal
|
||||
|
||||
// 获取银行卡
|
||||
const res = await supabaseService.getUserBankCards()
|
||||
// 转换类型
|
||||
const list: BankCard[] = []
|
||||
for(let i=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 m = item as Map<string, any>
|
||||
id = (m.get('id') as string) || ''
|
||||
bankName = (m.get('bank_name') as string) || ''
|
||||
cardNum = (m.get('card_number') as string) || ''
|
||||
}
|
||||
|
||||
if (id != '') {
|
||||
list.push({
|
||||
id: id,
|
||||
bank_name: bankName,
|
||||
card_number: cardNum
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
bankCards.value = list
|
||||
if (bankCards.value.length > 0) {
|
||||
selectedBank.value = bankCards.value[0]
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
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) 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;
|
||||
min-height: 100vh;
|
||||
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: 10px;
|
||||
}
|
||||
.placeholder {
|
||||
color: #999;
|
||||
}
|
||||
.amount-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
.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: 500;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user