Merge branch 'main' of https://github.com/fnvtk/MBTI_wang
# Conflicts: # api/app/controller/api/Analyze.php # api/app/controller/api/AppConfig.php # api/app/controller/api/Payment.php # api/app/controller/api/Test.php # miniprogram/app.js # miniprogram/pages/enterprise/index.js # miniprogram/pages/enterprise/resume-history.js # miniprogram/pages/index/result.js # miniprogram/pages/index/result.wxml # miniprogram/pages/index/result.wxss # miniprogram/pages/promo/poster.js # miniprogram/pages/recharge/index.js # miniprogram/pages/result/disc.wxss # miniprogram/pages/result/mbti.wxss # miniprogram/pages/result/pdp.wxss # miniprogram/pages/result/resume.js # miniprogram/utils/payment.js # miniprogram/utils/share.js
This commit is contained in:
@@ -1,270 +1,270 @@
|
||||
// pages/promo/index.js
|
||||
const app = getApp()
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
balance: '0.00',
|
||||
totalEarned: '0.00',
|
||||
pendingAmount: '0.00',
|
||||
bindingCount: 0,
|
||||
paidCount: 0,
|
||||
expiringCount: 0,
|
||||
activeTab: 0,
|
||||
userList: [],
|
||||
listTotal: 0,
|
||||
listPage: 1,
|
||||
listLoading: false,
|
||||
listFinished: false,
|
||||
commissionRate: '',
|
||||
bindingDays: '',
|
||||
testCommissionType: '',
|
||||
testCommissionRate: '',
|
||||
testCommissionAmount: '',
|
||||
testNoPayment: false,
|
||||
withdrawMinYuan: '1.00',
|
||||
withdrawMaxYuan: '',
|
||||
withdrawFeePct: 0,
|
||||
requireWithdrawAudit: true,
|
||||
showWithdrawDialog: false,
|
||||
withdrawAmountInput: '',
|
||||
withdrawError: '',
|
||||
withdrawFeeYuan: '0.00',
|
||||
withdrawActualYuan: '0.00'
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadStats()
|
||||
this.loadBindings(true)
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadStats()
|
||||
},
|
||||
|
||||
/** 加载推广统计数据 */
|
||||
loadStats() {
|
||||
request({
|
||||
url: '/api/distribution/stats',
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const d = payload.data
|
||||
const title = d.promoCenterTitle || '推广中心'
|
||||
wx.setNavigationBarTitle({ title })
|
||||
this.setData({
|
||||
balance: d.walletBalance,
|
||||
totalEarned: d.totalEarned,
|
||||
pendingAmount: d.pendingAmount,
|
||||
bindingCount: d.bindingCount,
|
||||
paidCount: d.paidCount,
|
||||
expiringCount: d.expiringCount,
|
||||
totalInvite: d.totalInvite,
|
||||
commissionRate: d.commissionRate,
|
||||
bindingDays: d.bindingDays,
|
||||
testCommissionType: d.testCommissionType,
|
||||
testCommissionRate: d.testCommissionRate,
|
||||
testCommissionAmount: d.testCommissionAmount,
|
||||
testNoPayment: d.testNoPayment,
|
||||
withdrawMinYuan: d.withdrawMinYuan != null ? d.withdrawMinYuan : '1.00',
|
||||
withdrawMaxYuan: d.withdrawMaxYuan != null && d.withdrawMaxYuan !== '' ? d.withdrawMaxYuan : '',
|
||||
withdrawFeePct: d.withdrawFeePct != null ? d.withdrawFeePct : 0,
|
||||
requireWithdrawAudit: d.requireWithdrawAudit !== false,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 加载绑定用户列表(tab: 0=绑定中 1=已付款 2=已过期) */
|
||||
loadBindings(reset = false) {
|
||||
if (this.data.listLoading) return
|
||||
if (!reset && this.data.listFinished) return
|
||||
|
||||
const page = reset ? 1 : this.data.listPage
|
||||
this.setData({ listLoading: true })
|
||||
|
||||
request({
|
||||
url: `/api/distribution/bindings?tab=${this.data.activeTab}&page=${page}&pageSize=10`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const { list, total } = payload.data
|
||||
const formatted = (list || []).map(item => ({
|
||||
...item,
|
||||
createdAtStr: item.createdAt ? this._fmtTimestamp(item.createdAt) : ''
|
||||
}))
|
||||
const newList = reset ? formatted : [...this.data.userList, ...formatted]
|
||||
this.setData({
|
||||
userList: newList,
|
||||
listTotal: total,
|
||||
listPage: page + 1,
|
||||
listFinished: newList.length >= total,
|
||||
})
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.setData({ listLoading: false })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 时间戳格式化为 YYYY-MM-DD */
|
||||
_fmtTimestamp(ts) {
|
||||
if (!ts) return ''
|
||||
const d = new Date(ts * 1000)
|
||||
if (isNaN(d.getTime())) return ''
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${day}`
|
||||
},
|
||||
|
||||
/** 切换用户列表 Tab */
|
||||
switchTab(e) {
|
||||
const index = parseInt(e.currentTarget.dataset.index)
|
||||
if (index === this.data.activeTab) return
|
||||
this.setData({ activeTab: index, userList: [], listPage: 1, listFinished: false })
|
||||
this.loadBindings(true)
|
||||
},
|
||||
|
||||
/** 上拉加载更多 */
|
||||
onReachBottom() {
|
||||
this.loadBindings(false)
|
||||
},
|
||||
|
||||
/** 申请提现:打开自定义金额弹框 */
|
||||
handleWithdraw() {
|
||||
const balance = parseFloat(this.data.balance)
|
||||
if (balance < 1) {
|
||||
wx.showToast({ title: '余额不足1元,暂无法提现', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
const feeYuan = (balance * pct / 100).toFixed(2)
|
||||
const actualYuan = (balance - balance * pct / 100).toFixed(2)
|
||||
this.setData({
|
||||
showWithdrawDialog: true,
|
||||
withdrawAmountInput: this.data.balance,
|
||||
withdrawFeeYuan: feeYuan,
|
||||
withdrawActualYuan: actualYuan,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 关闭提现弹框 */
|
||||
closeWithdrawDialog() {
|
||||
this.setData({
|
||||
showWithdrawDialog: false,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 输入金额:实时计算手续费与实际到账 */
|
||||
onWithdrawInput(e) {
|
||||
const raw = e.detail.value
|
||||
const val = parseFloat(raw)
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
let feeYuan = '0.00'
|
||||
let actualYuan = '0.00'
|
||||
if (raw !== '' && !isNaN(val) && val >= 0) {
|
||||
const fee = val * pct / 100
|
||||
feeYuan = fee.toFixed(2)
|
||||
actualYuan = (val - fee).toFixed(2)
|
||||
}
|
||||
this.setData({
|
||||
withdrawAmountInput: raw,
|
||||
withdrawFeeYuan: feeYuan,
|
||||
withdrawActualYuan: actualYuan,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 确认提现(使用用户填写的金额) */
|
||||
confirmWithdraw() {
|
||||
const balance = parseFloat(this.data.balance)
|
||||
const val = parseFloat(this.data.withdrawAmountInput)
|
||||
const minYuan = parseFloat(this.data.withdrawMinYuan) || 1
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
|
||||
if (isNaN(val)) {
|
||||
this.setData({ withdrawError: '请输入正确的金额' })
|
||||
return
|
||||
}
|
||||
if (val < 1) {
|
||||
this.setData({ withdrawError: '单次提现金额至少 1 元' })
|
||||
return
|
||||
}
|
||||
if (val > balance) {
|
||||
this.setData({ withdrawError: '不可超过当前可提现金额' })
|
||||
return
|
||||
}
|
||||
const actualYuan = val - val * pct / 100
|
||||
if (actualYuan < minYuan) {
|
||||
this.setData({ withdrawError: `实际到账金额不得低于最低提现金额 ¥${minYuan.toFixed(2)}` })
|
||||
return
|
||||
}
|
||||
|
||||
const amountFen = Math.floor(val * 100)
|
||||
this.setData({ withdrawError: '' })
|
||||
|
||||
request({
|
||||
url: '/api/distribution/withdraw',
|
||||
method: 'POST',
|
||||
data: { amountFen },
|
||||
success: (r) => {
|
||||
const payload = r && r.data
|
||||
if (payload && payload.code === 200) {
|
||||
const msg = payload.msg || payload.message || ''
|
||||
wx.showToast({ title: '申请已提交', icon: 'success' })
|
||||
this.setData({ showWithdrawDialog: false })
|
||||
this.loadStats()
|
||||
// 免审核且已自动发起微信转账时,自动进入提现记录页
|
||||
if (msg.indexOf('已自动发起') !== -1) {
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({ url: '/pages/promo/withdrawals' })
|
||||
}, 500)
|
||||
}
|
||||
} else {
|
||||
const errMsg = (payload && (payload.msg || payload.message)) || '申请失败,请稍后重试'
|
||||
this.setData({ withdrawError: errMsg })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 查看提现记录 */
|
||||
goToWithdrawHistory() {
|
||||
wx.navigateTo({ url: '/pages/promo/withdrawals' })
|
||||
},
|
||||
|
||||
/** 生成海报 */
|
||||
generatePoster() {
|
||||
wx.navigateTo({ url: '/pages/promo/poster' })
|
||||
},
|
||||
|
||||
/** 分享到朋友圈:引导用户使用右上角菜单 */
|
||||
shareToTimeline() {
|
||||
wx.showToast({ title: '请点击右上角 ··· 选择「分享到朋友圈」', icon: 'none', duration: 2500 })
|
||||
},
|
||||
|
||||
/** 分享给好友 */
|
||||
onShareAppMessage() {
|
||||
const { getSharePathByScope } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队AI性格测试 - 发现你的内在潜能',
|
||||
path: getSharePathByScope('/pages/index/index')
|
||||
}
|
||||
},
|
||||
|
||||
/** 分享到朋友圈 */
|
||||
onShareTimeline() {
|
||||
const { buildShareQuery } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队AI性格测试 - 发现你的内在潜能',
|
||||
query: buildShareQuery()
|
||||
}
|
||||
}
|
||||
})
|
||||
// pages/promo/index.js
|
||||
const app = getApp()
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
balance: '0.00',
|
||||
totalEarned: '0.00',
|
||||
pendingAmount: '0.00',
|
||||
bindingCount: 0,
|
||||
paidCount: 0,
|
||||
expiringCount: 0,
|
||||
activeTab: 0,
|
||||
userList: [],
|
||||
listTotal: 0,
|
||||
listPage: 1,
|
||||
listLoading: false,
|
||||
listFinished: false,
|
||||
commissionRate: '',
|
||||
bindingDays: '',
|
||||
testCommissionType: '',
|
||||
testCommissionRate: '',
|
||||
testCommissionAmount: '',
|
||||
testNoPayment: false,
|
||||
withdrawMinYuan: '1.00',
|
||||
withdrawMaxYuan: '',
|
||||
withdrawFeePct: 0,
|
||||
requireWithdrawAudit: true,
|
||||
showWithdrawDialog: false,
|
||||
withdrawAmountInput: '',
|
||||
withdrawError: '',
|
||||
withdrawFeeYuan: '0.00',
|
||||
withdrawActualYuan: '0.00'
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadStats()
|
||||
this.loadBindings(true)
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this.loadStats()
|
||||
},
|
||||
|
||||
/** 加载推广统计数据 */
|
||||
loadStats() {
|
||||
request({
|
||||
url: '/api/distribution/stats',
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const d = payload.data
|
||||
const title = d.promoCenterTitle || '推广中心'
|
||||
wx.setNavigationBarTitle({ title })
|
||||
this.setData({
|
||||
balance: d.walletBalance,
|
||||
totalEarned: d.totalEarned,
|
||||
pendingAmount: d.pendingAmount,
|
||||
bindingCount: d.bindingCount,
|
||||
paidCount: d.paidCount,
|
||||
expiringCount: d.expiringCount,
|
||||
totalInvite: d.totalInvite,
|
||||
commissionRate: d.commissionRate,
|
||||
bindingDays: d.bindingDays,
|
||||
testCommissionType: d.testCommissionType,
|
||||
testCommissionRate: d.testCommissionRate,
|
||||
testCommissionAmount: d.testCommissionAmount,
|
||||
testNoPayment: d.testNoPayment,
|
||||
withdrawMinYuan: d.withdrawMinYuan != null ? d.withdrawMinYuan : '1.00',
|
||||
withdrawMaxYuan: d.withdrawMaxYuan != null && d.withdrawMaxYuan !== '' ? d.withdrawMaxYuan : '',
|
||||
withdrawFeePct: d.withdrawFeePct != null ? d.withdrawFeePct : 0,
|
||||
requireWithdrawAudit: d.requireWithdrawAudit !== false,
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 加载绑定用户列表(tab: 0=绑定中 1=已付款 2=已过期) */
|
||||
loadBindings(reset = false) {
|
||||
if (this.data.listLoading) return
|
||||
if (!reset && this.data.listFinished) return
|
||||
|
||||
const page = reset ? 1 : this.data.listPage
|
||||
this.setData({ listLoading: true })
|
||||
|
||||
request({
|
||||
url: `/api/distribution/bindings?tab=${this.data.activeTab}&page=${page}&pageSize=10`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const { list, total } = payload.data
|
||||
const formatted = (list || []).map(item => ({
|
||||
...item,
|
||||
createdAtStr: item.createdAt ? this._fmtTimestamp(item.createdAt) : ''
|
||||
}))
|
||||
const newList = reset ? formatted : [...this.data.userList, ...formatted]
|
||||
this.setData({
|
||||
userList: newList,
|
||||
listTotal: total,
|
||||
listPage: page + 1,
|
||||
listFinished: newList.length >= total,
|
||||
})
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.setData({ listLoading: false })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 时间戳格式化为 YYYY-MM-DD */
|
||||
_fmtTimestamp(ts) {
|
||||
if (!ts) return ''
|
||||
const d = new Date(ts * 1000)
|
||||
if (isNaN(d.getTime())) return ''
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${day}`
|
||||
},
|
||||
|
||||
/** 切换用户列表 Tab */
|
||||
switchTab(e) {
|
||||
const index = parseInt(e.currentTarget.dataset.index)
|
||||
if (index === this.data.activeTab) return
|
||||
this.setData({ activeTab: index, userList: [], listPage: 1, listFinished: false })
|
||||
this.loadBindings(true)
|
||||
},
|
||||
|
||||
/** 上拉加载更多 */
|
||||
onReachBottom() {
|
||||
this.loadBindings(false)
|
||||
},
|
||||
|
||||
/** 申请提现:打开自定义金额弹框 */
|
||||
handleWithdraw() {
|
||||
const balance = parseFloat(this.data.balance)
|
||||
if (balance < 1) {
|
||||
wx.showToast({ title: '余额不足1元,暂无法提现', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
const feeYuan = (balance * pct / 100).toFixed(2)
|
||||
const actualYuan = (balance - balance * pct / 100).toFixed(2)
|
||||
this.setData({
|
||||
showWithdrawDialog: true,
|
||||
withdrawAmountInput: this.data.balance,
|
||||
withdrawFeeYuan: feeYuan,
|
||||
withdrawActualYuan: actualYuan,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 关闭提现弹框 */
|
||||
closeWithdrawDialog() {
|
||||
this.setData({
|
||||
showWithdrawDialog: false,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 输入金额:实时计算手续费与实际到账 */
|
||||
onWithdrawInput(e) {
|
||||
const raw = e.detail.value
|
||||
const val = parseFloat(raw)
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
let feeYuan = '0.00'
|
||||
let actualYuan = '0.00'
|
||||
if (raw !== '' && !isNaN(val) && val >= 0) {
|
||||
const fee = val * pct / 100
|
||||
feeYuan = fee.toFixed(2)
|
||||
actualYuan = (val - fee).toFixed(2)
|
||||
}
|
||||
this.setData({
|
||||
withdrawAmountInput: raw,
|
||||
withdrawFeeYuan: feeYuan,
|
||||
withdrawActualYuan: actualYuan,
|
||||
withdrawError: ''
|
||||
})
|
||||
},
|
||||
|
||||
/** 确认提现(使用用户填写的金额) */
|
||||
confirmWithdraw() {
|
||||
const balance = parseFloat(this.data.balance)
|
||||
const val = parseFloat(this.data.withdrawAmountInput)
|
||||
const minYuan = parseFloat(this.data.withdrawMinYuan) || 1
|
||||
const pct = this.data.withdrawFeePct || 0
|
||||
|
||||
if (isNaN(val)) {
|
||||
this.setData({ withdrawError: '请输入正确的金额' })
|
||||
return
|
||||
}
|
||||
if (val < 1) {
|
||||
this.setData({ withdrawError: '单次提现金额至少 1 元' })
|
||||
return
|
||||
}
|
||||
if (val > balance) {
|
||||
this.setData({ withdrawError: '不可超过当前可提现金额' })
|
||||
return
|
||||
}
|
||||
const actualYuan = val - val * pct / 100
|
||||
if (actualYuan < minYuan) {
|
||||
this.setData({ withdrawError: `实际到账金额不得低于最低提现金额 ¥${minYuan.toFixed(2)}` })
|
||||
return
|
||||
}
|
||||
|
||||
const amountFen = Math.floor(val * 100)
|
||||
this.setData({ withdrawError: '' })
|
||||
|
||||
request({
|
||||
url: '/api/distribution/withdraw',
|
||||
method: 'POST',
|
||||
data: { amountFen },
|
||||
success: (r) => {
|
||||
const payload = r && r.data
|
||||
if (payload && payload.code === 200) {
|
||||
const msg = payload.msg || payload.message || ''
|
||||
wx.showToast({ title: '申请已提交', icon: 'success' })
|
||||
this.setData({ showWithdrawDialog: false })
|
||||
this.loadStats()
|
||||
// 免审核且已自动发起微信转账时,自动进入提现记录页
|
||||
if (msg.indexOf('已自动发起') !== -1) {
|
||||
setTimeout(() => {
|
||||
wx.navigateTo({ url: '/pages/promo/withdrawals' })
|
||||
}, 500)
|
||||
}
|
||||
} else {
|
||||
const errMsg = (payload && (payload.msg || payload.message)) || '申请失败,请稍后重试'
|
||||
this.setData({ withdrawError: errMsg })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 查看提现记录 */
|
||||
goToWithdrawHistory() {
|
||||
wx.navigateTo({ url: '/pages/promo/withdrawals' })
|
||||
},
|
||||
|
||||
/** 生成海报 */
|
||||
generatePoster() {
|
||||
wx.navigateTo({ url: '/pages/promo/poster' })
|
||||
},
|
||||
|
||||
/** 分享到朋友圈:引导用户使用右上角菜单 */
|
||||
shareToTimeline() {
|
||||
wx.showToast({ title: '请点击右上角 ··· 选择「分享到朋友圈」', icon: 'none', duration: 2500 })
|
||||
},
|
||||
|
||||
/** 分享给好友 */
|
||||
onShareAppMessage() {
|
||||
const { getSharePathByScope } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队性格测试 - 发现你的内在潜能',
|
||||
path: getSharePathByScope('/pages/index/index')
|
||||
}
|
||||
},
|
||||
|
||||
/** 分享到朋友圈 */
|
||||
onShareTimeline() {
|
||||
const { buildShareQuery } = require('../../utils/share')
|
||||
return {
|
||||
title: '神仙团队性格测试 - 发现你的内在潜能',
|
||||
query: buildShareQuery()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "推广中心",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black"
|
||||
{
|
||||
"navigationBarTitleText": "推广中心",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
@@ -1,219 +1,219 @@
|
||||
<!--pages/promo/index.wxml-->
|
||||
<view class="container">
|
||||
<!-- 顶部统计卡片 -->
|
||||
<view class="hero-card">
|
||||
<view class="hero-header-row">
|
||||
<view class="hero-left">
|
||||
<view class="wallet-icon">
|
||||
<text class="emoji">🧧</text>
|
||||
</view>
|
||||
<view class="title-text">
|
||||
<text class="label">可提现金额</text>
|
||||
<view class="badge">
|
||||
<view class="dot"></view>
|
||||
<text class="badge-text">{{commissionRate}}% 高额返利</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="hero-right">
|
||||
<text class="amount">¥{{balance}}</text>
|
||||
<text class="amount-sub">累计: ¥{{totalEarned}} | 待审核: ¥{{pendingAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="withdraw-btn {{balance > 0 ? '' : 'disabled'}}" bindtap="handleWithdraw">
|
||||
<text>{{balance > 0 ? '立即提现' : '暂无提现'}}</text>
|
||||
</button>
|
||||
|
||||
<view class="record-link" bindtap="goToWithdrawHistory">
|
||||
<text>查看提现记录</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义提现金额弹框 -->
|
||||
<view class="withdraw-mask" wx:if="{{showWithdrawDialog}}">
|
||||
<view class="withdraw-dialog">
|
||||
<view class="dialog-title">申请提现</view>
|
||||
<view class="dialog-sub">可提现 ¥{{balance}},请输入本次提现金额</view>
|
||||
|
||||
<view class="amount-input-row">
|
||||
<text class="currency">¥</text>
|
||||
<input
|
||||
class="amount-input"
|
||||
type="digit"
|
||||
focus="true"
|
||||
value="{{withdrawAmountInput}}"
|
||||
placeholder="至少 1.00 元"
|
||||
bindinput="onWithdrawInput"
|
||||
/>
|
||||
</view>
|
||||
<view class="amount-hint">
|
||||
<text>本次最高可提 ¥{{balance}}</text>
|
||||
</view>
|
||||
<view class="fee-actual-row" wx:if="{{withdrawFeePct > 0}}">
|
||||
<text>手续费 ¥{{withdrawFeeYuan}}</text>
|
||||
<text class="actual">实际到账 ¥{{withdrawActualYuan}}</text>
|
||||
</view>
|
||||
<view class="error-text" wx:if="{{withdrawError}}">
|
||||
<text>{{withdrawError}}</text>
|
||||
</view>
|
||||
|
||||
<view class="dialog-actions">
|
||||
<view class="btn cancel" bindtap="closeWithdrawDialog">取消</view>
|
||||
<view class="btn confirm" bindtap="confirmWithdraw">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 四格统计 -->
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{bindingCount}}</text>
|
||||
<text class="stat-label">绑定中</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{paidCount}}</text>
|
||||
<text class="stat-label">已付款</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val highlight">{{expiringCount}}</text>
|
||||
<text class="stat-label">即将过期</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{totalInvite}}</text>
|
||||
<text class="stat-label">总邀请</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收益规则 -->
|
||||
<view class="section rule-section">
|
||||
<view class="section-header">
|
||||
<view class="header-icon red">
|
||||
<text class="emoji-sm">🛡️</text>
|
||||
</view>
|
||||
<text class="section-title">推广收益规则</text>
|
||||
</view>
|
||||
<view class="rule-list">
|
||||
<view class="rule-item">
|
||||
<text class="rule-bullet">✨</text>
|
||||
<text class="rule-text">用户绑定有效期为 <text class="highlight">{{bindingDays}}天</text>,期满自动解除</text>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<text class="rule-bullet">💰</text>
|
||||
<text class="rule-text">单笔提现:最低 <text class="highlight">¥{{withdrawMinYuan}}</text>,</text>
|
||||
<text class="rule-text" wx:if="{{withdrawMaxYuan}}">最高 <text class="highlight">¥{{withdrawMaxYuan}}</text></text>
|
||||
<text class="rule-text" wx:else>不设上限</text>
|
||||
</view>
|
||||
<view class="rule-item" wx:if="{{withdrawFeePct > 0}}">
|
||||
<text class="rule-bullet">📋</text>
|
||||
<text class="rule-text">提现手续费:<text class="highlight">{{withdrawFeePct}}%</text></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 绑定用户列表 -->
|
||||
<view class="section user-section">
|
||||
<view class="section-header border-b">
|
||||
<view class="header-left">
|
||||
<text class="emoji-sm">👥</text>
|
||||
<text class="section-title">绑定用户</text>
|
||||
<text class="count-label">({{totalInvite}})</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Tab 切换 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item {{activeTab === 0 ? 'active' : ''}}" bindtap="switchTab" data-index="0">
|
||||
绑定中 ({{bindingCount}})
|
||||
</view>
|
||||
<view class="tab-item {{activeTab === 1 ? 'active' : ''}}" bindtap="switchTab" data-index="1">
|
||||
已付款 ({{paidCount}})
|
||||
</view>
|
||||
<view class="tab-item {{activeTab === 2 ? 'active' : ''}}" bindtap="switchTab" data-index="2">
|
||||
已过期 ({{expiringCount}})
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:if="{{userList.length === 0}}">
|
||||
<view class="empty-icon">
|
||||
<text class="emoji-lg">🔍</text>
|
||||
</view>
|
||||
<text class="empty-text">目前还没有绑定的用户哦</text>
|
||||
<text class="empty-sub">快去分享链接邀请好友吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" wx:else>
|
||||
<view class="user-item" wx:for="{{userList}}" wx:key="id">
|
||||
<!-- 头像 -->
|
||||
<image wx:if="{{item.avatar}}" class="user-avatar" src="{{item.avatar}}" mode="aspectFill"/>
|
||||
<view wx:else class="user-avatar user-avatar-placeholder">
|
||||
<text class="user-avatar-letter">{{item.nickname ? item.nickname[0] : '?'}}</text>
|
||||
</view>
|
||||
<!-- 昵称 + 绑定时间 -->
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{item.nickname || '微信用户'}}</text>
|
||||
<text class="user-time">绑定于 {{item.createdAtStr}}</text>
|
||||
</view>
|
||||
<!-- 状态 / 剩余天数 -->
|
||||
<view class="user-badge {{item.status === 'active' ? 'badge-active' : (item.status === 'expired' ? 'badge-expired' : 'badge-paid')}}">
|
||||
<text wx:if="{{item.status === 'active'}}">{{item.remainDays}}天到期</text>
|
||||
<text wx:elif="{{item.status === 'expired'}}">已过期</text>
|
||||
<text wx:else>已付款</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 / 没有更多 -->
|
||||
<view class="list-footer" wx:if="{{listLoading}}">
|
||||
<text class="list-footer-text">加载中...</text>
|
||||
</view>
|
||||
<view class="list-footer" wx:elif="{{listFinished && userList.length > 0}}">
|
||||
<text class="list-footer-text">— 已加载全部 —</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作菜单 -->
|
||||
<view class="menu-card">
|
||||
<view class="menu-item" bindtap="generatePoster">
|
||||
<view class="menu-icon-wrap rose">
|
||||
<text class="emoji-sm">🎨</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">生成推广海报</text>
|
||||
<text class="menu-desc">一键生成您的专属精美海报</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="shareToTimeline">
|
||||
<view class="menu-icon-wrap emerald">
|
||||
<text class="emoji-sm">💬</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">分享到朋友圈</text>
|
||||
<text class="menu-desc">通过右上角菜单分享到朋友圈</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<button class="menu-item share-btn" open-type="share">
|
||||
<view class="menu-icon-wrap violet">
|
||||
<text class="emoji-sm">📤</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">分享给好友</text>
|
||||
<text class="menu-desc">通过系统面板发送给微信好友</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<view class="footer-tip">
|
||||
<text>分享专属链接,好友通过链接点击后自动绑定</text>
|
||||
<text>购买任意测评即可获得 <text class="highlight">90%</text> 的现金返利</text>
|
||||
</view>
|
||||
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
<!--pages/promo/index.wxml-->
|
||||
<view class="container">
|
||||
<!-- 顶部统计卡片 -->
|
||||
<view class="hero-card">
|
||||
<view class="hero-header-row">
|
||||
<view class="hero-left">
|
||||
<view class="wallet-icon">
|
||||
<text class="emoji">🧧</text>
|
||||
</view>
|
||||
<view class="title-text">
|
||||
<text class="label">可提现金额</text>
|
||||
<view class="badge">
|
||||
<view class="dot"></view>
|
||||
<text class="badge-text">{{commissionRate}}% 高额返利</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="hero-right">
|
||||
<text class="amount">¥{{balance}}</text>
|
||||
<text class="amount-sub">累计: ¥{{totalEarned}} | 待审核: ¥{{pendingAmount}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<button class="withdraw-btn {{balance > 0 ? '' : 'disabled'}}" bindtap="handleWithdraw">
|
||||
<text>{{balance > 0 ? '立即提现' : '暂无提现'}}</text>
|
||||
</button>
|
||||
|
||||
<view class="record-link" bindtap="goToWithdrawHistory">
|
||||
<text>查看提现记录</text>
|
||||
<text class="arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 自定义提现金额弹框 -->
|
||||
<view class="withdraw-mask" wx:if="{{showWithdrawDialog}}">
|
||||
<view class="withdraw-dialog">
|
||||
<view class="dialog-title">申请提现</view>
|
||||
<view class="dialog-sub">可提现 ¥{{balance}},请输入本次提现金额</view>
|
||||
|
||||
<view class="amount-input-row">
|
||||
<text class="currency">¥</text>
|
||||
<input
|
||||
class="amount-input"
|
||||
type="digit"
|
||||
focus="true"
|
||||
value="{{withdrawAmountInput}}"
|
||||
placeholder="至少 1.00 元"
|
||||
bindinput="onWithdrawInput"
|
||||
/>
|
||||
</view>
|
||||
<view class="amount-hint">
|
||||
<text>本次最高可提 ¥{{balance}}</text>
|
||||
</view>
|
||||
<view class="fee-actual-row" wx:if="{{withdrawFeePct > 0}}">
|
||||
<text>手续费 ¥{{withdrawFeeYuan}}</text>
|
||||
<text class="actual">实际到账 ¥{{withdrawActualYuan}}</text>
|
||||
</view>
|
||||
<view class="error-text" wx:if="{{withdrawError}}">
|
||||
<text>{{withdrawError}}</text>
|
||||
</view>
|
||||
|
||||
<view class="dialog-actions">
|
||||
<view class="btn cancel" bindtap="closeWithdrawDialog">取消</view>
|
||||
<view class="btn confirm" bindtap="confirmWithdraw">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 四格统计 -->
|
||||
<view class="stats-grid">
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{bindingCount}}</text>
|
||||
<text class="stat-label">绑定中</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{paidCount}}</text>
|
||||
<text class="stat-label">已付款</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val highlight">{{expiringCount}}</text>
|
||||
<text class="stat-label">即将过期</text>
|
||||
</view>
|
||||
<view class="stat-item">
|
||||
<text class="stat-val">{{totalInvite}}</text>
|
||||
<text class="stat-label">总邀请</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 收益规则 -->
|
||||
<view class="section rule-section">
|
||||
<view class="section-header">
|
||||
<view class="header-icon red">
|
||||
<text class="emoji-sm">🛡️</text>
|
||||
</view>
|
||||
<text class="section-title">推广收益规则</text>
|
||||
</view>
|
||||
<view class="rule-list">
|
||||
<view class="rule-item">
|
||||
<text class="rule-bullet">✨</text>
|
||||
<text class="rule-text">用户绑定有效期为 <text class="highlight">{{bindingDays}}天</text>,期满自动解除</text>
|
||||
</view>
|
||||
<view class="rule-item">
|
||||
<text class="rule-bullet">💰</text>
|
||||
<text class="rule-text">单笔提现:最低 <text class="highlight">¥{{withdrawMinYuan}}</text>,</text>
|
||||
<text class="rule-text" wx:if="{{withdrawMaxYuan}}">最高 <text class="highlight">¥{{withdrawMaxYuan}}</text></text>
|
||||
<text class="rule-text" wx:else>不设上限</text>
|
||||
</view>
|
||||
<view class="rule-item" wx:if="{{withdrawFeePct > 0}}">
|
||||
<text class="rule-bullet">📋</text>
|
||||
<text class="rule-text">提现手续费:<text class="highlight">{{withdrawFeePct}}%</text></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 绑定用户列表 -->
|
||||
<view class="section user-section">
|
||||
<view class="section-header border-b">
|
||||
<view class="header-left">
|
||||
<text class="emoji-sm">👥</text>
|
||||
<text class="section-title">绑定用户</text>
|
||||
<text class="count-label">({{totalInvite}})</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Tab 切换 -->
|
||||
<view class="tab-bar">
|
||||
<view class="tab-item {{activeTab === 0 ? 'active' : ''}}" bindtap="switchTab" data-index="0">
|
||||
绑定中 ({{bindingCount}})
|
||||
</view>
|
||||
<view class="tab-item {{activeTab === 1 ? 'active' : ''}}" bindtap="switchTab" data-index="1">
|
||||
已付款 ({{paidCount}})
|
||||
</view>
|
||||
<view class="tab-item {{activeTab === 2 ? 'active' : ''}}" bindtap="switchTab" data-index="2">
|
||||
已过期 ({{expiringCount}})
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty-state" wx:if="{{userList.length === 0}}">
|
||||
<view class="empty-icon">
|
||||
<text class="emoji-lg">🔍</text>
|
||||
</view>
|
||||
<text class="empty-text">目前还没有绑定的用户哦</text>
|
||||
<text class="empty-sub">快去分享链接邀请好友吧</text>
|
||||
</view>
|
||||
|
||||
<!-- 用户列表 -->
|
||||
<view class="user-list" wx:else>
|
||||
<view class="user-item" wx:for="{{userList}}" wx:key="id">
|
||||
<!-- 头像 -->
|
||||
<image wx:if="{{item.avatar}}" class="user-avatar" src="{{item.avatar}}" mode="aspectFill"/>
|
||||
<view wx:else class="user-avatar user-avatar-placeholder">
|
||||
<text class="user-avatar-letter">{{item.nickname ? item.nickname[0] : '?'}}</text>
|
||||
</view>
|
||||
<!-- 昵称 + 绑定时间 -->
|
||||
<view class="user-info">
|
||||
<text class="user-name">{{item.nickname || '微信用户'}}</text>
|
||||
<text class="user-time">绑定于 {{item.createdAtStr}}</text>
|
||||
</view>
|
||||
<!-- 状态 / 剩余天数 -->
|
||||
<view class="user-badge {{item.status === 'active' ? 'badge-active' : (item.status === 'expired' ? 'badge-expired' : 'badge-paid')}}">
|
||||
<text wx:if="{{item.status === 'active'}}">{{item.remainDays}}天到期</text>
|
||||
<text wx:elif="{{item.status === 'expired'}}">已过期</text>
|
||||
<text wx:else>已付款</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 / 没有更多 -->
|
||||
<view class="list-footer" wx:if="{{listLoading}}">
|
||||
<text class="list-footer-text">加载中...</text>
|
||||
</view>
|
||||
<view class="list-footer" wx:elif="{{listFinished && userList.length > 0}}">
|
||||
<text class="list-footer-text">— 已加载全部 —</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作菜单 -->
|
||||
<view class="menu-card">
|
||||
<view class="menu-item" bindtap="generatePoster">
|
||||
<view class="menu-icon-wrap rose">
|
||||
<text class="emoji-sm">🎨</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">生成推广海报</text>
|
||||
<text class="menu-desc">一键生成您的专属精美海报</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<view class="menu-item" bindtap="shareToTimeline">
|
||||
<view class="menu-icon-wrap emerald">
|
||||
<text class="emoji-sm">💬</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">分享到朋友圈</text>
|
||||
<text class="menu-desc">通过右上角菜单分享到朋友圈</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</view>
|
||||
<button class="menu-item share-btn" open-type="share">
|
||||
<view class="menu-icon-wrap violet">
|
||||
<text class="emoji-sm">📤</text>
|
||||
</view>
|
||||
<view class="menu-info">
|
||||
<text class="menu-name">分享给好友</text>
|
||||
<text class="menu-desc">通过系统面板发送给微信好友</text>
|
||||
</view>
|
||||
<text class="menu-arrow">›</text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<view class="footer-tip">
|
||||
<text>分享专属链接,好友通过链接点击后自动绑定</text>
|
||||
<text>购买任意测评即可获得 <text class="highlight">90%</text> 的现金返利</text>
|
||||
</view>
|
||||
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"navigationBarTitleText": "生成推广海报",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {}
|
||||
{
|
||||
"navigationBarTitleText": "生成推广海报",
|
||||
"navigationBarBackgroundColor": "#FFFFFF",
|
||||
"navigationBarTextStyle": "black",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
<view class="container">
|
||||
<view class="poster-preview">
|
||||
<image wx:if="{{posterUrl}}" class="poster-img" src="{{posterUrl}}" mode="widthFix" />
|
||||
<view wx:if="{{loading}}" class="loading-mask">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">{{loadingText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-bar">
|
||||
<button class="save-btn" bindtap="savePoster" disabled="{{loading || !posterUrl}}">
|
||||
<text>保存海报分享</text>
|
||||
</button>
|
||||
<view class="tip-text">保存到相册后可分享至朋友圈</view>
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="container">
|
||||
<view class="poster-preview">
|
||||
<image wx:if="{{posterUrl}}" class="poster-img" src="{{posterUrl}}" mode="widthFix" />
|
||||
<view wx:if="{{loading}}" class="loading-mask">
|
||||
<view class="loading-spinner"></view>
|
||||
<text class="loading-text">{{loadingText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="action-bar">
|
||||
<button class="save-btn" bindtap="savePoster" disabled="{{loading || !posterUrl}}">
|
||||
<text>保存海报分享</text>
|
||||
</button>
|
||||
<view class="tip-text">保存到相册后可分享至朋友圈</view>
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.poster-preview {
|
||||
position: relative;
|
||||
width: 600rpx;
|
||||
min-height: 800rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 20rpx 50rpx rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.poster-img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-mask {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx; height: 60rpx;
|
||||
border: 4rpx solid #f3f4f6;
|
||||
border-top: 4rpx solid #f43f5e;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text { font-size: 24rpx; color: #64748b; }
|
||||
|
||||
.action-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
box-shadow: 0 -10rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 100% !important;
|
||||
height: 100rpx;
|
||||
background: #f43f5e;
|
||||
color: #fff;
|
||||
border-radius: 24rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 10rpx 20rpx rgba(244, 63, 94, 0.2);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.save-btn[disabled] {
|
||||
background: #cbd5e1;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tip-text { font-size: 24rpx; color: #94a3b8; margin-bottom: 20rpx; }
|
||||
.safe-bottom { height: env(safe-area-inset-bottom); }
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background-color: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.poster-preview {
|
||||
position: relative;
|
||||
width: 600rpx;
|
||||
min-height: 800rpx;
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx;
|
||||
box-shadow: 0 20rpx 50rpx rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.poster-img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading-mask {
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 60rpx; height: 60rpx;
|
||||
border: 4rpx solid #f3f4f6;
|
||||
border-top: 4rpx solid #f43f5e;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-text { font-size: 24rpx; color: #64748b; }
|
||||
|
||||
.action-bar {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
padding: 40rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-radius: 40rpx 40rpx 0 0;
|
||||
box-shadow: 0 -10rpx 30rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
width: 100% !important;
|
||||
height: 100rpx;
|
||||
background: #f43f5e;
|
||||
color: #fff;
|
||||
border-radius: 24rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
box-shadow: 0 10rpx 20rpx rgba(244, 63, 94, 0.2);
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.save-btn[disabled] {
|
||||
background: #cbd5e1;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tip-text { font-size: 24rpx; color: #94a3b8; margin-bottom: 20rpx; }
|
||||
.safe-bottom { height: env(safe-area-inset-bottom); }
|
||||
|
||||
@@ -1,236 +1,236 @@
|
||||
// pages/promo/withdrawals.js - 提现记录列表页
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
loading: false,
|
||||
finished: false,
|
||||
// 确认收款按钮 loading 状态(按单条)
|
||||
confirmingId: null
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadData(true)
|
||||
},
|
||||
|
||||
// 加载提现记录列表(reset 为 true 时重置分页)
|
||||
loadData(reset = false) {
|
||||
if (this.data.loading) return
|
||||
if (!reset && this.data.finished) return
|
||||
|
||||
const page = reset ? 1 : this.data.page
|
||||
this.setData({ loading: true })
|
||||
|
||||
request({
|
||||
url: `/api/distribution/withdrawals?page=${page}&pageSize=${this.data.pageSize}`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const { list = [], total = 0 } = payload.data
|
||||
const mapped = list.map(item => {
|
||||
const statusCode = this._normalizeStatusCode(item.status)
|
||||
const amountYuan = item.amountYuan != null
|
||||
? item.amountYuan
|
||||
: (item.amountFen != null ? (item.amountFen / 100).toFixed(2) : '')
|
||||
|
||||
const balanceAfterYuan = item.balanceAfterYuan != null
|
||||
? item.balanceAfterYuan
|
||||
: (item.balanceAfterFen != null ? (item.balanceAfterFen / 100).toFixed(2) : '')
|
||||
|
||||
const feeYuan = item.feeYuan != null
|
||||
? item.feeYuan
|
||||
: (item.feeFen != null ? (item.feeFen / 100).toFixed(2) : '0.00')
|
||||
|
||||
return {
|
||||
...item,
|
||||
statusCode,
|
||||
amountYuan,
|
||||
balanceAfterYuan,
|
||||
feeYuan,
|
||||
// 申请时间:用 createdAt
|
||||
applyAtStr: this._fmtTime(item.createdAt),
|
||||
// 处理时间:用 transferAt(如有)
|
||||
handleAtStr: this._fmtTime(item.transferAt),
|
||||
statusTagText: this._statusTagText(statusCode),
|
||||
statusTagClass: this._statusTagClass(statusCode)
|
||||
}
|
||||
})
|
||||
const newList = reset ? mapped : [...this.data.list, ...mapped]
|
||||
this.setData({
|
||||
list: newList,
|
||||
total,
|
||||
page: page + 1,
|
||||
finished: newList.length >= total
|
||||
})
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 时间戳格式化
|
||||
_fmtTime(ts) {
|
||||
if (!ts) return ''
|
||||
const d = new Date(ts * 1000)
|
||||
if (isNaN(d.getTime())) return ''
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hh = String(d.getHours()).padStart(2, '0')
|
||||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||||
return `${y}-${m}-${day} ${hh}:${mm}`
|
||||
},
|
||||
|
||||
// 将后端 status(数字 / 中文 / 英文)统一转成数字枚举:
|
||||
// 0=审核中,1=已驳回,2=待收款,3=已收款,4=已过期
|
||||
_normalizeStatusCode(status) {
|
||||
if (typeof status === 'number') return status
|
||||
if (typeof status === 'string') {
|
||||
const s = status.trim()
|
||||
if (/^\d+$/.test(s)) return parseInt(s, 10)
|
||||
switch (s) {
|
||||
case 'pending':
|
||||
case '审核中':
|
||||
return 0
|
||||
case 'rejected':
|
||||
case '已驳回':
|
||||
return 1
|
||||
case '待收款':
|
||||
return 2
|
||||
case 'transferred':
|
||||
case '已收款':
|
||||
return 3
|
||||
case '已过期':
|
||||
return 4
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
},
|
||||
|
||||
// 状态中文文案
|
||||
_statusTagText(code) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return '待审核'
|
||||
case 1:
|
||||
return '已驳回'
|
||||
case 2:
|
||||
return '待收款'
|
||||
case 3:
|
||||
return '已收款'
|
||||
case 4:
|
||||
return '已过期'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
// 顶部右侧小标签样式
|
||||
_statusTagClass(code) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return 'tag-pending'
|
||||
case 1:
|
||||
return 'tag-rejected'
|
||||
case 2:
|
||||
return 'tag-wait'
|
||||
case 3:
|
||||
return 'tag-transferred'
|
||||
case 4:
|
||||
return 'tag-expired'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
// 用户点击单条「确认收款」
|
||||
handleConfirmReceipt(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (!id || this.data.confirmingId === id) return
|
||||
|
||||
const item = (this.data.list || []).find(x => String(x.id) === String(id))
|
||||
if (!item) {
|
||||
wx.showToast({ title: '记录不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 兼容驼峰 / 下划线字段
|
||||
const packageInfo = item.packageInfo || item.package_info
|
||||
if (!packageInfo) {
|
||||
wx.showToast({ title: '该提现单无法确认收款,请联系客服', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!wx.canIUse || !wx.canIUse('requestMerchantTransfer')) {
|
||||
wx.showModal({
|
||||
content: '当前微信版本过低,无法使用确认收款功能,请升级微信后重试',
|
||||
showCancel: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ confirmingId: id })
|
||||
|
||||
wx.showLoading({ title: '正在调起确认收款...' })
|
||||
|
||||
// 获取 AppID:优先记录里的,其次运行时获取
|
||||
let appId = item.appId || item.app_id
|
||||
try {
|
||||
if (!appId && wx.getAccountInfoSync) {
|
||||
const info = wx.getAccountInfoSync()
|
||||
appId = info && info.miniProgram && info.miniProgram.appId
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
// 商户号:从记录中取(后端已写入 mch_id)
|
||||
const mchId = item.mchId || item.mch_id
|
||||
|
||||
wx.requestMerchantTransfer({
|
||||
mchId,
|
||||
appId,
|
||||
// 文档里字段名是 package,对应后台返回的 package_info
|
||||
package: packageInfo,
|
||||
success: (res) => {
|
||||
console.log('requestMerchantTransfer success', res)
|
||||
wx.showToast({ title: '已调起收款确认', icon: 'none', duration: 2000 })
|
||||
// 主动查询转账状态并更新订单,实现及时刷新(参考商户单号查询:https://pay.weixin.qq.com/doc/v3/merchant/4012716437)
|
||||
request({
|
||||
url: '/api/distribution/withdrawals/query-transfer',
|
||||
method: 'POST',
|
||||
data: { id: item.id },
|
||||
success: (r) => {
|
||||
const p = r && r.data
|
||||
if (p && p.code === 200 && p.data && p.data.status === 3) {
|
||||
wx.showToast({ title: '已收款', icon: 'success' })
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.loadData(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('requestMerchantTransfer fail', err)
|
||||
wx.showToast({ title: '调起失败,请稍后重试', icon: 'none' })
|
||||
},
|
||||
complete: () => {
|
||||
wx.hideLoading()
|
||||
this.setData({ confirmingId: null })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.loadData(false)
|
||||
}
|
||||
})
|
||||
|
||||
// pages/promo/withdrawals.js - 提现记录列表页
|
||||
const { request } = require('../../utils/request')
|
||||
|
||||
Page({
|
||||
data: {
|
||||
list: [],
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
loading: false,
|
||||
finished: false,
|
||||
// 确认收款按钮 loading 状态(按单条)
|
||||
confirmingId: null
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
this.loadData(true)
|
||||
},
|
||||
|
||||
// 加载提现记录列表(reset 为 true 时重置分页)
|
||||
loadData(reset = false) {
|
||||
if (this.data.loading) return
|
||||
if (!reset && this.data.finished) return
|
||||
|
||||
const page = reset ? 1 : this.data.page
|
||||
this.setData({ loading: true })
|
||||
|
||||
request({
|
||||
url: `/api/distribution/withdrawals?page=${page}&pageSize=${this.data.pageSize}`,
|
||||
method: 'GET',
|
||||
success: (res) => {
|
||||
const payload = res && res.data
|
||||
if (payload && payload.code === 200 && payload.data) {
|
||||
const { list = [], total = 0 } = payload.data
|
||||
const mapped = list.map(item => {
|
||||
const statusCode = this._normalizeStatusCode(item.status)
|
||||
const amountYuan = item.amountYuan != null
|
||||
? item.amountYuan
|
||||
: (item.amountFen != null ? (item.amountFen / 100).toFixed(2) : '')
|
||||
|
||||
const balanceAfterYuan = item.balanceAfterYuan != null
|
||||
? item.balanceAfterYuan
|
||||
: (item.balanceAfterFen != null ? (item.balanceAfterFen / 100).toFixed(2) : '')
|
||||
|
||||
const feeYuan = item.feeYuan != null
|
||||
? item.feeYuan
|
||||
: (item.feeFen != null ? (item.feeFen / 100).toFixed(2) : '0.00')
|
||||
|
||||
return {
|
||||
...item,
|
||||
statusCode,
|
||||
amountYuan,
|
||||
balanceAfterYuan,
|
||||
feeYuan,
|
||||
// 申请时间:用 createdAt
|
||||
applyAtStr: this._fmtTime(item.createdAt),
|
||||
// 处理时间:用 transferAt(如有)
|
||||
handleAtStr: this._fmtTime(item.transferAt),
|
||||
statusTagText: this._statusTagText(statusCode),
|
||||
statusTagClass: this._statusTagClass(statusCode)
|
||||
}
|
||||
})
|
||||
const newList = reset ? mapped : [...this.data.list, ...mapped]
|
||||
this.setData({
|
||||
list: newList,
|
||||
total,
|
||||
page: page + 1,
|
||||
finished: newList.length >= total
|
||||
})
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.setData({ loading: false })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 时间戳格式化
|
||||
_fmtTime(ts) {
|
||||
if (!ts) return ''
|
||||
const d = new Date(ts * 1000)
|
||||
if (isNaN(d.getTime())) return ''
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hh = String(d.getHours()).padStart(2, '0')
|
||||
const mm = String(d.getMinutes()).padStart(2, '0')
|
||||
return `${y}-${m}-${day} ${hh}:${mm}`
|
||||
},
|
||||
|
||||
// 将后端 status(数字 / 中文 / 英文)统一转成数字枚举:
|
||||
// 0=审核中,1=已驳回,2=待收款,3=已收款,4=已过期
|
||||
_normalizeStatusCode(status) {
|
||||
if (typeof status === 'number') return status
|
||||
if (typeof status === 'string') {
|
||||
const s = status.trim()
|
||||
if (/^\d+$/.test(s)) return parseInt(s, 10)
|
||||
switch (s) {
|
||||
case 'pending':
|
||||
case '审核中':
|
||||
return 0
|
||||
case 'rejected':
|
||||
case '已驳回':
|
||||
return 1
|
||||
case '待收款':
|
||||
return 2
|
||||
case 'transferred':
|
||||
case '已收款':
|
||||
return 3
|
||||
case '已过期':
|
||||
return 4
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
},
|
||||
|
||||
// 状态中文文案
|
||||
_statusTagText(code) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return '待审核'
|
||||
case 1:
|
||||
return '已驳回'
|
||||
case 2:
|
||||
return '待收款'
|
||||
case 3:
|
||||
return '已收款'
|
||||
case 4:
|
||||
return '已过期'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
// 顶部右侧小标签样式
|
||||
_statusTagClass(code) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return 'tag-pending'
|
||||
case 1:
|
||||
return 'tag-rejected'
|
||||
case 2:
|
||||
return 'tag-wait'
|
||||
case 3:
|
||||
return 'tag-transferred'
|
||||
case 4:
|
||||
return 'tag-expired'
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
// 用户点击单条「确认收款」
|
||||
handleConfirmReceipt(e) {
|
||||
const id = e.currentTarget.dataset.id
|
||||
if (!id || this.data.confirmingId === id) return
|
||||
|
||||
const item = (this.data.list || []).find(x => String(x.id) === String(id))
|
||||
if (!item) {
|
||||
wx.showToast({ title: '记录不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 兼容驼峰 / 下划线字段
|
||||
const packageInfo = item.packageInfo || item.package_info
|
||||
if (!packageInfo) {
|
||||
wx.showToast({ title: '该提现单无法确认收款,请联系客服', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!wx.canIUse || !wx.canIUse('requestMerchantTransfer')) {
|
||||
wx.showModal({
|
||||
content: '当前微信版本过低,无法使用确认收款功能,请升级微信后重试',
|
||||
showCancel: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ confirmingId: id })
|
||||
|
||||
wx.showLoading({ title: '正在调起确认收款...' })
|
||||
|
||||
// 获取 AppID:优先记录里的,其次运行时获取
|
||||
let appId = item.appId || item.app_id
|
||||
try {
|
||||
if (!appId && wx.getAccountInfoSync) {
|
||||
const info = wx.getAccountInfoSync()
|
||||
appId = info && info.miniProgram && info.miniProgram.appId
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
// 商户号:从记录中取(后端已写入 mch_id)
|
||||
const mchId = item.mchId || item.mch_id
|
||||
|
||||
wx.requestMerchantTransfer({
|
||||
mchId,
|
||||
appId,
|
||||
// 文档里字段名是 package,对应后台返回的 package_info
|
||||
package: packageInfo,
|
||||
success: (res) => {
|
||||
console.log('requestMerchantTransfer success', res)
|
||||
wx.showToast({ title: '已调起收款确认', icon: 'none', duration: 2000 })
|
||||
// 主动查询转账状态并更新订单,实现及时刷新(参考商户单号查询:https://pay.weixin.qq.com/doc/v3/merchant/4012716437)
|
||||
request({
|
||||
url: '/api/distribution/withdrawals/query-transfer',
|
||||
method: 'POST',
|
||||
data: { id: item.id },
|
||||
success: (r) => {
|
||||
const p = r && r.data
|
||||
if (p && p.code === 200 && p.data && p.data.status === 3) {
|
||||
wx.showToast({ title: '已收款', icon: 'success' })
|
||||
}
|
||||
},
|
||||
complete: () => {
|
||||
this.loadData(true)
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('requestMerchantTransfer fail', err)
|
||||
wx.showToast({ title: '调起失败,请稍后重试', icon: 'none' })
|
||||
},
|
||||
complete: () => {
|
||||
wx.hideLoading()
|
||||
this.setData({ confirmingId: null })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onReachBottom() {
|
||||
this.loadData(false)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"navigationBarTitleText": "提现记录",
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
||||
{
|
||||
"navigationBarTitleText": "提现记录",
|
||||
"usingComponents": {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
<!-- pages/promo/withdrawals.wxml - 提现记录列表 -->
|
||||
<view class="page">
|
||||
<!-- 顶部说明 -->
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty" wx:if="{{!loading && list.length === 0}}">
|
||||
<view class="empty-icon">
|
||||
<text>📭</text>
|
||||
</view>
|
||||
<text class="empty-text">暂无提现记录</text>
|
||||
<text class="empty-sub">成功发起提现后,将在这里展示进度</text>
|
||||
</view>
|
||||
|
||||
<!-- 列表(参考碎片时间提现列表样式结构) -->
|
||||
<view class="list" wx:else>
|
||||
<view class="item" wx:for="{{list}}" wx:key="id">
|
||||
<!-- 顶部:单号 + 状态标签 -->
|
||||
<view class="item-header">
|
||||
<text class="order-no">单号:{{item.out_bill_no}}</text>
|
||||
<view class="status-tag {{item.statusTagClass}}">
|
||||
<text>{{item.statusTagText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 中部:金额 + 手续费 + 提现后余额 -->
|
||||
<view class="item-top">
|
||||
<view class="amount-block">
|
||||
<text class="amount">¥{{item.amountYuan}}</text>
|
||||
</view>
|
||||
<view class="fee-block" wx:if="{{item.feeYuan && item.feeYuan !== '0.00'}}">
|
||||
<text class="fee-label">手续费</text>
|
||||
<text class="fee-value">¥{{item.feeYuan}}</text>
|
||||
</view>
|
||||
<view class="balance-block" wx:if="{{item.balanceAfterYuan}}">
|
||||
<text class="balance-label">提现后余额</text>
|
||||
<text class="balance-value">¥{{item.balanceAfterYuan}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<view class="time-block">
|
||||
<text class="time-row">申请时间:{{item.applyAtStr}}</text>
|
||||
<text class="time-row" wx:if="{{item.handleAtStr}}">处理时间:{{item.handleAtStr}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 拒绝原因 -->
|
||||
<view class="reject-box" wx:if="{{item.status === 'rejected' && item.auditNote}}">
|
||||
<text class="reject-title">拒绝原因:</text>
|
||||
<text class="reject-text">{{item.auditNote}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 每条记录自己的确认收款按钮:只有「待收款」(statusCode=2) 需要用户确认 -->
|
||||
<view class="confirm-wrapper" wx:if="{{item.statusCode === 2}}">
|
||||
<button
|
||||
class="confirm-btn"
|
||||
bindtap="handleConfirmReceipt"
|
||||
data-id="{{item.id}}"
|
||||
loading="{{confirmingId === item.id}}"
|
||||
disabled="{{confirmingId === item.id}}"
|
||||
>
|
||||
确认收款
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer" wx:if="{{loading}}">
|
||||
<text class="footer-text">加载中...</text>
|
||||
</view>
|
||||
<view class="footer" wx:elif="{{finished}}">
|
||||
<text class="footer-text">— 已加载全部 —</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
|
||||
<!-- pages/promo/withdrawals.wxml - 提现记录列表 -->
|
||||
<view class="page">
|
||||
<!-- 顶部说明 -->
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view class="empty" wx:if="{{!loading && list.length === 0}}">
|
||||
<view class="empty-icon">
|
||||
<text>📭</text>
|
||||
</view>
|
||||
<text class="empty-text">暂无提现记录</text>
|
||||
<text class="empty-sub">成功发起提现后,将在这里展示进度</text>
|
||||
</view>
|
||||
|
||||
<!-- 列表(参考碎片时间提现列表样式结构) -->
|
||||
<view class="list" wx:else>
|
||||
<view class="item" wx:for="{{list}}" wx:key="id">
|
||||
<!-- 顶部:单号 + 状态标签 -->
|
||||
<view class="item-header">
|
||||
<text class="order-no">单号:{{item.out_bill_no}}</text>
|
||||
<view class="status-tag {{item.statusTagClass}}">
|
||||
<text>{{item.statusTagText}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 中部:金额 + 手续费 + 提现后余额 -->
|
||||
<view class="item-top">
|
||||
<view class="amount-block">
|
||||
<text class="amount">¥{{item.amountYuan}}</text>
|
||||
</view>
|
||||
<view class="fee-block" wx:if="{{item.feeYuan && item.feeYuan !== '0.00'}}">
|
||||
<text class="fee-label">手续费</text>
|
||||
<text class="fee-value">¥{{item.feeYuan}}</text>
|
||||
</view>
|
||||
<view class="balance-block" wx:if="{{item.balanceAfterYuan}}">
|
||||
<text class="balance-label">提现后余额</text>
|
||||
<text class="balance-value">¥{{item.balanceAfterYuan}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 时间信息 -->
|
||||
<view class="time-block">
|
||||
<text class="time-row">申请时间:{{item.applyAtStr}}</text>
|
||||
<text class="time-row" wx:if="{{item.handleAtStr}}">处理时间:{{item.handleAtStr}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 拒绝原因 -->
|
||||
<view class="reject-box" wx:if="{{item.status === 'rejected' && item.auditNote}}">
|
||||
<text class="reject-title">拒绝原因:</text>
|
||||
<text class="reject-text">{{item.auditNote}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 每条记录自己的确认收款按钮:只有「待收款」(statusCode=2) 需要用户确认 -->
|
||||
<view class="confirm-wrapper" wx:if="{{item.statusCode === 2}}">
|
||||
<button
|
||||
class="confirm-btn"
|
||||
bindtap="handleConfirmReceipt"
|
||||
data-id="{{item.id}}"
|
||||
loading="{{confirmingId === item.id}}"
|
||||
disabled="{{confirmingId === item.id}}"
|
||||
>
|
||||
确认收款
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer" wx:if="{{loading}}">
|
||||
<text class="footer-text">加载中...</text>
|
||||
</view>
|
||||
<view class="footer" wx:elif="{{finished}}">
|
||||
<text class="footer-text">— 已加载全部 —</text>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
<view class="safe-bottom"></view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -1,244 +1,244 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f9fafb;
|
||||
padding: 24rpx 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 80rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
background: #f3f4f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 56rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 4rpx;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.empty-sub {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.order-no {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.tag-pending {
|
||||
background: #e5e7eb;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.tag-approved {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.tag-wait {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.tag-rejected {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.tag-transferred {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.tag-expired {
|
||||
background: #f3f4f6;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.item-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.amount-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.fee-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
.fee-label {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.fee-value {
|
||||
font-size: 26rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.balance-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.balance-value {
|
||||
margin-top: 4rpx;
|
||||
font-size: 26rpx;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.time-block {
|
||||
margin-top: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.time-row {
|
||||
font-size: 22rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.reject-box {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #fef2f2;
|
||||
}
|
||||
|
||||
.reject-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.reject-text {
|
||||
font-size: 24rpx;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 16rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.confirm-wrapper {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 8rpx 4rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #FF6B8A 0%, #FF8E53 100%);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10rpx 18rpx rgba(255, 107, 138, 0.35);
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.confirm-btn[disabled] {
|
||||
opacity: 0.7;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.safe-bottom {
|
||||
height: calc(32rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: #f9fafb;
|
||||
padding: 24rpx 32rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
margin-top: 6rpx;
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.empty {
|
||||
margin-top: 80rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
border-radius: 60rpx;
|
||||
background: #f3f4f6;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 56rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
margin-bottom: 4rpx;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.empty-sub {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 24rpx 28rpx;
|
||||
margin-bottom: 20rpx;
|
||||
box-shadow: 0 4rpx 16rpx rgba(15, 23, 42, 0.04);
|
||||
}
|
||||
|
||||
.item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.order-no {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 6rpx 16rpx;
|
||||
border-radius: 999rpx;
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.tag-pending {
|
||||
background: #e5e7eb;
|
||||
color: #4b5563;
|
||||
}
|
||||
|
||||
.tag-approved {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.tag-wait {
|
||||
background: #dbeafe;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.tag-rejected {
|
||||
background: #fee2e2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.tag-transferred {
|
||||
background: #dcfce7;
|
||||
color: #15803d;
|
||||
}
|
||||
|
||||
.tag-expired {
|
||||
background: #f3f4f6;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.item-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.amount-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.fee-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rpx;
|
||||
}
|
||||
|
||||
.fee-label {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.fee-value {
|
||||
font-size: 26rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.balance-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.balance-value {
|
||||
margin-top: 4rpx;
|
||||
font-size: 26rpx;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.time-block {
|
||||
margin-top: 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
}
|
||||
|
||||
.time-row {
|
||||
font-size: 22rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.reject-box {
|
||||
margin-top: 16rpx;
|
||||
padding: 16rpx;
|
||||
border-radius: 12rpx;
|
||||
background: #fef2f2;
|
||||
}
|
||||
|
||||
.reject-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.reject-text {
|
||||
font-size: 24rpx;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 16rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.confirm-wrapper {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 8rpx 4rpx;
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(135deg, #FF6B8A 0%, #FF8E53 100%);
|
||||
color: #ffffff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 10rpx 18rpx rgba(255, 107, 138, 0.35);
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.confirm-btn[disabled] {
|
||||
opacity: 0.7;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.safe-bottom {
|
||||
height: calc(32rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user