/** * 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(() => { // 用户拒绝授权,不影响功能 }) } } } }) }, // 分享 - 带自己的邀请码(与 app.getMyReferralCode 一致) onShareAppMessage() { const app = getApp() const ref = app.getMyReferralCode() || this.data.referralCode return { title: 'Soul创业派对 - 来自派对房的真实商业故事', path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index' } }, 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}` } })