Files
soul-yongping/miniprogram/pages/referral/referral.js

849 lines
30 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Soul创业派对 - 分销中心页
*
* 可见数据:
* - 绑定用户数(当前有效绑定)
* - 通过链接进的人数(总访问量)
* - 带来的付款人数(已转化购买)
* - 收益统计90%归分发者)
*/
const app = getApp()
Page({
data: {
statusBarHeight: 44,
isLoggedIn: false,
userInfo: null,
// === 核心可见数据 ===
bindingCount: 0, // 绑定用户数(当前有效)
visitCount: 0, // 通过链接进的人数
paidCount: 0, // 带来的付款人数
unboughtCount: 0, // 待购买人数(绑定但未付款)
expiredCount: 0, // 已过期人数
// === 收益数据 ===
totalCommission: 0, // 累计佣金总额(所有获得的佣金)
availableEarnings: 0, // 可提现金额(未申请提现的佣金)- 字符串格式用于显示
availableEarningsNum: 0, // 可提现金额 - 数字格式用于判断
pendingWithdrawAmount: 0, // 待审核金额(已申请提现但未审核)
withdrawnEarnings: 0, // 已提现金额
earnings: 0, // 已结算收益(保留兼容)
pendingEarnings: 0, // 待结算收益(保留兼容)
shareRate: 90, // 分成比例90%),从 referral/data 或 config 获取
minWithdrawAmount: 10, // 最低提现金额,从 referral/data 获取
bindingDays: 30, // 绑定期天数,从 referral/data 获取
userDiscount: 5, // 好友购买优惠%,从 referral/data 获取
hasWechatId: false, // 是否已绑定微信号(未绑定时需引导去设置)
// === 统计数据 ===
referralCount: 0, // 总推荐人数
expiringCount: 0, // 即将过期人数
// 邀请码
referralCode: '',
// 绑定用户列表
showBindingList: true,
activeTab: 'active',
activeBindings: [],
convertedBindings: [],
expiredBindings: [],
currentBindings: [],
totalBindings: 0,
// 收益明细
earningsDetails: [],
// 海报
showPosterModal: false,
isGeneratingPoster: false,
posterQrSrc: '',
posterQrFilePath: '',
posterReferralLink: '',
posterNickname: '',
posterNicknameInitial: '',
posterCaseCount: 62,
},
onLoad() {
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.initData()
},
onShow() {
// 从设置页返回时同步微信号绑定状态,便于提现按钮立即更新
const hasWechatId = !!(app.globalData.userInfo?.wechat || app.globalData.userInfo?.wechatId || wx.getStorageSync('user_wechat'))
this.setData({ hasWechatId })
this.initData()
},
// 初始化数据
async initData() {
const { isLoggedIn, userInfo } = app.globalData
if (isLoggedIn && userInfo) {
// 显示加载提示
wx.showLoading({
title: '加载中...',
mask: true // 防止触摸穿透
})
// 生成邀请码
const referralCode = userInfo.referralCode || 'SOUL' + (userInfo.id || Date.now().toString(36)).toUpperCase().slice(-6)
console.log('[Referral] 开始加载分销数据userId:', userInfo.id)
// 从API获取真实数据
let realData = null
try {
// app.request 第一个参数是 URL 字符串(会自动拼接 baseUrl
const res = await app.request('/api/miniprogram/referral/data?userId=' + userInfo.id)
console.log('[Referral] API返回:', JSON.stringify(res).substring(0, 200))
if (res && res.success && res.data) {
realData = res.data
console.log('[Referral] ✅ 获取推广数据成功')
console.log('[Referral] - bindingCount:', realData.bindingCount)
console.log('[Referral] - paidCount:', realData.paidCount)
console.log('[Referral] - earnings:', realData.earnings)
console.log('[Referral] - expiringCount:', realData.stats?.expiringCount)
} else {
console.log('[Referral] ❌ API返回格式错误:', res?.error || 'unknown')
}
} catch (e) {
console.log('[Referral] ❌ API调用失败:', e.message || e)
console.log('[Referral] 错误详情:', e)
}
// 使用真实数据或默认值
let activeBindings = realData?.activeUsers || []
let convertedBindings = realData?.convertedUsers || []
let expiredBindings = realData?.expiredUsers || []
console.log('[Referral] activeBindings:', activeBindings.length)
console.log('[Referral] convertedBindings:', convertedBindings.length)
console.log('[Referral] expiredBindings:', expiredBindings.length)
// 计算即将过期的数量7天内
const expiringCount = realData?.stats?.expiringCount || activeBindings.filter(b => b.daysRemaining <= 7 && b.daysRemaining > 0).length
console.log('[Referral] expiringCount:', expiringCount)
// 计算各类统计
const bindingCount = realData?.bindingCount || activeBindings.length
const paidCount = realData?.paidCount || convertedBindings.length
const expiredCount = realData?.expiredCount || expiredBindings.length
const unboughtCount = bindingCount - paidCount // 绑定中但未付款的
// 格式化用户数据
const formatUser = (user, type) => {
const formatted = {
id: user.id,
nickname: user.nickname || '用户' + (user.id || '').slice(-4),
avatar: user.avatar,
status: type,
daysRemaining: user.daysRemaining || 0,
bindingDate: user.bindingDate ? this.formatDate(user.bindingDate) : '--',
expiryDate: user.expiryDate ? this.formatDate(user.expiryDate) : '--',
commission: (user.commission || 0).toFixed(2),
orderAmount: (user.orderAmount || 0).toFixed(2),
purchaseCount: user.purchaseCount || 0,
conversionDate: user.conversionDate ? this.formatDate(user.conversionDate) : '--'
}
console.log('[Referral] 格式化用户:', formatted.nickname, formatted.status, formatted.daysRemaining + '天')
return formatted
}
// 格式化金额(保留两位小数)
const formatMoney = (num) => {
return typeof num === 'number' ? num.toFixed(2) : '0.00'
}
// ✅ 可提现金额 = 累计佣金 - 已提现金额 - 待审核金额,且不低于 0防止数据不同步时出现负数
const totalCommissionNum = realData?.totalCommission || 0
const withdrawnNum = realData?.withdrawnEarnings || 0
const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
const availableEarningsNum = Math.max(0, totalCommissionNum - withdrawnNum - pendingWithdrawNum)
const minWithdrawAmount = realData?.minWithdrawAmount || 10
console.log('=== [Referral] 收益计算(完整版)===')
console.log('累计佣金 (totalCommission):', totalCommissionNum)
console.log('已提现金额 (withdrawnEarnings):', withdrawnNum)
console.log('待审核金额 (pendingWithdrawAmount):', pendingWithdrawNum)
console.log('可提现金额 = 累计 - 已提现 - 待审核 =', totalCommissionNum, '-', withdrawnNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
console.log('最低提现金额 (minWithdrawAmount):', minWithdrawAmount)
console.log('按钮判断:', availableEarningsNum, '>=', minWithdrawAmount, '=', availableEarningsNum >= minWithdrawAmount)
console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用(绿色)' : '⚫ 禁用(灰色)')
const hasWechatId = !!(userInfo?.wechat || userInfo?.wechatId || wx.getStorageSync('user_wechat'))
this.setData({
isLoggedIn: true,
userInfo,
hasWechatId,
// 核心可见数据
bindingCount,
visitCount: realData?.visitCount || 0,
paidCount,
unboughtCount: expiringCount, // "即将过期"显示的是 expiringCount
expiredCount,
// 收益数据 - 格式化为两位小数
totalCommission: formatMoney(totalCommissionNum),
availableEarnings: formatMoney(availableEarningsNum), // ✅ 使用计算后的可提现金额
availableEarningsNum: availableEarningsNum, // ✅ 数字格式用于按钮判断
pendingWithdrawAmount: formatMoney(pendingWithdrawNum),
withdrawnEarnings: formatMoney(realData?.withdrawnEarnings || 0),
earnings: formatMoney(realData?.earnings || 0),
pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
shareRate: realData?.shareRate ?? 90,
minWithdrawAmount: minWithdrawAmount,
bindingDays: realData?.bindingDays ?? 30,
userDiscount: realData?.userDiscount ?? 5,
// 统计
referralCount: realData?.referralCount || realData?.stats?.totalBindings || activeBindings.length + convertedBindings.length,
expiringCount,
referralCode,
activeBindings: activeBindings.map(u => formatUser(u, 'active')),
convertedBindings: convertedBindings.map(u => formatUser(u, 'converted')),
expiredBindings: expiredBindings.map(u => formatUser(u, 'expired')),
currentBindings: activeBindings.map(u => formatUser(u, 'active')),
totalBindings: activeBindings.length + convertedBindings.length + expiredBindings.length,
// 收益明细
earningsDetails: (realData?.earningsDetails || []).map(item => {
// 解析商品描述,获取书名和章节
const productInfo = this.parseProductDescription(item.description, item.productType)
return {
id: item.id,
productType: item.productType,
bookTitle: productInfo.bookTitle,
chapterTitle: productInfo.chapterTitle,
commission: (item.commission || 0).toFixed(2),
payTime: item.payTime ? this.formatDate(item.payTime) : '--',
buyerNickname: item.buyerNickname || '用户',
buyerAvatar: item.buyerAvatar
}
})
})
console.log('[Referral] ✅ 数据设置完成')
console.log('[Referral] - 绑定中:', this.data.bindingCount)
console.log('[Referral] - 即将过期:', this.data.expiringCount)
console.log('[Referral] - 收益:', this.data.earnings)
console.log('=== [Referral] 按钮状态验证 ===')
console.log('累计佣金 (totalCommission):', this.data.totalCommission)
console.log('待审核金额 (pendingWithdrawAmount):', this.data.pendingWithdrawAmount)
console.log('可提现金额 (availableEarnings 显示):', this.data.availableEarnings)
console.log('可提现金额 (availableEarningsNum 判断):', this.data.availableEarningsNum, typeof this.data.availableEarningsNum)
console.log('最低提现金额 (minWithdrawAmount):', this.data.minWithdrawAmount, typeof this.data.minWithdrawAmount)
console.log('按钮启用条件:', this.data.availableEarningsNum, '>=', this.data.minWithdrawAmount, '=', this.data.availableEarningsNum >= this.data.minWithdrawAmount)
console.log('✅ 最终结果: 按钮应该', this.data.availableEarningsNum >= this.data.minWithdrawAmount ? '🟢 启用' : '⚫ 禁用')
// 隐藏加载提示
wx.hideLoading()
} else {
// 未登录时也隐藏loading
this.setData({ isLoading: false })
}
},
// 切换Tab
switchTab(e) {
const tab = e.currentTarget.dataset.tab
let currentBindings = []
if (tab === 'active') {
currentBindings = this.data.activeBindings
} else if (tab === 'converted') {
currentBindings = this.data.convertedBindings
} else {
currentBindings = this.data.expiredBindings
}
this.setData({ activeTab: tab, currentBindings })
},
// 切换绑定列表显示
toggleBindingList() {
this.setData({ showBindingList: !this.data.showBindingList })
},
// 复制邀请链接
copyLink() {
const link = `https://soul.quwanzhi.com/?ref=${this.data.referralCode}`
wx.setClipboardData({
data: link,
success: () => wx.showToast({ title: '链接已复制', icon: 'success' })
})
},
// 生成推广海报 - 1:1 对齐 Next.js 设计
async generatePoster() {
wx.showLoading({ title: '生成中...', mask: true })
this.setData({ showPosterModal: true, isGeneratingPoster: true })
try {
const { referralCode, userInfo } = this.data
const nickname = userInfo?.nickname || '用户'
const scene = `ref=${referralCode}`
console.log('[Poster] 请求小程序码, scene:', scene)
// 调用后端接口生成「小程序码」(官方 getwxacodeunlimit不再使用 H5 二维码
const res = await app.request('/api/miniprogram/qrcode', {
method: 'POST',
data: {
scene, // ref=XXXX
page: 'pages/index/index',
width: 280,
},
})
// 接口返回 { success, image: "data:image/png;base64,...", scene }
const imageData = res?.image || res?.data?.image
if (!res || !res.success || !imageData) {
console.error('[Poster] 生成小程序码失败:', res)
throw new Error(res?.error || res?.message || '生成小程序码失败')
}
// 小程序 image 组件支持 base64 格式,直接使用;同时写入本地供预览用
const base64Str = String(imageData).trim()
const fs = wx.getFileSystemManager()
const filePath = `${wx.env.USER_DATA_PATH}/poster_qrcode_${Date.now()}.png`
const base64Data = base64Str.replace(/^data:image\/\w+;base64,/, '')
await new Promise((resolve, reject) => {
fs.writeFile({
filePath,
data: base64Data,
encoding: 'base64',
success: () => resolve(true),
fail: (err) => {
console.error('[Poster] 小程序码写入本地失败:', err)
reject(err)
},
})
})
// 优先用 base64 直接显示(兼容性更好);预览时用本地路径
this.setData({
posterQrSrc: base64Str,
posterQrFilePath: filePath,
posterReferralLink: '', // 小程序版本不再使用 H5 链接
posterNickname: nickname,
posterNicknameInitial: (nickname || '用').charAt(0),
isGeneratingPoster: false
})
wx.hideLoading()
} catch (e) {
console.error('[Poster] 生成二维码失败:', e)
wx.hideLoading()
wx.showToast({ title: '生成失败', icon: 'none' })
this.setData({ showPosterModal: false, isGeneratingPoster: false, posterQrSrc: '', posterQrFilePath: '', posterReferralLink: '' })
}
},
// 绘制数据卡片
drawDataCard(ctx, x, y, width, height, value, label, color) {
// 卡片背景
ctx.setFillStyle('rgba(255,255,255,0.05)')
this.drawRoundRect(ctx, x, y, width, height, 8)
ctx.setStrokeStyle('rgba(255,255,255,0.1)')
ctx.setLineWidth(1)
ctx.stroke()
// 数值
ctx.setFillStyle(color)
ctx.setFontSize(24)
ctx.setTextAlign('center')
ctx.fillText(value, x + width / 2, y + 24)
// 标签
ctx.setFillStyle('rgba(255,255,255,0.5)')
ctx.setFontSize(10)
ctx.fillText(label, x + width / 2, y + 40)
},
// 绘制圆角矩形
drawRoundRect(ctx, x, y, width, height, radius) {
ctx.beginPath()
ctx.moveTo(x + radius, y)
ctx.lineTo(x + width - radius, y)
ctx.arc(x + width - radius, y + radius, radius, -Math.PI / 2, 0)
ctx.lineTo(x + width, y + height - radius)
ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2)
ctx.lineTo(x + radius, y + height)
ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI)
ctx.lineTo(x, y + radius)
ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
ctx.closePath()
ctx.fill()
},
// 光晕(替代 createRadialGradient用同心圆叠加模拟模糊
// centerX/centerY: 圆心坐标radius: 最大半径rgb: [r,g,b]maxAlpha: 最内层透明度
drawGlow(ctx, centerX, centerY, radius, rgb, maxAlpha = 0.10) {
const steps = 14
for (let i = steps; i >= 1; i--) {
const r = (radius * i) / steps
const alpha = (maxAlpha * i) / steps
ctx.setFillStyle(`rgba(${rgb[0]},${rgb[1]},${rgb[2]},${alpha})`)
ctx.beginPath()
ctx.arc(centerX, centerY, r, 0, Math.PI * 2)
ctx.fill()
}
},
// 绘制二维码支持Base64和URL两种格式
async drawQRCode(ctx, qrcodeImage, x, y, size) {
return new Promise((resolve) => {
if (!qrcodeImage) {
console.log('[Poster] 无二维码数据,绘制占位符')
this.drawQRPlaceholder(ctx, x, y, size)
resolve()
return
}
// 判断是Base64还是URL
if (qrcodeImage.startsWith('data:image') || !qrcodeImage.startsWith('http')) {
// Base64格式小程序码
console.log('[Poster] 绘制Base64二维码')
const fs = wx.getFileSystemManager()
const filePath = `${wx.env.USER_DATA_PATH}/qrcode_promo_${Date.now()}.png`
const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '')
fs.writeFile({
filePath,
data: base64Data,
encoding: 'base64',
success: () => {
console.log('[Poster] ✅ Base64写入成功')
ctx.drawImage(filePath, x, y, size, size)
resolve()
},
fail: (err) => {
console.error('[Poster] ❌ Base64写入失败:', err)
this.drawQRPlaceholder(ctx, x, y, size)
resolve()
}
})
} else {
// URL格式第三方二维码
console.log('[Poster] 下载在线二维码:', qrcodeImage)
wx.downloadFile({
url: qrcodeImage,
success: (res) => {
if (res.statusCode === 200) {
console.log('[Poster] ✅ 二维码下载成功')
ctx.drawImage(res.tempFilePath, x, y, size, size)
resolve()
} else {
console.error('[Poster] ❌ 二维码下载失败, status:', res.statusCode)
this.drawQRPlaceholder(ctx, x, y, size)
resolve()
}
},
fail: (err) => {
console.error('[Poster] ❌ 二维码下载失败:', err)
this.drawQRPlaceholder(ctx, x, y, size)
resolve()
}
})
}
})
},
// 绘制小程序码占位符
drawQRPlaceholder(ctx, x, y, size) {
// 绘制占位符方框
ctx.setFillStyle('rgba(200,200,200,0.3)')
this.drawRoundRect(ctx, x, y, size, size, 8)
ctx.setFillStyle('#00CED1')
ctx.setFontSize(11)
ctx.setTextAlign('center')
ctx.fillText('小程序码', x + size / 2, y + size / 2)
},
// 关闭海报弹窗
closePosterModal() {
this.setData({ showPosterModal: false })
},
// 保存海报
savePoster() {
const { posterQrSrc } = this.data
if (!posterQrSrc) {
wx.showToast({ title: '二维码未生成', icon: 'none' })
return
}
wx.showLoading({ title: '保存中...', mask: true })
wx.downloadFile({
url: posterQrSrc,
success: (res) => {
if (res.statusCode !== 200) {
wx.hideLoading()
wx.showToast({ title: '下载失败', icon: 'none' })
return
}
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.hideLoading()
wx.showToast({ title: '已保存到相册', icon: 'success' })
},
fail: (err) => {
wx.hideLoading()
if (String(err.errMsg || '').includes('auth deny')) {
wx.showModal({
title: '提示',
content: '需要相册权限才能保存二维码',
confirmText: '去设置',
success: (r) => {
if (r.confirm) wx.openSetting()
}
})
return
}
wx.showToast({ title: '保存失败', icon: 'none' })
}
})
},
fail: () => {
wx.hideLoading()
wx.showToast({ title: '下载失败', icon: 'none' })
}
})
},
// 预览二维码
previewPosterQr() {
const { posterQrSrc, posterQrFilePath } = this.data
const url = posterQrFilePath || posterQrSrc
if (!url) return
wx.previewImage({ urls: [url] })
},
// 阻止冒泡
stopPropagation() {},
// 提现 - 直接到微信零钱
async handleWithdraw() {
const availableEarnings = this.data.availableEarningsNum || 0
const minWithdrawAmount = this.data.minWithdrawAmount || 10
const hasWechatId = this.data.hasWechatId
if (availableEarnings <= 0) {
wx.showToast({ title: '暂无可提现收益', icon: 'none' })
return
}
if (availableEarnings < minWithdrawAmount) {
wx.showToast({ title: `${minWithdrawAmount}元可提现`, icon: 'none' })
return
}
// 未绑定微信号时引导去设置
if (!hasWechatId) {
wx.showModal({
title: '请先绑定微信号',
content: '提现需先绑定微信号,便于到账核对。请到「设置」中绑定后再提现。',
confirmText: '去绑定',
cancelText: '取消',
success: (res) => {
if (res.confirm) wx.navigateTo({ url: '/pages/settings/settings' })
}
})
return
}
wx.showModal({
title: '确认提现',
content: `将提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱`,
confirmText: '立即提现',
success: async (res) => {
if (!res.confirm) return
const tmplId = app.globalData.withdrawSubscribeTmplId
if (tmplId && tmplId.length > 10) {
wx.requestSubscribeMessage({
tmplIds: [tmplId],
success: () => { this.doWithdraw(availableEarnings) },
fail: () => { this.doWithdraw(availableEarnings) }
})
} else {
await this.doWithdraw(availableEarnings)
}
}
})
},
// 跳转提现记录页
goToWithdrawRecords() {
wx.navigateTo({ url: '/pages/withdraw-records/withdraw-records' })
},
// 执行提现
async doWithdraw(amount) {
wx.showLoading({ title: '提现中...' })
try {
const userId = app.globalData.userInfo?.id
if (!userId) {
wx.hideLoading()
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
const res = await app.request('/api/miniprogram/withdraw', {
method: 'POST',
data: { userId, amount }
})
wx.hideLoading()
if (res.success) {
wx.showModal({
title: '提现申请已提交 ✅',
content: res.message || '正在审核中,通过后会自动到账您的微信零钱',
showCancel: false,
confirmText: '知道了'
})
// 刷新数据(此时待审核金额会增加,可提现金额会减少)
this.initData()
} else {
if (res.needBind || res.needBindWechat) {
wx.showModal({
title: res.needBindWechat ? '请先绑定微信号' : '需要绑定微信',
content: res.needBindWechat ? '请到「设置」中绑定微信号后再提现,便于到账核对。' : '请先在设置中绑定微信账号后再提现',
confirmText: '去绑定',
success: (modalRes) => {
if (modalRes.confirm) wx.navigateTo({ url: '/pages/settings/settings' })
}
})
} else {
wx.showToast({ title: res.message || res.error || '提现失败', icon: 'none', duration: 3000 })
}
}
} catch (e) {
wx.hideLoading()
console.error('[Referral] 提现失败:', e)
wx.showToast({ title: '提现失败,请重试', icon: 'none' })
}
},
// 显示通知
showNotification() {
wx.showToast({ title: '暂无新消息', icon: 'none' })
},
// 显示设置
showSettings() {
wx.showActionSheet({
itemList: ['自动提现设置', '收益通知设置'],
success: (res) => {
if (res.tapIndex === 0) {
this.showAutoWithdrawSettings()
} else {
this.showNotificationSettings()
}
}
})
},
// 自动提现设置
async showAutoWithdrawSettings() {
const app = getApp()
const { userInfo } = app.globalData
if (!userInfo) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
// 获取当前设置
let autoWithdrawEnabled = wx.getStorageSync(`autoWithdraw_${userInfo.id}`) || false
let autoWithdrawThreshold = wx.getStorageSync(`autoWithdrawThreshold_${userInfo.id}`) || this.data.minWithdrawAmount || 10
wx.showModal({
title: '自动提现设置',
content: `当前状态:${autoWithdrawEnabled ? '已开启' : '已关闭'}\n自动提现阈值:¥${autoWithdrawThreshold}\n\n开启后,当可提现金额达到阈值时将自动发起提现申请。`,
confirmText: autoWithdrawEnabled ? '关闭' : '开启',
cancelText: '修改阈值',
success: (res) => {
if (res.confirm) {
// 切换开关
this.toggleAutoWithdraw(!autoWithdrawEnabled, autoWithdrawThreshold)
} else if (res.cancel) {
// 修改阈值
this.setAutoWithdrawThreshold(autoWithdrawEnabled, autoWithdrawThreshold)
}
}
})
},
// 切换自动提现开关
toggleAutoWithdraw(enabled, threshold) {
const app = getApp()
const { userInfo } = app.globalData
wx.setStorageSync(`autoWithdraw_${userInfo.id}`, enabled)
wx.showToast({
title: enabled ? '自动提现已开启' : '自动提现已关闭',
icon: 'success'
})
// 如果开启,检查当前金额是否达到阈值
if (enabled && this.data.availableEarningsNum >= threshold) {
wx.showModal({
title: '提示',
content: `当前可提现金额¥${this.data.availableEarnings}已达到阈值¥${threshold},是否立即提现?`,
success: (res) => {
if (res.confirm) {
this.handleWithdraw()
}
}
})
}
},
// 设置自动提现阈值
setAutoWithdrawThreshold(currentEnabled, currentThreshold) {
const minAmount = this.data.minWithdrawAmount || 10
wx.showModal({
title: '设置提现阈值',
content: `请输入自动提现金额阈值(最低¥${minAmount}`,
editable: true,
placeholderText: currentThreshold.toString(),
success: (res) => {
if (res.confirm && res.content) {
const threshold = parseFloat(res.content)
if (isNaN(threshold) || threshold < minAmount) {
wx.showToast({
title: `请输入不小于¥${minAmount}的金额`,
icon: 'none'
})
return
}
const app = getApp()
const { userInfo } = app.globalData
wx.setStorageSync(`autoWithdrawThreshold_${userInfo.id}`, threshold)
wx.showToast({
title: `阈值已设置为¥${threshold}`,
icon: 'success'
})
// 重新显示设置界面
setTimeout(() => {
this.showAutoWithdrawSettings()
}, 1500)
}
}
})
},
// 收益通知设置
showNotificationSettings() {
const app = getApp()
const { userInfo } = app.globalData
if (!userInfo) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
// 获取当前设置
let notifyEnabled = wx.getStorageSync(`earningsNotify_${userInfo.id}`) !== false // 默认开启
wx.showModal({
title: '收益通知设置',
content: `当前状态:${notifyEnabled ? '已开启' : '已关闭'}\n\n开启后,将在有新收益时收到小程序通知提醒。`,
confirmText: notifyEnabled ? '关闭通知' : '开启通知',
success: (res) => {
if (res.confirm) {
const newState = !notifyEnabled
wx.setStorageSync(`earningsNotify_${userInfo.id}`, newState)
wx.showToast({
title: newState ? '收益通知已开启' : '收益通知已关闭',
icon: 'success'
})
// 如果开启,请求通知权限
if (newState) {
wx.requestSubscribeMessage({
tmplIds: [''] // 需要配置模板ID
}).catch(() => {
// 用户拒绝授权,不影响功能
})
}
}
}
})
},
// 分享 - 带推荐码
onShareAppMessage() {
console.log('[Referral] 分享给好友,推荐码:', this.data.referralCode)
return {
title: 'Soul创业派对 - 来自派对房的真实商业故事',
path: `/pages/index/index?ref=${this.data.referralCode}`
// 不设置 imageUrl使用小程序默认截图
// 如需自定义图片,请将图片放在 /assets/ 目录并配置路径
}
},
goBack() {
wx.navigateBack()
},
// 解析商品描述,获取书名和章节
parseProductDescription(description, productType) {
if (!description) {
return {
bookTitle: '未知商品',
chapterTitle: ''
}
}
// 匹配格式:《书名》- 章节名
const match = description.match(/《(.+?)》(?:\s*-\s*(.+))?/)
if (match) {
return {
bookTitle: match[1] || '未知书籍',
chapterTitle: match[2] || (productType === 'fullbook' ? '全书购买' : '')
}
}
// 如果匹配失败,直接返回原始描述
return {
bookTitle: description.split('-')[0] || description,
chapterTitle: description.split('-')[1] || ''
}
},
// 格式化日期
formatDate(dateStr) {
if (!dateStr) return '--'
const d = new Date(dateStr)
const month = (d.getMonth() + 1).toString().padStart(2, '0')
const day = d.getDate().toString().padStart(2, '0')
return `${month}-${day}`
}
})