Files
shop-toy/src/pagesMerchant/finance/withdraw.vue
2025-12-19 12:04:22 +08:00

377 lines
8.5 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.

<script lang="ts" setup>
import { useMerchantStore } from '@/store/merchant'
import { getWithdrawRecords, applyWithdraw } from '@/pagesMerchant/api'
import { WithdrawStatus, WITHDRAW_STATUS_CONFIG } from '@/typings/merchant'
import type { WithdrawRecord } from '@/typings/merchant'
definePage({
style: {
navigationBarTitleText: '申请提现',
},
})
const merchantStore = useMerchantStore()
// 提现金额
const withdrawAmount = ref('')
// 银行卡列表
const bankCards = [
{ id: '1', name: '中国工商银行', account: '**** **** **** 1234' },
{ id: '2', name: '中国建设银行', account: '**** **** **** 5678' },
]
const selectedCardIndex = ref(0)
// 提现记录
const withdrawRecords = ref<WithdrawRecord[]>([])
// 快捷金额
const quickAmounts = [1000, 5000, 10000]
// 选择快捷金额
function selectQuickAmount(amount: number) {
withdrawAmount.value = amount.toString()
}
// 全部提现
function withdrawAll() {
if (merchantStore.financeOverview) {
withdrawAmount.value = merchantStore.financeOverview.balance.toString()
}
}
// 选择银行卡
function handleCardChange(e: any) {
selectedCardIndex.value = e.detail.value
}
// 申请提现
async function handleWithdraw() {
if (!withdrawAmount.value || parseFloat(withdrawAmount.value) <= 0) {
uni.showToast({ title: '请输入提现金额', icon: 'none' })
return
}
const amount = parseFloat(withdrawAmount.value)
if (merchantStore.financeOverview && amount > merchantStore.financeOverview.balance) {
uni.showToast({ title: '提现金额不能超过可用余额', icon: 'none' })
return
}
uni.showModal({
title: '确认提现',
content: `确定要提现 ¥${amount.toFixed(2)}${bankCards[selectedCardIndex.value].name} 吗?`,
success: async (res) => {
if (res.confirm) {
uni.showLoading({ title: '提交中...' })
try {
await applyWithdraw({
amount,
bankAccount: bankCards[selectedCardIndex.value].account,
})
uni.hideLoading()
uni.showToast({ title: '申请已提交', icon: 'success' })
withdrawAmount.value = ''
loadData()
} catch (error) {
uni.hideLoading()
uni.showToast({ title: '申请失败', icon: 'none' })
}
}
},
})
}
// 加载数据
async function loadData() {
await merchantStore.fetchFinanceOverview()
const res = await getWithdrawRecords()
withdrawRecords.value = res.list
}
onMounted(() => {
loadData()
})
</script>
<template>
<view class="withdraw-page">
<!-- 可用余额 -->
<view class="balance-info" v-if="merchantStore.financeOverview">
<text class="label">可用余额</text>
<text class="amount">{{ merchantStore.financeOverview.balance.toFixed(2) }}</text>
</view>
<!-- 提现表单 -->
<view class="withdraw-form">
<view class="form-item">
<text class="label">提现金额</text>
<view class="amount-input">
<text class="unit">¥</text>
<input
v-model="withdrawAmount"
type="digit"
placeholder="请输入提现金额"
class="input"
/>
<text class="all-btn" @click="withdrawAll">全部提现</text>
</view>
</view>
<view class="quick-amounts">
<view
v-for="amount in quickAmounts"
:key="amount"
class="quick-item"
:class="{ active: withdrawAmount === amount.toString() }"
@click="selectQuickAmount(amount)"
>
¥{{ amount }}
</view>
</view>
<view class="form-item">
<text class="label">到账银行卡</text>
<picker :range="bankCards" range-key="name" @change="handleCardChange">
<view class="bank-card">
<text class="bank-name">{{ bankCards[selectedCardIndex].name }}</text>
<text class="bank-account">{{ bankCards[selectedCardIndex].account }}</text>
<text class="i-carbon-chevron-right"></text>
</view>
</picker>
</view>
<view class="tips">
<text class="i-carbon-information"></text>
<text>提现申请提交后预计1-3个工作日内到账请耐心等待审核</text>
</view>
<view class="submit-btn" @click="handleWithdraw">立即提现</view>
</view>
<!-- 提现记录 -->
<view class="section">
<view class="section-title">提现记录</view>
<view v-if="withdrawRecords.length === 0" class="empty">
<text>暂无提现记录</text>
</view>
<view v-for="item in withdrawRecords" :key="item.id" class="record-item">
<view class="info">
<text class="amount">¥{{ item.amount.toFixed(2) }}</text>
<text class="bank">{{ item.bankName }} {{ item.bankAccount }}</text>
<text class="time">{{ item.applyTime }}</text>
</view>
<view class="status" :style="{ color: WITHDRAW_STATUS_CONFIG[item.status].color }">
{{ WITHDRAW_STATUS_CONFIG[item.status].label }}
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.withdraw-page {
min-height: 100vh;
background: #f5f5f5;
width: 100%;
max-width: 540px;
margin: 0 auto;
}
.balance-info {
background: linear-gradient(135deg, #ff8f0d 0%, #ffb347 100%);
padding: 40rpx;
color: #fff;
.label {
font-size: 26rpx;
opacity: 0.9;
display: block;
margin-bottom: 12rpx;
}
.amount {
font-size: 56rpx;
font-weight: 700;
}
}
.withdraw-form {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 24rpx;
.form-item {
padding: 24rpx 0;
border-bottom: 1rpx solid #f5f5f5;
.label {
font-size: 26rpx;
color: #999;
display: block;
margin-bottom: 16rpx;
}
.amount-input {
display: flex;
align-items: center;
.unit {
font-size: 40rpx;
font-weight: 600;
color: #333;
margin-right: 8rpx;
}
.input {
flex: 1;
font-size: 48rpx;
font-weight: 600;
color: #333;
}
.all-btn {
font-size: 26rpx;
color: #ff8f0d;
}
}
.bank-card {
display: flex;
align-items: center;
gap: 16rpx;
.bank-name {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.bank-account {
flex: 1;
font-size: 26rpx;
color: #666;
}
}
}
.quick-amounts {
display: flex;
gap: 20rpx;
padding: 24rpx 0;
.quick-item {
flex: 1;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx solid #ddd;
border-radius: 8rpx;
font-size: 26rpx;
color: #666;
&.active {
border-color: #ff8f0d;
background: rgba(255, 143, 13, 0.1);
color: #ff8f0d;
}
}
}
.tips {
display: flex;
align-items: flex-start;
gap: 8rpx;
padding: 24rpx 0;
text {
font-size: 24rpx;
color: #999;
line-height: 1.5;
&:first-child {
color: #ff8f0d;
flex-shrink: 0;
}
}
}
.submit-btn {
height: 88rpx;
background: linear-gradient(135deg, #ff8f0d 0%, #ffb347 100%);
border-radius: 44rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
font-weight: 600;
color: #fff;
margin-top: 20rpx;
}
}
.section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 24rpx;
.section-title {
font-size: 30rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
}
.empty {
text-align: center;
padding: 40rpx 0;
color: #999;
font-size: 26rpx;
}
.record-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.info {
.amount {
font-size: 32rpx;
font-weight: 600;
color: #333;
display: block;
margin-bottom: 4rpx;
}
.bank {
font-size: 24rpx;
color: #666;
display: block;
margin-bottom: 4rpx;
}
.time {
font-size: 22rpx;
color: #999;
}
}
.status {
font-size: 26rpx;
font-weight: 500;
}
}
}
</style>