Files
qnc-webview-v3/src/views/Withdraw.vue
2026-02-06 15:23:41 +08:00

637 lines
27 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>
<div class="p-4 bg-gradient-to-b from-blue-50/30 to-gray-50 min-h-screen">
<!-- 实名认证提示 -->
<div v-if="!agentStore.isRealName" class="mb-4">
<div class="rounded-xl shadow-lg p-6" style="background-color: var(--van-theme-primary-light);">
<div class="flex items-center mb-3">
<van-icon name="warning-o" class="text-xl mr-2" style="color: var(--van-theme-primary);" />
<h2 class="text-lg font-bold" style="color: var(--van-text-color);">
未完成实名认证
</h2>
</div>
<p class="text-sm mb-4" style="color: var(--van-text-color-2);">
根据相关规定提现功能需要完成实名认证后才能使用提现金额将转入您实名认证的账户中
</p>
<van-button type="primary" block class="text-white rounded-xl h-10"
style="background-color: var(--van-theme-primary);"
@click="openRealNameAuth">
立即实名认证
</van-button>
</div>
</div>
<div>
<!-- 提现卡片 -->
<div class="rounded-xl shadow-lg p-6 mb-4" style="background: linear-gradient(135deg, var(--van-theme-primary-light), rgba(255,255,255,0.8));">
<!-- 提现方式切换 -->
<van-tabs v-model:active="withdrawalType" shrink class="mb-4" title-active-color="var(--van-theme-primary)" @change="onWithdrawalTypeChange">
<van-tab title="支付宝" :name="1">
<div class="pt-2">
<div class="flex items-center mb-4">
<van-icon name="alipay" class="text-xl mr-2" style="color: #1677FF;" />
<span class="text-base font-medium" style="color: var(--van-text-color);">支付宝提现</span>
</div>
<!-- 支付宝账号 -->
<div class="mb-6">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">支付宝账号</label>
<van-field v-model="alipayAccount" placeholder="请输入支付宝账号"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm"
:rules="[{ required: true, message: ' ' }]">
<template #left-icon>
<van-icon name="phone-o" style="color: var(--van-text-color-2);" />
</template>
</van-field>
<small class="text-xs mt-1 block" style="color: var(--van-text-color-2);">可填写支付宝账户绑定的手机号</small>
</div>
<!-- 支付宝实名姓名 -->
<div class="mb-6">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">实名姓名</label>
<van-field v-model="realName" placeholder="请输入支付宝认证姓名"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm" :rules="[
{
required: true,
message: ' ',
validator: (val) =>
/^[\u4e00-\u9fa5]{2,4}$/.test(val),
},
]">
<template #left-icon>
<van-icon name="contact-o" style="color: var(--van-text-color-2);" />
</template>
</van-field>
<small class="text-xs mt-1 block" style="color: var(--van-text-color-2);">请填写支付宝账户认证的真实姓名</small>
</div>
</div>
</van-tab>
<van-tab title="银行卡" :name="2">
<div class="pt-2">
<div class="flex items-center mb-4">
<van-icon name="balance-list-o" class="text-xl mr-2" style="color: var(--van-theme-primary);" />
<span class="text-base font-medium" style="color: var(--van-text-color);">银行卡提现</span>
</div>
<!-- 银行卡号 -->
<div class="mb-6">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">银行卡号</label>
<van-field v-model="bankCardNo" placeholder="请输入银行卡号" type="number"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm"
:rules="[{ required: true, message: ' ' }]">
<template #left-icon>
<van-icon name="balance-list-o" style="color: var(--van-text-color-2);" />
</template>
</van-field>
<small class="text-xs mt-1 block" style="color: var(--van-text-color-2);">请填写与实名一致的开户银行卡号</small>
</div>
<!-- 开户行名称 -->
<div class="mb-6">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">开户行名称</label>
<van-field v-model="bankName" placeholder="请输入开户行名称中国工商银行XX支行"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm"
:rules="[{ required: true, message: ' ' }]">
<template #left-icon>
<van-icon name="shop-o" style="color: var(--van-text-color-2);" />
</template>
</van-field>
</div>
<!-- 收款人姓名 -->
<div class="mb-6">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">收款人姓名</label>
<van-field v-model="bankPayeeName" placeholder="请输入银行卡户主姓名"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm" :rules="[
{
required: true,
message: ' ',
validator: (val) =>
/^[\u4e00-\u9fa5]{2,4}$/.test(val),
},
]">
<template #left-icon>
<van-icon name="contact-o" style="color: var(--van-text-color-2);" />
</template>
</van-field>
<small class="text-xs mt-1 block" style="color: var(--van-text-color-2);">需与银行卡开户姓名一致</small>
</div>
</div>
</van-tab>
</van-tabs>
<!-- 提现金额 -->
<div class="mb-4">
<label class="text-sm mb-2 block" style="color: var(--van-text-color);">提现金额</label>
<van-field v-model.number="amount" type="number" placeholder="请输入提现金额"
class="flex items-center rounded-lg bg-white/90 backdrop-blur-sm shadow-sm" :rules="[
{ required: true, message: ' ' },
{ validator: validateAmount, message: ' ' },
]">
<template #left-icon>
<van-icon name="gold-coin-o" style="color: var(--van-text-color-2);" />
</template>
<template #right-icon> </template>
<template #button>
<van-button size="small" type="primary"
class="rounded-full px-3 shadow-sm"
style="background-color: var(--van-theme-primary); color: white;"
@click="fillMaxAmount">
全部提现
</van-button>
</template>
</van-field>
</div>
<!-- 金额提示 -->
<div class="text-sm mb-2" style="color: var(--van-text-color);">
可提现金额<span class="font-semibold" style="color: var(--van-theme-primary);">¥{{ availableAmount }}</span>
</div>
<!-- 提现规则 -->
<div class="p-4 rounded-xl backdrop-blur-sm" style="background-color: var(--van-theme-primary-light);">
<div class="flex items-center text-sm mb-2" style="color: var(--van-theme-primary);">
<van-icon name="warning" class="mr-1" />提现须知
</div>
<!-- 支付宝提现说明 -->
<ul v-if="withdrawalType === 1" class="text-xs space-y-1" style="color: var(--van-text-color);">
<li>· 每日限提现1次最低50元</li>
<li>· 提现收取6%税收</li>
<li>· 到账时间24小时内</li>
</ul>
<!-- 银行卡提现说明 -->
<ul v-else class="text-xs space-y-1" style="color: var(--van-text-color);">
<li>· 每日限提现1次最低50元</li>
<li>· 提现收取6%税收</li>
<li>· 到账时间1-3个工作日需人工审核</li>
<li>· 银行卡提现请确保开户行户名与实名一致到账前会有审核</li>
</ul>
</div>
<!-- 税收说明 -->
<div class="p-4 rounded-xl backdrop-blur-sm mt-4" style="background-color: rgba(251, 191, 36, 0.1);">
<div class="flex items-center text-sm mb-2" style="color: #f59e0b;">
<van-icon name="info-o" class="mr-1" />税收说明
</div>
<div class="text-xs leading-relaxed" style="color: var(--van-text-color);">
<p>根据相关规定提现时将统一收取6%的税收该税收用于相关税费支出</p>
<p class="mt-2" style="color: #f59e0b;"> 税率标准统一按6%收取</p>
<p style="color: #f59e0b;"> 适用范围所有提现金额</p>
</div>
</div>
</div>
<!-- 提交按钮 -->
<van-button type="primary" block :loading="isSubmitting"
class="text-white rounded-xl shadow-lg h-12 font-bold text-base"
style="background: linear-gradient(135deg, var(--van-theme-primary), var(--van-theme-primary-dark));"
@click="handleSubmit">
立即提现
</van-button>
</div>
<!-- 税务确认弹窗 -->
<van-popup v-model:show="showTaxConfirmPopup" round position="center"
:style="{ width: '85%', borderRadius: '20px' }" :overlay-style="{ backgroundColor: 'rgba(0,0,0,0.4)' }">
<div class="p-8 bg-gradient-to-b from-white to-blue-50/30 relative">
<div class="text-center space-y-5">
<!-- 标题 -->
<div>
<h2 class="text-xl font-semibold text-gray-800 mb-2">提现确认</h2>
<p class="text-sm text-gray-500">请确认以下提现信息</p>
</div>
<!-- 金额详情 -->
<div class="rounded-xl p-4 space-y-3" style="background-color: var(--van-background-color-light);">
<div class="flex justify-between items-center">
<span style="color: var(--van-text-color-2);">提现金额</span>
<span class="font-semibold" style="color: var(--van-text-color);">¥{{ amount }}</span>
</div>
<div class="flex justify-between items-center">
<span style="color: var(--van-text-color-2);">预估税费</span>
<span class="font-semibold text-red-500">
-¥{{ estimatedTaxAmount.toFixed(2) }}
</span>
</div>
<div class="border-t pt-2" style="border-color: var(--van-border-color);">
<div class="flex justify-between items-center">
<span class="font-medium" style="color: var(--van-text-color);">预估到账</span>
<span class="font-bold text-lg" style="color: var(--van-theme-primary);">¥{{ estimatedActualAmount.toFixed(2) }}</span>
</div>
</div>
</div>
<!-- 提示信息 -->
<div class="bg-blue-50 rounded-lg p-3 text-xs">
<p class="text-blue-600 font-medium mb-2">
税收说明
</p>
<div class="text-gray-600 space-y-1">
<p> 提现金额¥{{ amount }}</p>
<p> 税率6%</p>
<p class="text-blue-600"> 税收计算¥{{ amount }} × 6% = ¥{{ taxAmount.toFixed(2) }}</p>
</div>
</div>
<!-- 操作按钮 -->
<div class="flex space-x-3 mt-6">
<van-button block round class="flex-1 h-11"
style="background-color: var(--van-background-color-light); color: var(--van-text-color-2);"
@click="showTaxConfirmPopup = false">
取消
</van-button>
<van-button block round class="flex-1 h-11 font-medium shadow-sm"
style="background-color: var(--van-theme-primary); color: white;"
:loading="isSubmitting" @click="confirmWithdraw">
确认提现
</van-button>
</div>
</div>
</div>
</van-popup>
<!-- 提现结果弹窗 -->
<van-popup v-model:show="showStatusPopup" round position="center"
:style="{ width: '85%', borderRadius: '20px' }" :overlay-style="{ backgroundColor: 'rgba(0,0,0,0.4)' }">
<div class="p-8 bg-gradient-to-b from-white to-blue-50/30 relative">
<!-- 状态内容 -->
<div class="text-center space-y-5">
<!-- 状态图标 -->
<div class="relative inline-block">
<div class="absolute inset-0 bg-gradient-to-r opacity-20 rounded-full animate-pulse blur-sm"
:class="statusBg[status]"></div>
<van-icon :name="statusIcon[status]" size="56" class="p-1 rounded-full border-[3px]"
:class="statusIconClass[status]" />
</div>
<!-- 状态文案 -->
<div>
<h2 class="text-xl font-semibold mb-1" :class="statusTextColors[status]">
{{ statusMessages[status] }}
</h2>
<template v-if="status === 2">
<p class="text-sm" style="color: var(--van-text-color-2);">
<template v-if="withdrawalType === 1">
已向
<span style="color: var(--van-theme-primary);">{{ alipayAccount }}</span>
转账
</template>
<template v-else>
已向
<span style="color: var(--van-theme-primary);">尾号{{ displayBankCardTail }}</span>
银行卡转账
</template>
</p>
<p class="text-2xl font-bold mt-2" style="color: #10b981;">
¥{{ amount }}
</p>
</template>
<template v-if="status === 3">
<p class="text-sm px-4" style="color: #ef4444;">
{{ failMsg }}
</p>
</template>
</div>
<!-- 进度条处理中状态 -->
<van-progress v-if="status === 1" :percentage="60" stroke-width="8"
:color="`linear-gradient(to right, var(--van-theme-primary), var(--van-theme-primary-light))`"
:track-color="`var(--van-theme-primary-light)`"
class="!rounded-full" />
<!-- 辅助文案 -->
<div class="text-xs space-y-1.5" style="color: var(--van-text-color-2);">
<template v-if="status === 2">
<p>预计1-3个工作日到账需人工审核</p>
<p v-if="withdrawalType === 1">可在支付宝账单中查看详情</p>
<p v-else>请留意银行卡到账通知</p>
</template>
<template v-if="status === 1">
<p>您的申请已进入处理队列</p>
<p>5分钟后结果在提现记录种查看</p>
</template>
</div>
<!-- 操作按钮 -->
<van-button block round size="small"
class="mt-4 h-11 font-medium shadow-sm"
:style="{ backgroundColor: statusButtonColor[status], color: status === 1 ? 'var(--van-theme-primary)' : 'white' }"
@click="handlePopupAction">
{{
status === 1
? "知道了"
: status === 2
? "完成"
: "重新提现"
}}
</van-button>
</div>
</div>
</van-popup>
</div>
<!-- 引入实名认证对话框 -->
<RealNameAuthDialog />
</template>
<script setup>
import { ref, computed } from "vue";
import { showToast } from "vant";
import RealNameAuthDialog from "@/components/RealNameAuthDialog.vue";
import { applyWithdrawal, getRevenueInfo } from "@/api/agent";
const agentStore = useAgentStore();
const dialogStore = useDialogStore();
// 状态管理
const status = ref(null);
const failMsg = ref("");
const isSubmitting = ref(false);
const showStatusPopup = ref(false);
const showTaxConfirmPopup = ref(false);
// 税费信息(新系统由后端自动计算)
const taxAmount = ref(0);
const actualAmount = ref(0);
// 计算预估税费和实际到账金额(前端仅作展示,实际由后端计算)
const estimatedTaxAmount = computed(() => {
if (!amount.value) return 0;
const withdrawAmount = Number(amount.value);
// 预估税费6%(实际以后端计算为准)
return withdrawAmount * 0.06;
});
const estimatedActualAmount = computed(() => {
if (!amount.value) return 0;
return Number(amount.value) - estimatedTaxAmount.value;
});
// 银行卡号尾号(成功弹窗展示用)
const displayBankCardTail = computed(() => {
const no = (bankCardNo.value || "").replace(/\s/g, "");
return no.length >= 4 ? no.slice(-4) : no || "****";
});
// 样式配置
const statusIcon = {
1: "clock",
2: "checked",
3: "close",
};
const statusIconClass = {
1: "text-blue-400 border-blue-100 bg-blue-50",
2: "text-green-500 border-green-100 bg-green-50",
3: "text-red-500 border-red-100 bg-red-50",
};
const statusBg = {
1: "from-blue-100 to-blue-50",
2: "from-green-100 to-green-50",
3: "from-red-100 to-red-50",
};
const statusTextColors = {
1: "text-blue-600",
2: "text-green-600",
3: "text-red-600",
};
const statusButtonColor = {
1: "var(--van-theme-primary-light)",
2: "#10b981",
3: "#ef4444",
};
const statusMessages = {
1: "提现申请处理中,请稍后再查询结果",
2: "提现成功",
3: "提现失败",
};
// 提现方式1=支付宝2=银行卡
const withdrawalType = ref(1);
// 表单数据 - 支付宝
const alipayAccount = ref("");
const amount = ref(0);
const availableAmount = ref(null);
const realName = ref("");
// 表单数据 - 银行卡
const bankCardNo = ref("");
const bankName = ref("");
const bankPayeeName = ref("");
// 切换提现方式时清空另一侧部分字段(金额保留)
const onWithdrawalTypeChange = () => {
if (withdrawalType.value === 1) {
bankCardNo.value = "";
bankName.value = "";
bankPayeeName.value = "";
} else {
alipayAccount.value = "";
realName.value = "";
}
};
const getData = async () => {
const { data, error } = await getRevenueInfo();
if (data.value?.code === 200 && !error.value) {
availableAmount.value = data.value.data.balance;
}
};
onBeforeMount(() => {
getData();
});
// 表单验证
const validateAmount = (val) => {
const num = Number(val);
return num >= 50 && num <= availableAmount.value;
};
const validateForm = () => {
const amountNum = Number(amount.value);
if (!amount.value || isNaN(amountNum)) {
showToast("请输入有效金额");
return false;
}
if (amountNum < 50) {
showToast("提现金额不能低于50元");
return false;
}
if (amountNum > availableAmount.value) {
showToast("超过可提现金额");
return false;
}
if (withdrawalType.value === 1) {
// 支付宝
if (!realName.value.trim()) {
showToast("请输入账户实名姓名");
return false;
}
if (!/^[\u4e00-\u9fa5]{2,4}$/.test(realName.value)) {
showToast("请输入2-4位中文姓名");
return false;
}
if (!alipayAccount.value.trim()) {
showToast("请输入支付宝账号");
return false;
}
} else {
// 银行卡
if (!bankCardNo.value.trim()) {
showToast("请输入银行卡号");
return false;
}
if (!/^\d{16,19}$/.test(bankCardNo.value.replace(/\s/g, ""))) {
showToast("请输入正确的银行卡号");
return false;
}
if (!bankName.value.trim()) {
showToast("请输入开户行名称");
return false;
}
if (!bankPayeeName.value.trim()) {
showToast("请输入收款人姓名");
return false;
}
if (!/^[\u4e00-\u9fa5]{2,4}$/.test(bankPayeeName.value)) {
showToast("请输入2-4位中文姓名");
return false;
}
}
return true;
};
// 打开实名认证对话框
const openRealNameAuth = () => {
dialogStore.openRealNameAuth();
};
// 注意:新系统中税费由后端自动计算,无需前端调用税务豁免接口
const handleSubmit = async () => {
// 检查实名认证状态
if (!agentStore.isRealName) {
showToast("请先完成实名认证");
openRealNameAuth();
return;
}
// 先进行表单验证
if (!validateForm()) return;
// 显示税务确认弹窗
showTaxConfirmPopup.value = true;
};
// 确认提现
const confirmWithdraw = async () => {
isSubmitting.value = true;
try {
const { applyWithdrawal } = await import('@/api/agent');
const payload = {
withdrawal_type: withdrawalType.value,
amount: Number(amount.value),
payee_name: withdrawalType.value === 1 ? realName.value : bankPayeeName.value,
};
if (withdrawalType.value === 1) {
payload.payee_account = alipayAccount.value;
} else {
payload.payee_account = bankCardNo.value.replace(/\s/g, "");
payload.bank_card_no = bankCardNo.value.replace(/\s/g, "");
payload.bank_name = bankName.value.trim();
}
const { data, error } = await applyWithdrawal(payload);
if (data.value?.code === 200 && !error.value) {
status.value = 1; // 新系统申请后状态为1待审核
showTaxConfirmPopup.value = false;
showStatusPopup.value = true;
} else {
showToast(data.value?.msg || '提现申请失败,请重试');
}
} catch (err) {
console.error('提现申请失败:', err);
showToast('提现申请失败,请重试');
} finally {
isSubmitting.value = false;
}
};
// 弹窗操作
const handlePopupAction = () => {
if (status.value === 3) {
showStatusPopup.value = false;
resetForm();
} else {
showStatusPopup.value = false;
if (status.value === 2) resetPage();
}
};
// 填充最大金额
const fillMaxAmount = () => {
amount.value = availableAmount.value;
};
// 重置页面
const resetForm = () => {
status.value = null;
alipayAccount.value = "";
amount.value = "";
realName.value = "";
bankCardNo.value = "";
bankName.value = "";
bankPayeeName.value = "";
};
</script>
<style>
/* 自定义表单样式 */
.van-field__control {
@apply py-1 px-4 text-gray-800;
}
.van-field__error-message {
@apply mt-1;
}
.van-button--disabled {
@apply opacity-60 cursor-not-allowed;
}
/* 弹窗入场动画 */
.van-popup {
transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1),
opacity 0.3s ease;
}
.van-popup-enter-active,
.van-popup-leave-active {
transition: opacity 0.3s;
}
.van-popup-enter-from,
.van-popup-leave-to {
opacity: 0;
}
.van-popup-enter-active {
transform: scale(0.95);
}
.van-popup-enter-to {
transform: scale(1);
}
/* 状态图标动画 */
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
50% {
opacity: 0.5;
}
}
/* 实名认证提示样式 */
.pointer-events-none {
pointer-events: none;
}
</style>