peixue-dev/peidu/uniapp/distributor-package/pages/distributor/withdraw.vue

632 lines
16 KiB
Vue
Raw Normal View History

<template>
<view class="withdraw-page">
<!-- 可提现金额 -->
<view class="balance-card">
<view class="balance-label">可提现佣金</view>
<view class="balance-amount">¥{{ availableAmount }}</view>
<view class="balance-tips">
<text>累计佣金: ¥{{ totalCommission }}</text>
<text>已提现: ¥{{ withdrawnAmount }}</text>
</view>
</view>
<!-- 提现表单 -->
<view class="form-section">
<view class="form-item">
<text class="label">提现金额</text>
<view class="input-wrapper">
<text class="currency">¥</text>
<input
class="input"
type="digit"
v-model="withdrawAmount"
placeholder="请输入提现金额"
@input="onAmountInput"
/>
</view>
<view class="amount-tips">
<text>最低提现: ¥{{ minAmount }}</text>
<text class="all-btn" @click="withdrawAll">全部提现</text>
</view>
</view>
<view class="form-item">
<text class="label">提现方式</text>
<picker
mode="selector"
:range="accountTypes"
range-key="label"
@change="onAccountTypeChange"
>
<view class="picker">
{{ selectedAccountType.label || '请选择提现方式' }}
<uni-icons class="iconfont" type="arrow-right" size="16" color="#999"></uni-icons>
</view>
</picker>
</view>
<view class="form-item" v-if="selectedAccountType.value">
<text class="label">{{ selectedAccountType.value === 1 ? '银行卡号' : '账号' }}</text>
<input
class="input"
v-model="accountNo"
:placeholder="'请输入' + (selectedAccountType.value === 1 ? '银行卡号' : '账号')"
/>
</view>
<view class="form-item" v-if="selectedAccountType.value === 1">
<text class="label">银行名称</text>
<input
class="input"
v-model="bankName"
placeholder="请输入银行名称,如:中国工商银行"
/>
</view>
<view class="form-item" v-if="selectedAccountType.value === 1">
<text class="label">持卡人姓名</text>
<input
class="input"
v-model="cardholderName"
placeholder="请输入持卡人姓名"
/>
</view>
<view class="form-item" v-if="selectedAccountType.value === 2">
<text class="label">账户名</text>
<input
class="input"
v-model="accountName"
placeholder="请输入账户名"
/>
</view>
</view>
<!-- 手续费说明 -->
<view class="fee-info">
<view class="fee-item">
<text>提现金额</text>
<text>¥{{ withdrawAmount || '0.00' }}</text>
</view>
<view class="fee-item">
<text>手续费 ({{ feeRate }}%)</text>
<text class="fee">-¥{{ fee }}</text>
</view>
<view class="fee-item total">
<text>实际到账</text>
<text class="amount">¥{{ actualAmount }}</text>
</view>
</view>
<!-- 提现按钮 -->
<view class="submit-btn" @click="submitWithdraw">
<button class="btn-primary">提交申请</button>
</view>
<!-- 提现记录 -->
<view class="record-section">
<view class="section-title" @click="goToRecords">
<text>提现记录</text>
<view class="more">查看全部 <uni-icons class="iconfont" type="arrow-right" size="16" color="#999"></uni-icons></view>
</view>
<view class="record-list" v-if="records.length > 0">
<view class="record-item" v-for="item in records" :key="item.id">
<view class="record-info">
<view class="record-amount">¥{{ item.amount }}</view>
<view class="record-time">{{ item.applyTime }}</view>
</view>
<view class="record-status" :class="'status-' + item.status">
{{ getStatusText(item.status) }}
</view>
</view>
</view>
<view class="empty" v-else>
<text>暂无提现记录</text>
</view>
</view>
<!-- 提现说明 -->
<view class="tips-section">
<view class="tips-title">提现说明</view>
<view class="tips-content">
<text>1. 提现金额最低{{ minAmount }},最高{{ maxAmount }}</text>
<text>2. 提现手续费为{{ feeRate }}%,从提现金额中扣除</text>
<text>3. 工作日提现,1-3个工作日到账</text>
<text>4. 周末及节假日提现,顺延至工作日处理</text>
<text>5. 请确保账户信息准确,否则可能导致提现失败</text>
</view>
</view>
</view>
</template>
<script>
import { distributorApi } from '@/api/index.js'
import { checkCanWithdraw, applyWithdraw, getWithdrawList } from '@/api/withdraw.js'
export default {
data() {
return {
availableAmount: 0,
totalCommission: 0,
withdrawnAmount: 0,
withdrawAmount: '',
minAmount: 1.00,
maxAmount: 10000.00,
feeRate: 0.6,
accountTypes: [
{ label: '银行卡', value: 1, key: 'bank_card' },
{ label: '支付宝', value: 2, key: 'alipay' },
{ label: '微信', value: 3, key: 'wechat' }
],
selectedAccountType: {},
accountNo: '',
accountName: '',
cardholderName: '',
bankName: '',
records: [],
canWithdraw: true,
canWithdrawMessage: ''
}
},
computed: {
fee() {
if (!this.withdrawAmount) return '0.00'
return (parseFloat(this.withdrawAmount) * this.feeRate / 100).toFixed(2)
},
actualAmount() {
if (!this.withdrawAmount) return '0.00'
return (parseFloat(this.withdrawAmount) - parseFloat(this.fee)).toFixed(2)
}
},
onLoad() {
this.checkWithdrawPermission()
this.loadCommissionInfo()
this.loadWithdrawRecords()
},
methods: {
// 检查提现权限
async checkWithdrawPermission() {
try {
const res = await checkCanWithdraw()
if (res.code === 200 && res.data) {
this.canWithdraw = res.data.canWithdraw
this.canWithdrawMessage = res.data.message || ''
}
} catch (e) {
console.error('检查提现权限失败', e)
}
},
async loadCommissionInfo() {
try {
// 使用佣金统计API
const res = await distributorApi.getCommissionStats()
if (res.code === 200 && res.data) {
this.availableAmount = res.data.availableBalance || 0
this.totalCommission = res.data.totalCommission || 0
this.withdrawnAmount = res.data.withdrawnCommission || 0
}
} catch (e) {
console.error('加载佣金信息失败', e)
}
},
async loadWithdrawRecords() {
try {
const res = await getWithdrawList({ status: null })
if (res.code === 200 && res.data) {
// 只显示最近5条
this.records = (res.data.records || res.data || []).slice(0, 5).map(item => ({
id: item.id,
amount: item.amount,
applyTime: item.applyTime || item.createTime,
status: this.mapStatus(item.status)
}))
}
} catch (e) {
console.error('加载提现记录失败', e)
}
},
// 映射状态值
mapStatus(status) {
const statusMap = {
0: 'pending',
1: 'approved',
2: 'completed',
3: 'rejected'
}
return statusMap[status] || 'pending'
},
onAmountInput(e) {
let value = e.detail.value
// 只允许输入数字和小数点
value = value.replace(/[^\d.]/g, '')
// 只保留一个小数点
const parts = value.split('.')
if (parts.length > 2) {
value = parts[0] + '.' + parts.slice(1).join('')
}
// 小数点后最多两位
if (parts[1] && parts[1].length > 2) {
value = parts[0] + '.' + parts[1].substring(0, 2)
}
this.withdrawAmount = value
},
withdrawAll() {
this.withdrawAmount = this.availableAmount.toString()
},
onAccountTypeChange(e) {
this.selectedAccountType = this.accountTypes[e.detail.value]
// 清空账户信息
this.accountNo = ''
this.accountName = ''
this.cardholderName = ''
},
async submitWithdraw() {
// 检查提现权限
if (!this.canWithdraw) {
uni.showToast({ title: this.canWithdrawMessage || '当前不可提现', icon: 'none' })
return
}
// 验证
if (!this.withdrawAmount) {
uni.showToast({ title: '请输入提现金额', icon: 'none' })
return
}
const amount = parseFloat(this.withdrawAmount)
if (amount < this.minAmount) {
uni.showToast({ title: `最低提现${this.minAmount}`, icon: 'none' })
return
}
if (amount > this.maxAmount) {
uni.showToast({ title: `最高提现${this.maxAmount}`, icon: 'none' })
return
}
if (amount > this.availableAmount) {
uni.showToast({ title: '提现金额超过可用余额', icon: 'none' })
return
}
if (!this.selectedAccountType.value) {
uni.showToast({ title: '请选择提现方式', icon: 'none' })
return
}
if (!this.accountNo) {
uni.showToast({ title: '请输入账号', icon: 'none' })
return
}
if (!this.accountName) {
uni.showToast({ title: '请输入账户名', icon: 'none' })
return
}
if (this.selectedAccountType.value === 1 && !this.cardholderName) {
uni.showToast({ title: '请输入持卡人姓名', icon: 'none' })
return
}
if (this.selectedAccountType.value === 1 && !this.bankName) {
uni.showToast({ title: '请输入银行名称', icon: 'none' })
return
}
try {
uni.showLoading({ title: '提交中...' })
// 构建请求数据
const data = {
amount: amount,
withdrawType: this.selectedAccountType.value
}
// 根据提现方式添加不同字段
if (this.selectedAccountType.value === 1) {
// 银行卡
data.bankName = this.bankName
data.bankAccount = this.accountNo
data.accountName = this.cardholderName
} else if (this.selectedAccountType.value === 2) {
// 支付宝
data.alipayAccount = this.accountNo
data.accountName = this.accountName
} else if (this.selectedAccountType.value === 3) {
// 微信
data.wechatAccount = this.accountNo
data.accountName = this.accountName
}
const res = await applyWithdraw(data)
uni.hideLoading()
if (res.code === 200 || res.success) {
uni.showToast({ title: '提交成功', icon: 'success' })
setTimeout(() => {
// 刷新数据
this.loadCommissionInfo()
this.loadWithdrawRecords()
// 清空表单
this.withdrawAmount = ''
this.selectedAccountType = {}
this.accountNo = ''
this.accountName = ''
this.cardholderName = ''
this.bankName = ''
}, 1500)
} else {
uni.showToast({ title: res.message || '提交失败', icon: 'none' })
}
} catch (e) {
uni.hideLoading()
uni.showToast({ title: e.message || '提交失败', icon: 'none' })
}
},
getStatusText(status) {
const statusMap = {
'pending': '审核中',
'approved': '已通过',
'rejected': '已拒绝',
'completed': '已完成'
}
return statusMap[status] || status
},
goToRecords() {
uni.navigateTo({
url: '/pages/distributor/withdraw-records'
})
}
}
}
</script>
<style lang="scss" scoped>
.withdraw-page {
min-height: 100vh;
background: #f0f9f7;
padding-bottom: 40rpx;
}
.balance-card {
background: linear-gradient(135deg, #2d9687 0%, #7dd3c0 100%);
padding: 60rpx 40rpx;
color: #fff;
.balance-label {
font-size: 28rpx;
opacity: 0.9;
margin-bottom: 20rpx;
}
.balance-amount {
font-size: 72rpx;
font-weight: bold;
margin-bottom: 30rpx;
}
.balance-tips {
display: flex;
justify-content: space-between;
font-size: 24rpx;
opacity: 0.8;
}
}
.form-section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 20rpx;
}
.form-item {
padding: 30rpx 20rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
}
.input-wrapper {
display: flex;
align-items: center;
.currency {
font-size: 40rpx;
color: #2d9687;
margin-right: 10rpx;
}
.input {
flex: 1;
font-size: 40rpx;
color: #333;
}
}
.input {
width: 100%;
font-size: 28rpx;
color: #333;
}
.picker {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
color: #333;
}
.amount-tips {
display: flex;
justify-content: space-between;
margin-top: 20rpx;
font-size: 24rpx;
color: #999;
.all-btn {
color: #2d9687;
}
}
}
.fee-info {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
.fee-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
font-size: 28rpx;
color: #666;
.fee {
color: #ff6b6b;
}
&.total {
border-top: 1rpx solid #f0f0f0;
padding-top: 30rpx;
margin-top: 10rpx;
font-size: 32rpx;
color: #333;
font-weight: bold;
.amount {
color: #2d9687;
font-size: 36rpx;
}
}
}
}
.submit-btn {
padding: 0 20rpx;
margin-top: 40rpx;
.btn-primary {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #2d9687 0%, #7dd3c0 100%);
color: #fff;
border-radius: 44rpx;
font-size: 32rpx;
border: none;
}
}
.record-section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
.section-title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
font-size: 32rpx;
font-weight: bold;
color: #333;
.more {
font-size: 24rpx;
color: #999;
font-weight: normal;
}
}
.record-list {
.record-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.record-info {
.record-amount {
font-size: 32rpx;
color: #333;
font-weight: bold;
margin-bottom: 10rpx;
}
.record-time {
font-size: 24rpx;
color: #999;
}
}
.record-status {
font-size: 24rpx;
padding: 8rpx 20rpx;
border-radius: 20rpx;
&.status-pending {
background: #fff3e0;
color: #ff9800;
}
&.status-approved {
background: #e8f5e9;
color: #4caf50;
}
&.status-rejected {
background: #ffebee;
color: #f44336;
}
&.status-completed {
background: #e0f7f4;
color: #2d9687;
}
}
}
}
.empty {
text-align: center;
padding: 80rpx 0;
color: #999;
font-size: 28rpx;
}
}
.tips-section {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
padding: 30rpx;
.tips-title {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 20rpx;
}
.tips-content {
text {
display: block;
font-size: 24rpx;
color: #999;
line-height: 40rpx;
margin-bottom: 10rpx;
}
}
}
</style>