Files
soul/miniprogram/pages/referral/referral.js
卡若 5501397542 fix: 优化支付失败提示+内容加载降级方案
1. 分享按钮精简为2个(推荐给好友+生成海报)
2. 章节内容三级降级:API→本地缓存→重试
3. 移除审核敏感文案(佣金/返利等)
4. 支付失败时显示客服微信号
2026-01-25 21:29:04 +08:00

461 lines
13 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创业派对 - 分销中心页
* 1:1还原Web版本
*/
const app = getApp()
Page({
data: {
statusBarHeight: 44,
isLoggedIn: false,
userInfo: null,
// 收益数据
earnings: 0,
pendingEarnings: 0,
distributorShare: 90,
// 统计数据
referralCount: 0,
expiringCount: 0,
// 邀请码
referralCode: '',
// 绑定用户
showBindingList: true,
activeTab: 'active',
activeBindings: [],
convertedBindings: [],
expiredBindings: [],
currentBindings: [],
totalBindings: 0,
// 海报
showPosterModal: false,
isGeneratingPoster: false
},
onLoad() {
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.initData()
},
onShow() {
this.initData()
},
// 初始化数据
async initData() {
const { isLoggedIn, userInfo } = app.globalData
if (isLoggedIn && userInfo) {
// 生成邀请码
const referralCode = userInfo.referralCode || 'SOUL' + (userInfo.id || Date.now().toString(36)).toUpperCase().slice(-6)
// 尝试从API获取真实数据
let realData = null
try {
const res = await app.request('/api/referral/data', {
method: 'GET',
data: { userId: userInfo.id }
})
if (res.success) {
realData = res.data
}
} catch (e) {
console.log('获取推广数据失败,使用本地数据')
}
// 使用真实数据或本地存储的数据
const storedBindings = wx.getStorageSync('referral_bindings') || []
const storedEarnings = wx.getStorageSync('referral_earnings') || { total: 0, pending: 0 }
let activeBindings, convertedBindings, expiredBindings
if (realData) {
activeBindings = realData.activeBindings || []
convertedBindings = realData.convertedBindings || []
expiredBindings = realData.expiredBindings || []
} else if (storedBindings.length > 0) {
// 使用本地存储的数据
activeBindings = storedBindings.filter(b => b.status === 'active')
convertedBindings = storedBindings.filter(b => b.status === 'converted')
expiredBindings = storedBindings.filter(b => b.status === 'expired')
} else {
// 默认空数据
activeBindings = []
convertedBindings = []
expiredBindings = []
}
const expiringCount = activeBindings.filter(b => b.daysRemaining <= 7).length
this.setData({
isLoggedIn: true,
userInfo,
earnings: realData?.earnings || storedEarnings.total || 0,
pendingEarnings: realData?.pendingEarnings || storedEarnings.pending || 0,
referralCount: realData?.referralCount || activeBindings.length + convertedBindings.length,
referralCode,
activeBindings,
convertedBindings,
expiredBindings,
expiringCount,
currentBindings: activeBindings,
totalBindings: activeBindings.length + convertedBindings.length + expiredBindings.length
})
}
},
// 切换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' })
})
},
// 生成推广海报
async generatePoster() {
wx.showLoading({ title: '生成中...' })
this.setData({ showPosterModal: true, isGeneratingPoster: true })
try {
const ctx = wx.createCanvasContext('promoPosterCanvas', this)
const { userInfo, earnings, referralCount, distributorShare } = this.data
const userId = userInfo?.id || ''
// 获取小程序码(带推荐人参数)
let qrcodeImage = null
try {
const scene = userId ? `ref=${userId.slice(0,20)}` : 'ref=soul'
const qrRes = await app.request('/api/miniprogram/qrcode', {
method: 'POST',
data: { scene, page: 'pages/index/index', width: 280 }
})
if (qrRes.success && qrRes.image) {
qrcodeImage = qrRes.image
}
} catch (e) {
console.log('[Poster] 获取小程序码失败,使用占位符')
}
// 海报尺寸 300x450
const width = 300
const height = 450
// 背景渐变
const grd = ctx.createLinearGradient(0, 0, 0, height)
grd.addColorStop(0, '#0f0c29')
grd.addColorStop(0.5, '#302b63')
grd.addColorStop(1, '#24243e')
ctx.setFillStyle(grd)
ctx.fillRect(0, 0, width, height)
// 顶部装饰
ctx.setFillStyle('#FFD700')
ctx.fillRect(0, 0, width, 5)
// 标题
ctx.setFillStyle('#FFD700')
ctx.setFontSize(20)
ctx.fillText('📚 Soul创业派对', 20, 45)
// 副标题
ctx.setFillStyle('rgba(255,255,255,0.8)')
ctx.setFontSize(12)
ctx.fillText('来自派对房的真实商业故事', 20, 70)
// 书籍介绍区域
ctx.setFillStyle('rgba(255,255,255,0.05)')
ctx.fillRect(15, 90, width - 30, 100)
ctx.setFillStyle('#ffffff')
ctx.setFontSize(14)
ctx.fillText('✨ 62个真实商业案例', 25, 115)
ctx.fillText('💡 私域运营实战经验', 25, 140)
ctx.fillText('🎯 从0到1创业方法论', 25, 165)
// 推广者信息
ctx.setFillStyle('#00CED1')
ctx.setFontSize(13)
ctx.fillText(`推荐人: ${userInfo?.nickname || '创业者'}`, 20, 220)
// 统计数据
ctx.setFillStyle('rgba(255,255,255,0.6)')
ctx.setFontSize(11)
ctx.fillText(`已推荐 ${referralCount} 位好友阅读`, 20, 245)
// 优惠信息
ctx.setFillStyle('rgba(255,215,0,0.15)')
ctx.fillRect(15, 265, width - 30, 50)
ctx.setFillStyle('#FFD700')
ctx.setFontSize(14)
ctx.fillText('🎁 专属福利', 25, 290)
ctx.setFillStyle('#ffffff')
ctx.setFontSize(12)
ctx.fillText('通过此码购买立享5%优惠', 25, 308)
// 底部区域
ctx.setFillStyle('rgba(0,206,209,0.1)')
ctx.fillRect(0, height - 80, width, 80)
// 底部提示
ctx.setFillStyle('#ffffff')
ctx.setFontSize(13)
ctx.fillText('长按识别 立即购买', 20, height - 50)
ctx.setFillStyle('rgba(255,255,255,0.6)')
ctx.setFontSize(11)
ctx.fillText('扫码立即阅读', 20, height - 28)
// 绘制小程序码
const drawQRCode = () => {
return new Promise((resolve) => {
if (qrcodeImage) {
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: () => {
ctx.drawImage(filePath, width - 75, height - 70, 60, 60)
resolve()
},
fail: () => {
this.drawQRPlaceholder(ctx, width, height)
resolve()
}
})
} else {
this.drawQRPlaceholder(ctx, width, height)
resolve()
}
})
}
await drawQRCode()
ctx.draw(true, () => {
wx.hideLoading()
this.setData({ isGeneratingPoster: false })
})
} catch (e) {
console.error('生成海报失败:', e)
wx.hideLoading()
wx.showToast({ title: '生成失败', icon: 'none' })
this.setData({ showPosterModal: false, isGeneratingPoster: false })
}
},
// 绘制小程序码占位符
drawQRPlaceholder(ctx, width, height) {
ctx.setFillStyle('#ffffff')
ctx.beginPath()
ctx.arc(width - 45, height - 40, 30, 0, Math.PI * 2)
ctx.fill()
ctx.setFillStyle('#00CED1')
ctx.setFontSize(9)
ctx.fillText('扫码', width - 52, height - 42)
ctx.fillText('购买', width - 52, height - 30)
},
// 关闭海报弹窗
closePosterModal() {
this.setData({ showPosterModal: false })
},
// 保存海报
savePoster() {
wx.canvasToTempFilePath({
canvasId: 'promoPosterCanvas',
success: (res) => {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
wx.showToast({ title: '已保存到相册', icon: 'success' })
this.setData({ showPosterModal: false })
},
fail: (err) => {
if (err.errMsg.includes('auth deny')) {
wx.showModal({
title: '提示',
content: '需要相册权限才能保存海报',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
wx.openSetting()
}
}
})
} else {
wx.showToast({ title: '保存失败', icon: 'none' })
}
}
})
},
fail: () => {
wx.showToast({ title: '生成图片失败', icon: 'none' })
}
}, this)
},
// 阻止冒泡
stopPropagation() {},
// 分享到朋友圈
shareToMoments() {
const shareText = `🔥 发现一本超棒的创业实战书《Soul创业派对》\n\n💡 62个真实商业案例从私域运营到资源整合干货满满\n\n🎁 通过我的链接购买立享5%优惠,我是 ${this.data.userInfo?.nickname || '卡若'} 推荐!\n\n👉 ${this.data.referralCode} 是我的专属邀请码\n\n#创业派对 #私域运营 #商业案例`
wx.setClipboardData({
data: shareText,
success: () => {
wx.showModal({
title: '文案已复制',
content: '请打开微信朋友圈,粘贴分享文案即可',
showCancel: false,
confirmText: '知道了'
})
}
})
},
// 提现 - 直接到微信零钱
async handleWithdraw() {
const pendingEarnings = parseFloat(this.data.pendingEarnings) || 0
if (pendingEarnings < 10) {
wx.showToast({ title: '满10元可提现', icon: 'none' })
return
}
// 确认提现
wx.showModal({
title: '确认提现',
content: `将提现 ¥${pendingEarnings.toFixed(2)} 到您的微信零钱`,
confirmText: '立即提现',
success: async (res) => {
if (res.confirm) {
await this.doWithdraw(pendingEarnings)
}
}
})
},
// 执行提现
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/withdraw', {
method: 'POST',
data: { userId, amount }
})
wx.hideLoading()
if (res.success) {
wx.showModal({
title: '提现成功 🎉',
content: `¥${amount.toFixed(2)} 已到账您的微信零钱`,
showCancel: false,
confirmText: '好的'
})
// 刷新数据
this.initData()
} else {
if (res.needBind) {
wx.showModal({
title: '需要绑定微信',
content: '请先在设置中绑定微信账号后再提现',
confirmText: '去绑定',
success: (modalRes) => {
if (modalRes.confirm) {
wx.navigateTo({ url: '/pages/settings/settings' })
}
}
})
} else {
wx.showToast({ title: res.error || '提现失败', icon: 'none' })
}
}
} 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) {
wx.showToast({ title: '自动提现功能开发中', icon: 'none' })
} else {
wx.showToast({ title: '通知设置开发中', icon: 'none' })
}
}
})
},
// 分享 - 带推荐码
onShareAppMessage() {
return {
title: '📚 Soul创业派对 - 来自派对房的真实商业故事',
path: `/pages/index/index?ref=${this.data.referralCode}`,
imageUrl: '/assets/share-cover.png'
}
},
// 分享到朋友圈
onShareTimeline() {
return {
title: `Soul创业派对 - 62个真实商业案例`,
query: `ref=${this.data.referralCode}`
}
},
goBack() {
wx.navigateBack()
}
})