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

485 lines
14 KiB
JavaScript
Raw Normal View History

/**
* Soul创业派对 - 分销中心页
*
* 可见数据
* - 绑定用户数当前有效绑定
* - 通过链接进的人数总访问量
* - 带来的付款人数已转化购买
* - 收益统计90%归分发者
*/
const app = getApp()
Page({
data: {
statusBarHeight: 44,
isLoggedIn: false,
userInfo: null,
// === 核心可见数据 ===
bindingCount: 0, // 绑定用户数(当前有效)
visitCount: 0, // 通过链接进的人数
paidCount: 0, // 带来的付款人数
// === 收益数据 ===
earnings: 0, // 已结算收益
pendingEarnings: 0, // 待结算收益
withdrawnEarnings: 0, // 已提现金额
shareRate: 90, // 分成比例90%
// === 统计数据 ===
referralCount: 0, // 总推荐人数
expiringCount: 0, // 即将过期人数
// 邀请码
referralCode: '',
// 绑定用户列表
showBindingList: true,
activeTab: 'active',
activeBindings: [],
convertedBindings: [],
expiredBindings: [],
currentBindings: [],
totalBindings: 0,
// 收益明细
earningsDetails: [],
// 海报
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
console.log('[Referral] 获取推广数据成功:', realData)
}
} catch (e) {
console.log('[Referral] 获取推广数据失败,使用本地数据')
}
// 使用真实数据或默认值
let activeBindings = realData?.activeUsers || []
let convertedBindings = realData?.convertedUsers || []
let expiredBindings = []
// 兼容旧字段名
if (!activeBindings.length && realData?.activeBindings) {
activeBindings = realData.activeBindings
}
if (!convertedBindings.length && realData?.convertedBindings) {
convertedBindings = realData.convertedBindings
}
if (realData?.expiredBindings) {
expiredBindings = realData.expiredBindings
}
const expiringCount = activeBindings.filter(b => b.daysRemaining <= 7 && b.daysRemaining > 0).length
this.setData({
isLoggedIn: true,
userInfo,
// 核心可见数据
bindingCount: realData?.bindingCount || activeBindings.length,
visitCount: realData?.visitCount || 0,
paidCount: realData?.paidCount || convertedBindings.length,
// 收益数据
earnings: realData?.earnings || 0,
pendingEarnings: realData?.pendingEarnings || 0,
withdrawnEarnings: realData?.withdrawnEarnings || 0,
shareRate: realData?.shareRate || 90,
// 统计
referralCount: realData?.referralCount || realData?.stats?.totalBindings || activeBindings.length + convertedBindings.length,
expiringCount,
referralCode,
activeBindings,
convertedBindings,
expiredBindings,
currentBindings: activeBindings,
totalBindings: activeBindings.length + convertedBindings.length + expiredBindings.length,
// 收益明细
earningsDetails: realData?.earningsDetails || []
})
}
},
// 切换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()
}
})