All checks were successful
continuous-integration/drone/push Build is passing
640 lines
13 KiB
Vue
640 lines
13 KiB
Vue
<template>
|
|
<view class="earnings-page">
|
|
<!-- 自定义导航栏 -->
|
|
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="navbar-content">
|
|
<view class="nav-back" @click="goBack">
|
|
<image class="back-icon" src="/static/ic_back.png" mode="aspectFit"></image>
|
|
</view>
|
|
<text class="navbar-title">我的收益</text>
|
|
<view class="nav-placeholder"></view>
|
|
</view>
|
|
</view>
|
|
<view :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
|
|
<!-- 四种金额状态展示 -->
|
|
<view class="amount-section">
|
|
<view class="amount-grid">
|
|
<view class="amount-item">
|
|
<text class="amount-value">¥{{ earnings.frozen || '0.00' }}</text>
|
|
<text class="amount-label">冻结中</text>
|
|
</view>
|
|
<view class="amount-item">
|
|
<text class="amount-value highlight">¥{{ earnings.available || '0.00' }}</text>
|
|
<text class="amount-label">待提现</text>
|
|
</view>
|
|
<view class="amount-item">
|
|
<text class="amount-value">¥{{ earnings.withdrawing || '0.00' }}</text>
|
|
<text class="amount-label">提现中</text>
|
|
</view>
|
|
<view class="amount-item">
|
|
<text class="amount-value">¥{{ earnings.withdrawn || '0.00' }}</text>
|
|
<text class="amount-label">已提现</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 操作按钮 -->
|
|
<view class="action-section">
|
|
<view class="action-btn primary" @click="openWithdraw">
|
|
<text>申请提现</text>
|
|
</view>
|
|
<view class="action-btn secondary" @click="goEarningsRecord">
|
|
<text>收益记录</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 提现说明 -->
|
|
<view class="guide-link" @click="openGuide">
|
|
<text>点击查看提现说明</text>
|
|
</view>
|
|
|
|
<!-- 提现记录 -->
|
|
<view class="record-section">
|
|
<view class="section-title"><text>提现记录</text></view>
|
|
<view v-if="withdrawals.length === 0" class="empty-tip">
|
|
<text>暂无提现记录</text>
|
|
</view>
|
|
<view v-for="item in withdrawals" :key="item.id" class="record-item">
|
|
<view class="record-left">
|
|
<text class="record-method">{{ item.paymentMethod === 'WeChat' ? '微信' : '支付宝' }}</text>
|
|
<text class="record-time">{{ formatTime(item.createdAt) }}</text>
|
|
</view>
|
|
<view class="record-right">
|
|
<text class="record-amount">-¥{{ item.amount }}</text>
|
|
<text class="record-status" :class="getWithdrawStatusClass(item.status)">{{ getWithdrawStatusLabel(item.status) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 申请提现弹窗 -->
|
|
<view class="modal-mask" v-if="showWithdrawModal" @click="showWithdrawModal = false">
|
|
<view class="modal-content" @click.stop>
|
|
<text class="modal-title">申请提现</text>
|
|
|
|
<view class="withdraw-available">
|
|
<text>可提现金额:</text>
|
|
<text class="available-amount">¥{{ earnings.available || '0.00' }}</text>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">提现金额</text>
|
|
<input
|
|
class="form-input"
|
|
v-model="withdrawForm.amount"
|
|
type="digit"
|
|
:placeholder="`最低${minWithdrawal}元,支持小数点两位`"
|
|
/>
|
|
</view>
|
|
|
|
<view class="withdraw-tip">
|
|
<text>提现将自动转入您的微信零钱</text>
|
|
</view>
|
|
|
|
<view class="modal-actions">
|
|
<button class="modal-btn cancel" @click="showWithdrawModal = false">取消</button>
|
|
<button class="modal-btn confirm" @click="submitWithdraw" :loading="withdrawSubmitting">申请提现</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 提现说明弹窗 -->
|
|
<view class="modal-mask" v-if="showGuideModal" @click="showGuideModal = false">
|
|
<view class="modal-content guide-modal" @click.stop>
|
|
<text class="modal-title">提现说明</text>
|
|
<scroll-view class="guide-content" scroll-y>
|
|
<rich-text :nodes="guideContent"></rich-text>
|
|
</scroll-view>
|
|
<view class="modal-actions">
|
|
<button class="modal-btn confirm full" @click="showGuideModal = false">我知道了</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { getEarnings, getWithdrawals, applyWithdraw, getWithdrawalGuide, getMinWithdrawal } from '../../utils/api'
|
|
import { uploadFile } from '../../utils/request'
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
earnings: {
|
|
frozen: '0.00',
|
|
available: '0.00',
|
|
withdrawing: '0.00',
|
|
withdrawn: '0.00'
|
|
},
|
|
withdrawals: [],
|
|
// 提现弹窗
|
|
showWithdrawModal: false,
|
|
withdrawForm: {
|
|
amount: '',
|
|
paymentMethod: 'WeChat',
|
|
qrImage: '',
|
|
qrImageUrl: ''
|
|
},
|
|
withdrawSubmitting: false,
|
|
// 提现说明弹窗
|
|
showGuideModal: false,
|
|
guideContent: '',
|
|
statusBarHeight: 0,
|
|
minWithdrawal: 1
|
|
}
|
|
},
|
|
onShow() {
|
|
const sysInfo = uni.getSystemInfoSync()
|
|
this.statusBarHeight = sysInfo.statusBarHeight || 0
|
|
this.loadData()
|
|
this.loadMinWithdrawal()
|
|
},
|
|
methods: {
|
|
goBack() { uni.navigateBack() },
|
|
/** 加载最低提现金额配置 */
|
|
async loadMinWithdrawal() {
|
|
try {
|
|
const res = await getMinWithdrawal()
|
|
if (res?.value) this.minWithdrawal = parseFloat(res.value) || 1
|
|
} catch (e) {}
|
|
},
|
|
/** 加载收益和提现数据 */
|
|
async loadData() {
|
|
try {
|
|
const [earningsRes, withdrawalsRes] = await Promise.all([
|
|
getEarnings(),
|
|
getWithdrawals()
|
|
])
|
|
if (earningsRes) {
|
|
this.earnings = {
|
|
frozen: (earningsRes.frozenAmount || 0).toFixed(2),
|
|
available: (earningsRes.availableAmount || 0).toFixed(2),
|
|
withdrawing: (earningsRes.withdrawingAmount || 0).toFixed(2),
|
|
withdrawn: (earningsRes.withdrawnAmount || 0).toFixed(2)
|
|
}
|
|
}
|
|
this.withdrawals = withdrawalsRes?.items || withdrawalsRes || []
|
|
} catch (e) {
|
|
// 静默处理
|
|
}
|
|
},
|
|
|
|
formatTime(dateStr) {
|
|
if (!dateStr) return '-'
|
|
const d = new Date(typeof dateStr === 'string' && !dateStr.endsWith('Z') ? dateStr + 'Z' : dateStr)
|
|
const pad = (n) => String(n).padStart(2, '0')
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`
|
|
},
|
|
|
|
getWithdrawStatusLabel(status) {
|
|
const map = { Pending: '待处理', Processing: '处理中', Completed: '已完成' }
|
|
return map[status] || status
|
|
},
|
|
|
|
getWithdrawStatusClass(status) {
|
|
const map = { Pending: 'ws-pending', Processing: 'ws-processing', Completed: 'ws-done' }
|
|
return map[status] || ''
|
|
},
|
|
|
|
/** 跳转收益记录 */
|
|
goEarningsRecord() {
|
|
uni.navigateTo({ url: '/pages/mine/earnings-record' })
|
|
},
|
|
|
|
/** 打开提现弹窗 */
|
|
openWithdraw() {
|
|
this.withdrawForm = { amount: '', paymentMethod: 'WeChat', qrImage: '', qrImageUrl: '' }
|
|
this.showWithdrawModal = true
|
|
},
|
|
|
|
/** 选择收款二维码图片 */
|
|
chooseQrImage() {
|
|
uni.chooseImage({
|
|
count: 1,
|
|
sourceType: ['album', 'camera'],
|
|
success: (res) => {
|
|
this.withdrawForm.qrImage = res.tempFilePaths[0]
|
|
}
|
|
})
|
|
},
|
|
|
|
/** 提交提现申请 */
|
|
async submitWithdraw() {
|
|
const amount = parseFloat(this.withdrawForm.amount)
|
|
|
|
// 金额校验
|
|
if (isNaN(amount) || amount < this.minWithdrawal) {
|
|
uni.showToast({ title: `提现金额最低${this.minWithdrawal}元`, icon: 'none' })
|
|
return
|
|
}
|
|
|
|
// 小数位校验
|
|
const parts = this.withdrawForm.amount.split('.')
|
|
if (parts.length > 1 && parts[1].length > 2) {
|
|
uni.showToast({ title: '请输入正确的提现金额', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
const available = parseFloat(this.earnings.available) || 0
|
|
if (amount > available) {
|
|
uni.showToast({ title: '超出可提现范围', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
this.withdrawSubmitting = true
|
|
try {
|
|
await applyWithdraw({
|
|
amount,
|
|
paymentMethod: 'WeChat'
|
|
})
|
|
|
|
uni.showToast({ title: '提现申请已提交', icon: 'success' })
|
|
this.showWithdrawModal = false
|
|
this.loadData()
|
|
} catch (e) {
|
|
// 错误已在 request 中处理
|
|
} finally {
|
|
this.withdrawSubmitting = false
|
|
}
|
|
},
|
|
|
|
/** 打开提现说明弹窗 */
|
|
async openGuide() {
|
|
try {
|
|
if (!this.guideContent) {
|
|
const res = await getWithdrawalGuide()
|
|
this.guideContent = res?.value || res?.content || '暂无提现说明'
|
|
}
|
|
this.showGuideModal = true
|
|
} catch (e) {
|
|
uni.showToast({ title: '加载失败', icon: 'none' })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 自定义导航栏 */
|
|
.custom-navbar {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
z-index: 999;
|
|
background: #FFB700;
|
|
}
|
|
.navbar-content {
|
|
height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20rpx;
|
|
}
|
|
.nav-back {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.back-icon {
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
}
|
|
.navbar-title {
|
|
font-size: 34rpx;
|
|
font-weight: bold;
|
|
color: #363636;
|
|
}
|
|
.nav-placeholder {
|
|
width: 60rpx;
|
|
}
|
|
.earnings-page {
|
|
min-height: 100vh;
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
/* 金额状态区域 */
|
|
.amount-section {
|
|
background-color: #FFB700;
|
|
padding: 40rpx 24rpx 50rpx;
|
|
}
|
|
|
|
.amount-grid {
|
|
display: flex;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.amount-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.amount-value {
|
|
font-size: 40rpx;
|
|
color: rgba(255, 255, 255, 0.85);
|
|
font-weight: bold;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
|
|
.amount-value.highlight {
|
|
color: #ffffff;
|
|
}
|
|
|
|
.amount-label {
|
|
font-size: 24rpx;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
}
|
|
|
|
/* 操作按钮 */
|
|
.action-section {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin: -20rpx 24rpx 0;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.action-btn {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 24rpx 0;
|
|
border-radius: 12rpx;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.action-btn.primary {
|
|
background-color: #ffffff;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.action-btn.primary text {
|
|
color: #FFB700;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.action-btn.secondary {
|
|
background-color: #ffffff;
|
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
|
|
}
|
|
|
|
.action-btn.secondary text {
|
|
color: #333333;
|
|
}
|
|
|
|
/* 提现说明链接 */
|
|
.guide-link {
|
|
text-align: center;
|
|
padding: 24rpx 0;
|
|
}
|
|
|
|
.guide-link text {
|
|
font-size: 26rpx;
|
|
color: #FFB700;
|
|
}
|
|
|
|
/* 提现记录 */
|
|
.record-section {
|
|
margin: 0 24rpx;
|
|
background-color: #ffffff;
|
|
border-radius: 16rpx;
|
|
padding: 24rpx 30rpx;
|
|
}
|
|
|
|
.section-title {
|
|
padding-bottom: 16rpx;
|
|
border-bottom: 1rpx solid #f0f0f0;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
|
|
.section-title text {
|
|
font-size: 30rpx;
|
|
color: #333333;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.empty-tip {
|
|
text-align: center;
|
|
padding: 40rpx 0;
|
|
}
|
|
|
|
.empty-tip text {
|
|
font-size: 26rpx;
|
|
color: #999999;
|
|
}
|
|
|
|
.record-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20rpx 0;
|
|
border-bottom: 1rpx solid #f5f5f5;
|
|
}
|
|
|
|
.record-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.record-left {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.record-method {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
margin-bottom: 6rpx;
|
|
}
|
|
|
|
.record-time {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
}
|
|
|
|
.record-right {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-end;
|
|
}
|
|
|
|
.record-amount {
|
|
font-size: 28rpx;
|
|
color: #e64340;
|
|
font-weight: 500;
|
|
margin-bottom: 6rpx;
|
|
}
|
|
|
|
.record-status {
|
|
font-size: 22rpx;
|
|
}
|
|
|
|
.ws-pending { color: #faad14; }
|
|
.ws-processing { color: #FFB700; }
|
|
.ws-done { color: #52c41a; }
|
|
|
|
/* 弹窗通用 */
|
|
.modal-mask {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 999;
|
|
}
|
|
|
|
.modal-content {
|
|
width: 620rpx;
|
|
background-color: #ffffff;
|
|
border-radius: 20rpx;
|
|
padding: 40rpx;
|
|
max-height: 80vh;
|
|
}
|
|
|
|
.modal-title {
|
|
font-size: 34rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
text-align: center;
|
|
display: block;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
/* 提现弹窗 */
|
|
.withdraw-available {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 30rpx;
|
|
font-size: 28rpx;
|
|
color: #666666;
|
|
}
|
|
|
|
.available-amount {
|
|
color: #e64340;
|
|
font-weight: bold;
|
|
font-size: 32rpx;
|
|
margin-left: 8rpx;
|
|
}
|
|
|
|
.form-item {
|
|
margin-bottom: 24rpx;
|
|
}
|
|
|
|
.form-label {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
display: block;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
|
|
.form-input {
|
|
height: 72rpx;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 12rpx;
|
|
padding: 0 20rpx;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.withdraw-tip {
|
|
text-align: center;
|
|
padding: 16rpx 0;
|
|
}
|
|
|
|
.withdraw-tip text {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
|
|
.payment-options {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.payment-option {
|
|
flex: 1;
|
|
text-align: center;
|
|
padding: 16rpx 0;
|
|
border: 1rpx solid #e0e0e0;
|
|
border-radius: 12rpx;
|
|
}
|
|
|
|
.payment-option.active {
|
|
border-color: #FFB700;
|
|
background-color: rgba(255, 183, 0, 0.05);
|
|
}
|
|
|
|
.payment-option text {
|
|
font-size: 28rpx;
|
|
color: #333333;
|
|
}
|
|
|
|
.payment-option.active text {
|
|
color: #FFB700;
|
|
}
|
|
|
|
.upload-area {
|
|
width: 200rpx;
|
|
height: 200rpx;
|
|
border: 2rpx dashed #dddddd;
|
|
border-radius: 12rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.qr-preview {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.upload-placeholder {
|
|
font-size: 26rpx;
|
|
color: #999999;
|
|
}
|
|
|
|
.modal-actions {
|
|
display: flex;
|
|
gap: 20rpx;
|
|
margin-top: 20rpx;
|
|
}
|
|
|
|
.modal-btn {
|
|
flex: 1;
|
|
height: 80rpx;
|
|
line-height: 80rpx;
|
|
font-size: 30rpx;
|
|
border-radius: 40rpx;
|
|
border: none;
|
|
}
|
|
|
|
.modal-btn::after {
|
|
border: none;
|
|
}
|
|
|
|
.modal-btn.confirm {
|
|
background-color: #FAD146;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.modal-btn.full {
|
|
flex: none;
|
|
width: 100%;
|
|
}
|
|
|
|
/* 提现说明弹窗 */
|
|
.guide-modal {
|
|
max-height: 70vh;
|
|
}
|
|
|
|
.guide-content {
|
|
max-height: 50vh;
|
|
margin-bottom: 20rpx;
|
|
font-size: 26rpx;
|
|
color: #333333;
|
|
line-height: 1.8;
|
|
}
|
|
</style>
|