uniapp-app/pages_app/myAccount/withdrawal.vue
2025-09-23 19:00:32 +08:00

705 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="withdrawal-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="提现"
@clickLeft="goBack"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
></uni-nav-bar>
<!-- 主内容区域 -->
<view class="main-content">
<!-- 加载状态 -->
<view class="loading-container" v-if="loading">
<text class="loading-text">数据加载中...</text>
</view>
<!-- 结算银行卡区域 -->
<view class="bank-card-section" v-else>
<view class="section-header">
<text class="section-title">结算银行卡</text>
<text class="withdrawal-rules" @click="showWithdrawalRules">提现规则</text>
</view>
<view class="divider"></view>
<view class="bank-card-info" @click="selectBankCard" v-if="selectedBankCard">
<view class="bank-logo">
<text class="bank-icon">{{ selectedBankCard.bankName ? selectedBankCard.bankName.charAt(0) : '工' }}</text>
</view>
<view class="bank-details">
<text class="bank-name">{{ selectedBankCard.bankName || '工商银行' }}</text>
<text class="bank-card-number">尾号为{{ selectedBankCard.cardNumber ? selectedBankCard.cardNumber.slice(-4) : '8937' }}储蓄卡</text>
</view>
<text class="arrow-icon"></text>
</view>
<view class="no-bank-card" v-else>
<text class="no-card-text">请先添加银行卡</text>
<text class="arrow-icon"></text>
</view>
<view class="divider"></view>
</view>
<!-- 提现金额区域 -->
<view class="amount-section">
<text class="amount-title">提现金额</text>
<view class="amount-display">
<text class="currency-symbol">¥</text>
<text class="amount-value">{{ withdrawalAmount }}</text>
</view>
<view class="amount-details">
<text class="tax-info">扣除个人所得税<text class="highlight-red">{{ (incomeTax / 100).toFixed(2) }}</text></text>
<text class="fee-info">扣除提现手续费<text class="highlight-red">{{ (withdrawalBalanceFee / 100).toFixed(2) }}</text></text>
<text class="actual-amount">此次提现实际到账<text class="highlight-red">{{ actualAmount }}</text></text>
</view>
</view>
</view>
<!-- 底部确认按钮 -->
<view class="bottom-button-container">
<button class="confirm-button" @click="confirmWithdrawal" :disabled="loading || !selectedBankCard">
{{ loading ? '处理中...' : '确认转出' }}
</button>
</view>
<!-- 底部手势指示器 -->
<view class="gesture-indicator"></view>
<!-- 短信验证码弹框 -->
<view v-if="showSmsDialog" class="sms-mask">
<view class="sms-dialog">
<view class="sms-title">短信验证码</view>
<view class="sms-subtitle">请输入手机{{ maskedMobile }}收到的验证码</view>
<view class="sms-input-row">
<input class="sms-input" v-model="smsCode" placeholder="请输入验证码" placeholder-style="color: #cccccc" />
<button class="sms-code-btn" :disabled="countdown > 0 || sendingCode" @click="onGetSmsCode">
<text v-if="countdown === 0">获取验证码</text>
<text v-else>{{ countdown }}s</text>
</button>
</view>
<view class="sms-actions">
<button class="sms-cancel" @click="onCancelSms">取消</button>
<button class="sms-confirm" @click="onConfirmSms">确定</button>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, computed } from 'vue'
import api from '@/api/api'
// 响应式数据
const accountBalance = ref(0) // 账户余额
const withdrawalBalanceMaxOnce = ref(0) // 单次提现限额
const withdrawalBalanceFee = ref(0) // 提现手续费
const lessWithdrawalBalance = ref(0) // 最低提现金额
const incomeTax = ref(0) // 个人所得税
const withdrawalAmount = ref('0.00') // 提现金额
const actualAmount = ref('0.00') // 实际到账金额
const bankCardList = ref([]) // 银行卡列表
const selectedBankCard = ref(null) // 选中的银行卡
const loading = ref(false)
// 短信验证码相关
const showSmsDialog = ref(false)
const smsCode = ref('')
const sendingCode = ref(false)
const countdown = ref(0)
let countdownTimer = null
// 计算属性
const maskedMobile = computed(() => {
if (selectedBankCard.value && selectedBankCard.value.mobile) {
const m = selectedBankCard.value.mobile
if (m && m.length >= 7) {
return `${m.slice(0,3)}****${m.slice(-4)}`
}
return m
}
return '***********'
})
onMounted(() => {
// 初始化数据
initData()
})
// 计算提现金额
const calculateAmounts = () => {
// 提现金额 = 账户余额 - 个人所得税 - 提现手续费
const totalDeduction = incomeTax.value + withdrawalBalanceFee.value
const amount = accountBalance.value - totalDeduction
withdrawalAmount.value = accountBalance.value > 0 ? (accountBalance.value/100).toFixed(2) : '0.00'
actualAmount.value = amount > 0 ? (amount/100).toFixed(2) : '0.00'
}
// 初始化数据
const initData = async () => {
loading.value = true
try {
await Promise.all([
getMyAccount(),
getBankCardList()
])
} catch (error) {
console.error('初始化数据失败:', error)
uni.showToast({
title: '数据加载失败',
icon: 'none'
})
} finally {
loading.value = false
}
}
// 方法
const goBack = () => {
uni.navigateBack()
}
// 获取我的账户信息
const getMyAccount = async () => {
try {
const res = await api.getMyAccount({})
console.log('账户信息:', res)
if (res && res.code === 200 && res.data) {
accountBalance.value = res.data.balance || 0
withdrawalBalanceMaxOnce.value = res.data.withdrawalBalanceMaxOnce
withdrawalBalanceFee.value = res.data.withdrawalBalanceFee
lessWithdrawalBalance.value = res.data.lessWithdrawalBalance
// 获取账户信息后计算金额
calculateAmounts()
// 然后获取个人所得税
getIncomeTax(accountBalance.value)
}
} catch (error) {
console.error('获取账户信息失败:', error)
}
}
// 获取个人所得税
const getIncomeTax = async (amount) => {
try {
const res = await api.getIncomeTax({amount: amount})
console.log('个人所得税:', res)
if (res && res.code === 200 && res.data) {
incomeTax.value = res.data.incomeTax || 0
// 获取税费后重新计算金额
calculateAmounts()
}
} catch (error) {
console.error('获取个人所得税失败:', error)
}
}
// 获取银行卡列表
const getBankCardList = async () => {
try {
const res = await api.bankCardList({})
console.log('银行卡列表:', res)
if (res && res.code === 200 && res.data && res.data.length > 0) {
bankCardList.value = res.data
// 默认选择第一张银行卡
selectedBankCard.value = res.data[0]
} else {
uni.showModal({
title: '提示',
content: '请先添加银行卡',
showCancel: false,
confirmText: '去添加',
success: (modalRes) => {
if (modalRes.confirm) {
uni.navigateTo({
url: '/pages_app/idcardAuth/bankCardList'
})
}
}
})
}
} catch (error) {
console.error('获取银行卡列表失败:', error)
}
}
// 选择银行卡
const selectBankCard = () => {
if (bankCardList.value.length <= 1) {
uni.showToast({
title: '只有一张银行卡',
icon: 'none'
})
return
}
// 这里可以实现银行卡选择逻辑
uni.showActionSheet({
itemList: bankCardList.value.map(card => `${card.bankName} 尾号${card.cardNumber.slice(-4)}`),
success: (res) => {
selectedBankCard.value = bankCardList.value[res.tapIndex]
}
})
}
// 显示提现规则
const showWithdrawalRules = () => {
const minAmount = (lessWithdrawalBalance.value / 100).toFixed(2)
const maxAmount = (withdrawalBalanceMaxOnce.value / 100).toFixed(2)
const feeAmount = (withdrawalBalanceFee.value / 100).toFixed(2)
uni.showModal({
title: '提现规则',
content: `1. 提现金额不得少于${minAmount}\n2. 单次提现限额为${maxAmount}\n3. 提现手续费为${feeAmount}\n4. 工作日24小时内到账`,
showCancel: false,
confirmText: '我知道了'
})
}
// 确认提现
const confirmWithdrawal = () => {
if (!selectedBankCard.value) {
uni.showToast({
title: '请先选择银行卡',
icon: 'none'
})
return
}
const minAmount = lessWithdrawalBalance.value / 100
if (parseFloat(withdrawalAmount.value) < minAmount) {
uni.showToast({
title: `提现金额不得少于${minAmount.toFixed(2)}`,
icon: 'none'
})
return
}
// 检查单次提现限额
const maxAmount = withdrawalBalanceMaxOnce.value / 100
if (parseFloat(withdrawalAmount.value) > maxAmount) {
uni.showToast({
title: `单次提现限额为${maxAmount.toFixed(2)}`,
icon: 'none'
})
return
}
// 弹出短信验证码弹框
showSmsDialog.value = true
smsCode.value = '' // 清空验证码
}
// 获取短信验证码
const onGetSmsCode = async () => {
if (countdown.value > 0) return
if (!selectedBankCard.value || !selectedBankCard.value.mobile) {
uni.showToast({
title: '银行卡手机号信息不完整',
icon: 'none'
})
return
}
sendingCode.value = true
try {
const res = await api.smsSend({
mobile: selectedBankCard.value.mobile,
type: 4 // 提现验证码类型
})
if (res.code === 200) {
uni.showToast({
title: '短信验证码发送成功',
icon: 'none'
})
// 开始60秒倒计时
countdown.value = 60
if (countdownTimer) clearInterval(countdownTimer)
countdownTimer = setInterval(() => {
if (countdown.value > 0) {
countdown.value -= 1
} else {
clearInterval(countdownTimer)
countdownTimer = null
}
}, 1000)
} else {
uni.showToast({
title: res.msg || '短信验证码发送失败',
icon: 'none'
})
}
} catch (error) {
console.error('发送短信验证码失败:', error)
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
})
} finally {
sendingCode.value = false
}
}
// 确认短信验证码
const onConfirmSms = async () => {
if (!smsCode.value) {
uni.showToast({
title: '请输入短信验证码',
icon: 'none'
})
return
}
// 关闭弹框
showSmsDialog.value = false
// 调用提现API
api.createWithdrawal({
bankcard_uuid: selectedBankCard.value.uuid,
phone_number: selectedBankCard.value.mobile,
sms: smsCode.value
}).then(res => {
console.log(res)
if(res.code === 200){
uni.showToast({
title: '提现申请已提交',
icon: 'none'
})
uni.navigateBack()
}
else{
uni.showToast({
title: res.msg || '提现申请失败',
icon: 'none'
})
}
})
.catch(err => {
console.log(err)
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
})
})
}
// 取消短信验证码
const onCancelSms = () => {
showSmsDialog.value = false
smsCode.value = ''
}
</script>
<style lang="scss" scoped>
.withdrawal-page {
background-color: #f5f5f5;
min-height: 100vh;
position: relative;
}
// 主内容区域
.main-content {
padding: 0 30rpx;
}
// 加载状态
.loading-container {
display: flex;
justify-content: center;
align-items: center;
padding: 100rpx 0;
.loading-text {
font-size: 28rpx;
color: #999999;
}
}
// 银行卡区域
.bank-card-section {
.withdrawal-rules {
font-size: 28rpx;
color: #333333;
}
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 0;
.section-title {
font-size: 32rpx;
color: #333333;
font-weight: 500;
}
.withdrawal-rules {
font-size: 28rpx;
color: #333333;
}
}
.divider {
height: 1rpx;
background-color: #f0f0f0;
margin: 0;
}
.bank-card-info {
display: flex;
align-items: center;
padding: 30rpx 0;
.bank-logo {
width: 60rpx;
height: 60rpx;
background-color: #8B2316;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 24rpx;
.bank-icon {
font-size: 32rpx;
color: #ffffff;
font-weight: bold;
}
}
.bank-details {
flex: 1;
.bank-name {
display: block;
font-size: 32rpx;
color: #333333;
margin-bottom: 8rpx;
}
.bank-card-number {
display: block;
font-size: 26rpx;
color: #999999;
}
}
.arrow-icon {
font-size: 32rpx;
color: #cccccc;
font-weight: bold;
}
}
// 无银行卡状态
.no-bank-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 0;
.no-card-text {
font-size: 32rpx;
color: #999999;
}
.arrow-icon {
font-size: 32rpx;
color: #cccccc;
font-weight: bold;
}
}
// 金额区域
.amount-section {
padding: 40rpx 0;
.amount-title {
display: block;
font-size: 32rpx;
color: #333333;
font-weight: 500;
margin-bottom: 30rpx;
}
.amount-display {
display: flex;
align-items: baseline;
margin-bottom: 30rpx;
.currency-symbol {
font-size: 48rpx;
color: #333333;
margin-right: 8rpx;
}
.amount-value {
font-size: 60rpx;
color: #333333;
font-weight: 500;
}
}
.amount-details {
.tax-info, .fee-info, .actual-amount {
display: block;
font-size: 26rpx;
color: #999999;
margin-bottom: 12rpx;
.highlight-red {
color: #8B2316;
}
}
}
}
// 底部按钮容器
.bottom-button-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 30rpx;
background-color: #ffffff;
.confirm-button {
width: 100%;
height: 88rpx;
background-color: #8B2316;
border-radius: 8rpx;
border: none;
color: #ffffff;
font-size: 32rpx;
font-weight: 500;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&:active:not(:disabled) {
background-color: #6B1A0F;
}
&:disabled {
background-color: #cccccc;
color: #999999;
}
}
}
// 底部手势指示器
.gesture-indicator {
position: fixed;
bottom: 20rpx;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 6rpx;
background-color: #e0e0e0;
border-radius: 3rpx;
}
/* 短信弹框样式 */
.sms-mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.sms-dialog {
width: 680rpx;
background: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
}
.sms-title {
text-align: center;
font-size: 34rpx;
color: #000000;
font-weight: 600;
margin-bottom: 20rpx;
}
.sms-subtitle {
text-align: center;
font-size: 28rpx;
color: #333333;
margin-bottom: 24rpx;
}
.sms-input-row {
display: flex;
align-items: center;
gap: 16rpx;
margin: 10rpx 0 30rpx;
}
.sms-input {
flex: 1;
height: 88rpx;
border: 2rpx solid #eeeeee;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.sms-code-btn {
width: 200rpx;
height: 88rpx;
border-radius: 8rpx;
background: #eeeeee;
color: #8B2316;
font-size: 26rpx;
border: none;
&:disabled {
background: #f5f5f5;
color: #cccccc;
}
}
.sms-actions {
display: flex;
justify-content: space-between;
margin-top: 10rpx;
}
.sms-cancel,
.sms-confirm {
flex: 1;
height: 88rpx;
border-radius: 8rpx;
font-size: 32rpx;
border: none;
}
.sms-cancel {
background: #f5f5f5;
color: #000000;
margin-right: 20rpx;
}
.sms-confirm {
background: #8B2316;
color: #ffffff;
margin-left: 20rpx;
}
</style>