chore: 停止上传开发文档并同步代码
- 从仓库索引移除 开发文档/(本地保留) - 忽略 wechat/info.log 与 soul-api-linux - 同步小程序/管理端/API改动 Made-with: Cursor
This commit is contained in:
@@ -13,12 +13,13 @@
|
||||
* - contentSegments 解析每行,mention 高亮可点;点击→确认→登录/资料校验→POST /api/miniprogram/ckb/lead
|
||||
*/
|
||||
|
||||
import accessManager from '../../utils/chapterAccessManager'
|
||||
import readingTracker from '../../utils/readingTracker'
|
||||
const accessManager = require('../../utils/chapterAccessManager')
|
||||
const readingTracker = require('../../utils/readingTracker')
|
||||
const { parseScene } = require('../../utils/scene.js')
|
||||
const contentParser = require('../../utils/contentParser.js')
|
||||
const { trackClick } = require('../../utils/trackClick')
|
||||
|
||||
const { trackClick } = require('../../utils/trackClick')
|
||||
const { checkAndExecute } = require('../../utils/ruleEngine')
|
||||
const app = getApp()
|
||||
|
||||
Page({
|
||||
@@ -62,7 +63,7 @@ Page({
|
||||
// 价格
|
||||
sectionPrice: 1,
|
||||
fullBookPrice: 9.9,
|
||||
totalSections: 62,
|
||||
totalSections: 0,
|
||||
|
||||
// 弹窗
|
||||
showShareModal: false,
|
||||
@@ -71,44 +72,42 @@ Page({
|
||||
showPosterModal: false,
|
||||
isPaying: false,
|
||||
isGeneratingPoster: false,
|
||||
showShareTip: false,
|
||||
_shareTipShown: false,
|
||||
_lastScrollTop: 0,
|
||||
|
||||
// 章节 mid(扫码/海报分享用,便于分享 path 带 mid)
|
||||
sectionMid: null,
|
||||
|
||||
// 余额(用于余额支付)
|
||||
walletBalance: 0,
|
||||
// 审核模式
|
||||
auditMode: false
|
||||
},
|
||||
|
||||
async onLoad(options) {
|
||||
wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'] })
|
||||
this.setData({ auditMode: app.globalData.auditMode })
|
||||
wx.showShareMenu({ withShareTimeline: true })
|
||||
|
||||
// 预加载 linkTags、linkedMiniprograms(供 onLinkTagTap 用密钥查 appId)
|
||||
if (!app.globalData.linkTagsConfig || !app.globalData.linkedMiniprograms) {
|
||||
app.request({ url: '/api/miniprogram/config', silent: true }).then(cfg => {
|
||||
// 预加载 linkTags、linkedMiniprograms、persons(供 onLinkTagTap / onMentionTap 和内容自动匹配用)
|
||||
if (!app.globalData.linkTagsConfig || !app.globalData.linkedMiniprograms || !app.globalData.personsConfig) {
|
||||
try {
|
||||
const cfg = await app.request({ url: '/api/miniprogram/config', silent: true })
|
||||
if (cfg) {
|
||||
if (Array.isArray(cfg.linkTags)) app.globalData.linkTagsConfig = cfg.linkTags
|
||||
if (Array.isArray(cfg.linkedMiniprograms)) app.globalData.linkedMiniprograms = cfg.linkedMiniprograms
|
||||
if (Array.isArray(cfg.persons)) app.globalData.personsConfig = cfg.persons
|
||||
}
|
||||
}).catch(() => {})
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
// 支持 scene(扫码)、mid、id、ref、gift(代付)
|
||||
// 支持 scene(扫码)、mid、id、ref
|
||||
const sceneStr = (options && options.scene) || ''
|
||||
const parsed = parseScene(sceneStr)
|
||||
const ref = options.ref || parsed.ref
|
||||
const isGift = options.gift === '1' || options.gift === 'true'
|
||||
// 代付统一到代付页:gift=1&ref=requestSn 时直接跳转,禁止在阅读页代付
|
||||
if (isGift && ref) {
|
||||
wx.redirectTo({ url: `/pages/gift-pay/detail?requestSn=${encodeURIComponent(ref)}` })
|
||||
return
|
||||
}
|
||||
const mid = options.mid ? parseInt(options.mid, 10) : (parsed.mid || app.globalData.initialSectionMid || 0)
|
||||
let id = options.id || parsed.id || app.globalData.initialSectionId
|
||||
const ref = options.ref || parsed.ref
|
||||
if (app.globalData.initialSectionMid) delete app.globalData.initialSectionMid
|
||||
if (app.globalData.initialSectionId) delete app.globalData.initialSectionId
|
||||
|
||||
console.log("页面:",mid);
|
||||
|
||||
// mid 有值但无 id 时,从 bookData 或 API 解析 id
|
||||
if (mid && !id) {
|
||||
const bookData = app.globalData.bookData || []
|
||||
@@ -148,6 +147,11 @@ Page({
|
||||
app.handleReferralCode({ query: { ref } })
|
||||
}
|
||||
|
||||
const giftCode = options.gift || ''
|
||||
if (giftCode) {
|
||||
this._pendingGiftCode = giftCode
|
||||
}
|
||||
|
||||
try {
|
||||
const config = await accessManager.fetchLatestConfig()
|
||||
this.setData({
|
||||
@@ -170,6 +174,13 @@ Page({
|
||||
// 加载内容(复用已拉取的章节数据,避免二次请求)
|
||||
await this.loadContent(id, accessState, chapterRes)
|
||||
|
||||
// 自动领取礼物码(代付解锁)
|
||||
if (this._pendingGiftCode && !canAccess && app.globalData.isLoggedIn) {
|
||||
await this._redeemGiftCode(this._pendingGiftCode)
|
||||
this._pendingGiftCode = null
|
||||
return
|
||||
}
|
||||
|
||||
// 【标准流程】4. 如果有权限,初始化阅读追踪
|
||||
if (canAccess) {
|
||||
readingTracker.init(id)
|
||||
@@ -178,6 +189,21 @@ Page({
|
||||
// 5. 加载导航
|
||||
this.loadNavigation(id)
|
||||
|
||||
// 6. 规则引擎:阅读前检查(填头像、绑手机等)
|
||||
checkAndExecute('before_read', this)
|
||||
|
||||
// 7. 记录浏览行为到 user_tracks
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (userId) {
|
||||
app.request('/api/miniprogram/track', {
|
||||
method: 'POST',
|
||||
data: { userId, action: 'view_chapter', target: id, extraData: { sectionId: id, mid: mid || '' } },
|
||||
silent: true
|
||||
}).catch(() => {})
|
||||
// 更新全局阅读计数
|
||||
app.globalData.readCount = (app.globalData.readCount || 0) + 1
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Read] 初始化失败:', e)
|
||||
wx.showToast({ title: '加载失败,请重试', icon: 'none' })
|
||||
@@ -194,6 +220,11 @@ Page({
|
||||
return
|
||||
}
|
||||
|
||||
const currentScrollTop = e.scrollTop || 0
|
||||
const lastScrollTop = this.data._lastScrollTop || 0
|
||||
const isScrollingDown = currentScrollTop < lastScrollTop
|
||||
this.setData({ _lastScrollTop: currentScrollTop })
|
||||
|
||||
// 获取滚动信息并更新追踪器
|
||||
const query = wx.createSelectorQuery()
|
||||
query.select('.page').boundingClientRect()
|
||||
@@ -212,6 +243,12 @@ Page({
|
||||
? Math.min((scrollInfo.scrollTop / totalScrollable) * 100, 100)
|
||||
: 0
|
||||
this.setData({ readingProgress: progress })
|
||||
|
||||
// 阅读超过20%且向上滑动时,弹出一次分享提示
|
||||
if (progress >= 20 && isScrollingDown && !this.data._shareTipShown) {
|
||||
this.setData({ showShareTip: true, _shareTipShown: true })
|
||||
setTimeout(() => { this.setData({ showShareTip: false }) }, 4000)
|
||||
}
|
||||
|
||||
// 更新阅读追踪器(记录最大进度、判断是否读完)
|
||||
readingTracker.updateProgress(scrollInfo)
|
||||
@@ -239,7 +276,8 @@ Page({
|
||||
// 已解锁用 data.content(完整内容),未解锁用 content(预览);先 determineAccessState 再 loadContent 保证顺序正确
|
||||
const displayContent = accessManager.canAccessFullContent(accessState) ? (res.data?.content ?? res.content) : res.content
|
||||
if (res && displayContent) {
|
||||
const { lines, segments } = contentParser.parseContent(displayContent)
|
||||
const parserConfig = { persons: app.globalData.personsConfig || [], linkTags: app.globalData.linkTagsConfig || [] }
|
||||
const { lines, segments } = contentParser.parseContent(displayContent, parserConfig)
|
||||
// 预览内容由后端统一截取比例,这里展示全部预览内容
|
||||
const previewCount = lines.length
|
||||
const updates = {
|
||||
@@ -263,7 +301,8 @@ Page({
|
||||
try {
|
||||
const cached = wx.getStorageSync(cacheKey)
|
||||
if (cached && cached.content) {
|
||||
const { lines, segments } = contentParser.parseContent(cached.content)
|
||||
const cachedParserConfig = { persons: app.globalData.personsConfig || [], linkTags: app.globalData.linkTagsConfig || [] }
|
||||
const { lines, segments } = contentParser.parseContent(cached.content, cachedParserConfig)
|
||||
// 预览内容由后端统一截取比例,这里展示全部预览内容
|
||||
const previewCount = lines.length
|
||||
this.setData({
|
||||
@@ -286,49 +325,31 @@ Page({
|
||||
|
||||
// 获取章节信息
|
||||
getSectionInfo(id) {
|
||||
// 特殊章节
|
||||
if (id === 'preface') {
|
||||
return { id: 'preface', title: '为什么我每天早上6点在Soul开播?', isFree: true, price: 0 }
|
||||
}
|
||||
if (id === 'epilogue') {
|
||||
return { id: 'epilogue', title: '这本书的真实目的', isFree: true, price: 0 }
|
||||
}
|
||||
if (id.startsWith('appendix')) {
|
||||
const appendixTitles = {
|
||||
'appendix-1': 'Soul派对房精选对话',
|
||||
'appendix-2': '创业者自检清单',
|
||||
'appendix-3': '本书提到的工具和资源'
|
||||
const cachedSection = (app.globalData.bookData || []).find((item) => item.id === id)
|
||||
if (cachedSection) {
|
||||
return {
|
||||
id,
|
||||
title: cachedSection.sectionTitle || cachedSection.section_title || cachedSection.title || cachedSection.chapterTitle || `章节 ${id}`,
|
||||
isFree: cachedSection.isFree === true || cachedSection.is_free === true || cachedSection.price === 0,
|
||||
price: cachedSection.price ?? 1
|
||||
}
|
||||
return { id, title: appendixTitles[id] || '附录', isFree: true, price: 0 }
|
||||
}
|
||||
|
||||
// 普通章节
|
||||
|
||||
return {
|
||||
id: id,
|
||||
id,
|
||||
title: this.getSectionTitle(id),
|
||||
isFree: id === '1.1',
|
||||
isFree: false,
|
||||
price: 1
|
||||
}
|
||||
},
|
||||
|
||||
// 获取章节标题
|
||||
getSectionTitle(id) {
|
||||
const titles = {
|
||||
'1.1': '荷包:电动车出租的被动收入模式',
|
||||
'1.2': '老墨:资源整合高手的社交方法',
|
||||
'1.3': '笑声背后的MBTI',
|
||||
'1.4': '人性的三角结构:利益、情感、价值观',
|
||||
'1.5': '沟通差的问题:为什么你说的别人听不懂',
|
||||
'2.1': '相亲故事:你以为找的是人,实际是在找模式',
|
||||
'2.2': '找工作迷茫者:为什么简历解决不了人生',
|
||||
'2.3': '撸运费险:小钱困住大脑的真实心理',
|
||||
'2.4': '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力',
|
||||
'2.5': '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒',
|
||||
'3.1': '3000万流水如何跑出来(退税模式解析)',
|
||||
'8.1': '流量杠杆:抖音、Soul、飞书',
|
||||
'9.14': '大健康私域:一个月150万的70后'
|
||||
const cachedSection = (app.globalData.bookData || []).find((item) => item.id === id)
|
||||
if (cachedSection) {
|
||||
return cachedSection.sectionTitle || cachedSection.section_title || cachedSection.title || cachedSection.chapterTitle || `章节 ${id}`
|
||||
}
|
||||
return titles[id] || `章节 ${id}`
|
||||
return `章节 ${id}`
|
||||
},
|
||||
|
||||
// 根据 id/mid 构造章节接口路径(优先使用 mid)。必须带 userId 才能让后端正确判断付费用户并返回完整内容
|
||||
@@ -502,33 +523,53 @@ Page({
|
||||
}
|
||||
}
|
||||
|
||||
// CKB 类型:复用 @mention 加好友流程,弹出留资表单
|
||||
// CKB 类型:走「链接卡若」留资流程(与首页 onLinkKaruo 一致)
|
||||
if (tagType === 'ckb') {
|
||||
// 触发通用加好友(无特定 personId,使用全局 CKB Key)
|
||||
this.onMentionTap({ currentTarget: { dataset: { userId: '', nickname: label } } })
|
||||
this._doCkbLead(label)
|
||||
return
|
||||
}
|
||||
|
||||
// 小程序类型:用密钥查 linkedMiniprograms 得 appId,再唤醒(需在 app.json 的 navigateToMiniProgramAppIdList 中配置)
|
||||
// 小程序类型:先查 linkedMiniprograms 得 appId,降级直接用 mpKey/appId 字段
|
||||
if (tagType === 'miniprogram') {
|
||||
let appId = (e.currentTarget.dataset.appId || '').trim()
|
||||
if (!mpKey && label) {
|
||||
const cached = (app.globalData.linkTagsConfig || []).find(t => t.label === label)
|
||||
if (cached) mpKey = cached.mpKey || ''
|
||||
if (cached) {
|
||||
mpKey = cached.mpKey || ''
|
||||
if (!appId && cached.appId) appId = cached.appId
|
||||
}
|
||||
}
|
||||
const linked = (app.globalData.linkedMiniprograms || []).find(m => m.key === mpKey)
|
||||
if (linked && linked.appId) {
|
||||
const targetAppId = (linked && linked.appId) ? linked.appId : (appId || mpKey || '')
|
||||
const selfAppId = (app.globalData.config?.mpConfig?.appId || app.globalData.appId || 'wxb8bbb2b10dec74aa')
|
||||
const targetPath = pagePath || (linked && linked.path) || ''
|
||||
if (targetAppId === selfAppId || !targetAppId) {
|
||||
if (targetPath) {
|
||||
const navPath = targetPath.startsWith('/') ? targetPath : '/' + targetPath
|
||||
wx.navigateTo({ url: navPath, fail: () => wx.switchTab({ url: navPath }) })
|
||||
} else {
|
||||
wx.switchTab({ url: '/pages/index/index' })
|
||||
}
|
||||
return
|
||||
}
|
||||
if (targetAppId) {
|
||||
wx.navigateToMiniProgram({
|
||||
appId: linked.appId,
|
||||
path: pagePath || linked.path || '',
|
||||
appId: targetAppId,
|
||||
path: targetPath,
|
||||
envVersion: 'release',
|
||||
success: () => {},
|
||||
fail: (err) => {
|
||||
wx.showToast({ title: err.errMsg || '跳转失败', icon: 'none' })
|
||||
console.warn('[LinkTag] 小程序跳转失败:', err)
|
||||
if (targetPath) {
|
||||
wx.navigateTo({ url: targetPath.startsWith('/') ? targetPath : '/' + targetPath, fail: () => {} })
|
||||
} else {
|
||||
wx.showToast({ title: '跳转失败,请检查小程序配置', icon: 'none' })
|
||||
}
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
if (mpKey) wx.showToast({ title: '未找到关联小程序配置', icon: 'none' })
|
||||
wx.showToast({ title: '未配置关联小程序', icon: 'none' })
|
||||
}
|
||||
|
||||
// 小程序内部路径(pagePath 或 url 以 /pages/ 开头)
|
||||
@@ -560,9 +601,17 @@ Page({
|
||||
|
||||
// 点击正文中的 @某人:确认弹窗 → 登录/资料校验 → 调用 ckb/lead 加好友留资
|
||||
onMentionTap(e) {
|
||||
const userId = e.currentTarget.dataset.userId
|
||||
let userId = e.currentTarget.dataset.userId
|
||||
const nickname = (e.currentTarget.dataset.nickname || '').trim() || 'TA'
|
||||
if (!userId) return
|
||||
if (!userId && nickname !== 'TA') {
|
||||
const persons = app.globalData.personsConfig || []
|
||||
const match = persons.find(p => p.name === nickname || (p.aliases || '').split(',').map(a => a.trim()).includes(nickname))
|
||||
if (match) userId = match.personId || ''
|
||||
}
|
||||
if (!userId) {
|
||||
wx.showToast({ title: `暂无 @${nickname} 的信息`, icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.showModal({
|
||||
title: '添加好友',
|
||||
content: `是否添加 @${nickname} ?`,
|
||||
@@ -593,19 +642,21 @@ Page({
|
||||
const myUserId = app.globalData.userInfo.id
|
||||
let phone = (app.globalData.userInfo.phone || '').trim()
|
||||
let wechatId = (app.globalData.userInfo.wechatId || app.globalData.userInfo.wechat_id || '').trim()
|
||||
let avatar = (app.globalData.userInfo.avatar || app.globalData.userInfo.avatarUrl || '').trim()
|
||||
if (!phone && !wechatId) {
|
||||
try {
|
||||
const profileRes = await app.request({ url: `/api/miniprogram/user/profile?userId=${myUserId}`, silent: true })
|
||||
if (profileRes?.success && profileRes.data) {
|
||||
phone = (profileRes.data.phone || '').trim()
|
||||
wechatId = (profileRes.data.wechatId || profileRes.data.wechat_id || '').trim()
|
||||
if (!avatar) avatar = (profileRes.data.avatar || '').trim()
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (!phone && !wechatId) {
|
||||
if ((!phone && !wechatId) || !avatar) {
|
||||
wx.showModal({
|
||||
title: '完善资料',
|
||||
content: '请先填写手机号或微信号,以便对方联系您',
|
||||
content: !avatar ? '请先设置头像和填写联系方式,以便对方联系您' : '请先填写手机号或微信号,以便对方联系您',
|
||||
confirmText: '去填写',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
@@ -614,12 +665,6 @@ Page({
|
||||
})
|
||||
return
|
||||
}
|
||||
// 2 分钟内只能点一次(与后端限频一致,与首页链接卡若共用)
|
||||
const leadLastTs = wx.getStorageSync('lead_last_submit_ts') || 0
|
||||
if (Date.now() - leadLastTs < 2 * 60 * 1000) {
|
||||
wx.showToast({ title: '操作太频繁,请2分钟后再试', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.showLoading({ title: '提交中...', mask: true })
|
||||
try {
|
||||
const res = await app.request({
|
||||
@@ -637,8 +682,84 @@ Page({
|
||||
})
|
||||
wx.hideLoading()
|
||||
if (res && res.success) {
|
||||
wx.setStorageSync('lead_last_submit_ts', Date.now())
|
||||
wx.showToast({ title: res.message || '提交成功,对方会尽快联系您', icon: 'success' })
|
||||
const who = targetNickname || '对方'
|
||||
wx.showModal({
|
||||
title: '提交成功',
|
||||
content: `${who} 会主动添加你微信,请注意你的微信消息`,
|
||||
showCancel: false,
|
||||
confirmText: '好的'
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: (e && e.message) || '提交失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
async _doCkbLead(label) {
|
||||
const app = getApp()
|
||||
if (!app.globalData.isLoggedIn || !app.globalData.userInfo) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '请先登录后再链接',
|
||||
confirmText: '去登录',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) wx.switchTab({ url: '/pages/my/my' })
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
const userId = app.globalData.userInfo.id
|
||||
let phone = (app.globalData.userInfo.phone || '').trim()
|
||||
let wechatId = (app.globalData.userInfo.wechatId || app.globalData.userInfo.wechat_id || '').trim()
|
||||
let avatar = (app.globalData.userInfo.avatar || app.globalData.userInfo.avatarUrl || '').trim()
|
||||
if (!phone && !wechatId) {
|
||||
try {
|
||||
const profileRes = await app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true })
|
||||
if (profileRes?.success && profileRes.data) {
|
||||
phone = (profileRes.data.phone || '').trim()
|
||||
wechatId = (profileRes.data.wechatId || profileRes.data.wechat_id || '').trim()
|
||||
if (!avatar) avatar = (profileRes.data.avatar || '').trim()
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if ((!phone && !wechatId) || !avatar) {
|
||||
wx.showModal({
|
||||
title: '完善资料',
|
||||
content: !avatar ? '请先设置头像和填写联系方式,以便对方联系您' : '请先填写手机号或微信号,以便对方联系您',
|
||||
confirmText: '去填写',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
wx.showLoading({ title: '提交中...', mask: true })
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/api/miniprogram/ckb/index-lead',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId,
|
||||
phone: phone || undefined,
|
||||
wechatId: wechatId || undefined,
|
||||
name: (app.globalData.userInfo.nickname || '').trim() || undefined,
|
||||
source: 'article_ckb_tag',
|
||||
tagLabel: label || undefined
|
||||
}
|
||||
})
|
||||
wx.hideLoading()
|
||||
if (res && res.success) {
|
||||
wx.showModal({
|
||||
title: '提交成功',
|
||||
content: '卡若会主动添加你微信,请注意你的微信消息',
|
||||
showCancel: false,
|
||||
confirmText: '好的'
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
|
||||
}
|
||||
@@ -657,41 +778,6 @@ Page({
|
||||
this.setData({ showShareModal: false })
|
||||
},
|
||||
|
||||
// 找好友代付:创建代付请求后跳转代付页(美团式,在代付页分享)
|
||||
async showGiftShareModal() {
|
||||
if (!app.globalData.userInfo?.id) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const { sectionId, sectionMid } = this.data
|
||||
const productId = sectionId || ''
|
||||
if (!productId) {
|
||||
wx.showToast({ title: '章节信息异常', icon: 'none' })
|
||||
return
|
||||
}
|
||||
wx.showLoading({ title: '创建代付请求...', mask: true })
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/api/miniprogram/gift-pay/create',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId: app.globalData.userInfo.id,
|
||||
productType: 'section',
|
||||
productId
|
||||
}
|
||||
})
|
||||
wx.hideLoading()
|
||||
if (res && res.success && res.requestSn) {
|
||||
wx.navigateTo({ url: `/pages/gift-pay/detail?requestSn=${res.requestSn}` })
|
||||
} else {
|
||||
wx.showToast({ title: res?.error || '创建失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '创建失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
// 复制链接
|
||||
copyLink() {
|
||||
const userInfo = app.globalData.userInfo
|
||||
@@ -709,16 +795,15 @@ Page({
|
||||
|
||||
// 复制分享文案(朋友圈风格)
|
||||
copyShareText() {
|
||||
const { section } = this.data
|
||||
|
||||
const shareText = `🔥 刚看完这篇《${section?.title || 'Soul创业派对'}》,太上头了!
|
||||
|
||||
62个真实商业案例,每个都是从0到1的实战经验。私域运营、资源整合、商业变现,干货满满。
|
||||
|
||||
推荐给正在创业或想创业的朋友,搜"Soul创业派对"小程序就能看!
|
||||
|
||||
#创业派对 #私域运营 #商业案例`
|
||||
|
||||
const title = this.data.section?.title || this.data.chapterTitle || '好文推荐'
|
||||
const raw = (this.data.content || '')
|
||||
.replace(/<[^>]+>/g, '\n')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').replace(/"/g, '"')
|
||||
.replace(/[#@]\S+/g, '')
|
||||
const sentences = raw.split(/[。!?\n]+/).map(s => s.trim()).filter(s => s.length > 4)
|
||||
const picked = sentences.slice(0, 5)
|
||||
const shareText = title + '\n\n' + picked.join('\n\n')
|
||||
wx.setClipboardData({
|
||||
data: shareText,
|
||||
success: () => {
|
||||
@@ -727,39 +812,63 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
// 分享到微信 - 自动带分享人ID
|
||||
// 分享到微信 - 自动带分享人ID;优先用 mid(扫码/海报闭环),无则用 id
|
||||
onShareAppMessage() {
|
||||
trackClick('read', 'btn_click', '分享_' + this.data.sectionId)
|
||||
const { section, sectionId, sectionMid } = this.data
|
||||
const ref = app.getMyReferralCode()
|
||||
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
|
||||
const path = ref ? `/pages/read/read?${q}&ref=${ref}` : `/pages/read/read?${q}`
|
||||
const title = section?.title
|
||||
const giftCode = this._giftCodeToShare || ''
|
||||
this._giftCodeToShare = null
|
||||
|
||||
let shareTitle = section?.title
|
||||
? `📚 ${section.title.length > 20 ? section.title.slice(0, 20) + '...' : section.title}`
|
||||
: '📚 Soul创业派对 - 真实商业故事'
|
||||
return { title, path }
|
||||
if (giftCode) shareTitle = `🎁 好友已为你解锁:${section?.title || '精选文章'}`
|
||||
|
||||
let path = `/pages/read/read?${q}`
|
||||
if (ref) path += `&ref=${ref}`
|
||||
if (giftCode) path += `&gift=${giftCode}`
|
||||
|
||||
return { title: shareTitle, path }
|
||||
},
|
||||
|
||||
// 底部「分享到朋友圈」按钮点击:微信不支持 button open-type=shareTimeline,只能通过右上角菜单分享,点击时引导用户
|
||||
onShareTimelineTap() {
|
||||
wx.showToast({
|
||||
title: '请点击右上角「...」→ 分享到朋友圈',
|
||||
icon: 'none',
|
||||
duration: 2500
|
||||
})
|
||||
},
|
||||
|
||||
// 分享到朋友圈:带文章标题,过长时截断
|
||||
// 分享到朋友圈:带文章标题,过长时截断(朋友圈卡片标题显示有限)
|
||||
onShareTimeline() {
|
||||
const { section, sectionId, sectionMid, chapterTitle } = this.data
|
||||
const ref = app.getMyReferralCode()
|
||||
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
|
||||
const query = ref ? `${q}&ref=${ref}` : q
|
||||
const articleTitle = (section?.title || chapterTitle || '').trim()
|
||||
const title = articleTitle
|
||||
? (articleTitle.length > 28 ? articleTitle.slice(0, 28) + '...' : articleTitle)
|
||||
: 'Soul创业派对 - 真实商业故事'
|
||||
return { title, query }
|
||||
? `📚 ${articleTitle.length > 24 ? articleTitle.slice(0, 24) + '...' : articleTitle}|Soul创业派对`
|
||||
: '📚 Soul创业派对 - 真实商业故事'
|
||||
return { title, query: ref ? `${q}&ref=${ref}` : q }
|
||||
},
|
||||
|
||||
shareToMoments() {
|
||||
const title = this.data.section?.title || this.data.chapterTitle || '好文推荐'
|
||||
const raw = (this.data.content || '')
|
||||
.replace(/<[^>]+>/g, '\n')
|
||||
.replace(/ /g, ' ')
|
||||
.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').replace(/"/g, '"')
|
||||
.replace(/[#@]\S+/g, '')
|
||||
const sentences = raw.split(/[。!?\n]+/).map(s => s.trim()).filter(s => s.length > 4)
|
||||
const picked = sentences.slice(0, 5)
|
||||
const copyText = title + '\n\n' + picked.join('\n\n')
|
||||
wx.setClipboardData({
|
||||
data: copyText,
|
||||
success: () => {
|
||||
wx.showModal({
|
||||
title: '文案已复制',
|
||||
content: '请点击右上角「···」菜单,选择「分享到朋友圈」即可发布',
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '复制失败,请手动复制', icon: 'none' })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 显示登录弹窗(每次打开协议未勾选,符合审核要求)
|
||||
@@ -971,39 +1080,6 @@ Page({
|
||||
wx.showLoading({ title: '正在发起支付...', mask: true })
|
||||
|
||||
try {
|
||||
// 0. 尝试余额支付(若余额足够)
|
||||
const userId = app.globalData.userInfo?.id
|
||||
const referralCode = wx.getStorageSync('referral_code') || ''
|
||||
if (userId) {
|
||||
try {
|
||||
const balanceRes = await app.request({ url: `/api/miniprogram/balance?userId=${userId}`, silent: true })
|
||||
const balance = balanceRes?.data?.balance || 0
|
||||
if (balance >= amount) {
|
||||
const productId = type === 'section' ? sectionId : (type === 'fullbook' ? 'fullbook' : '')
|
||||
const consumeRes = await app.request({
|
||||
url: '/api/miniprogram/balance/consume',
|
||||
method: 'POST',
|
||||
data: {
|
||||
userId,
|
||||
productType: type,
|
||||
productId: type === 'section' ? sectionId : (type === 'fullbook' ? 'fullbook' : 'vip_annual'),
|
||||
amount,
|
||||
referralCode: referralCode || undefined
|
||||
}
|
||||
})
|
||||
if (consumeRes?.success) {
|
||||
wx.hideLoading()
|
||||
this.setData({ isPaying: false })
|
||||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||||
await this.onPaymentSuccess()
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Pay] 余额支付失败,改用微信支付:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 先获取openId (支付必需)
|
||||
let openId = app.globalData.openId || wx.getStorageSync('openId')
|
||||
|
||||
@@ -1071,15 +1147,18 @@ Page({
|
||||
console.error('[Pay] API创建订单失败:', apiError)
|
||||
wx.hideLoading()
|
||||
// 支付接口失败时,显示客服联系方式
|
||||
const supportWechat = app.globalData.supportWechat || ''
|
||||
wx.showModal({
|
||||
title: '支付通道维护中',
|
||||
content: '微信支付正在审核中,请添加客服微信(28533368)手动购买,感谢理解!',
|
||||
confirmText: '复制微信号',
|
||||
content: supportWechat
|
||||
? `微信支付正在审核中,请添加客服微信(${supportWechat})手动购买,感谢理解!`
|
||||
: '微信支付正在审核中,请联系管理员手动购买,感谢理解!',
|
||||
confirmText: supportWechat ? '复制微信号' : '我知道了',
|
||||
cancelText: '稍后再说',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
if (res.confirm && supportWechat) {
|
||||
wx.setClipboardData({
|
||||
data: '28533368',
|
||||
data: supportWechat,
|
||||
success: () => {
|
||||
wx.showToast({ title: '微信号已复制', icon: 'success' })
|
||||
}
|
||||
@@ -1119,15 +1198,18 @@ Page({
|
||||
wx.showToast({ title: '已取消支付', icon: 'none' })
|
||||
} else if (payErr.errMsg && payErr.errMsg.includes('requestPayment:fail')) {
|
||||
// 支付失败,可能是参数错误或权限问题
|
||||
const supportWechat = app.globalData.supportWechat || ''
|
||||
wx.showModal({
|
||||
title: '支付失败',
|
||||
content: '微信支付暂不可用,请添加客服微信(28533368)手动购买',
|
||||
confirmText: '复制微信号',
|
||||
content: supportWechat
|
||||
? `微信支付暂不可用,请添加客服微信(${supportWechat})手动购买`
|
||||
: '微信支付暂不可用,请稍后重试或联系管理员',
|
||||
confirmText: supportWechat ? '复制微信号' : '我知道了',
|
||||
cancelText: '取消',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
if (res.confirm && supportWechat) {
|
||||
wx.setClipboardData({
|
||||
data: '28533368',
|
||||
data: supportWechat,
|
||||
success: () => wx.showToast({ title: '微信号已复制', icon: 'success' })
|
||||
})
|
||||
}
|
||||
@@ -1280,6 +1362,11 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/referral/referral' })
|
||||
},
|
||||
|
||||
showPosterModal() {
|
||||
this.setData({ showPosterModal: true })
|
||||
this.generatePoster()
|
||||
},
|
||||
|
||||
// 生成海报
|
||||
async generatePoster() {
|
||||
wx.showLoading({ title: '生成中...' })
|
||||
@@ -1444,7 +1531,160 @@ Page({
|
||||
closePosterModal() {
|
||||
this.setData({ showPosterModal: false })
|
||||
},
|
||||
|
||||
|
||||
closeShareTip() {
|
||||
this.setData({ showShareTip: false })
|
||||
},
|
||||
|
||||
// 代付分享:微信支付或余额帮好友解锁当前章节
|
||||
async handleGiftPay() {
|
||||
if (!app.globalData.isLoggedIn || !app.globalData.userInfo) {
|
||||
wx.showModal({ title: '提示', content: '请先登录', confirmText: '去登录', success: (r) => { if (r.confirm) this.showLoginModal() } })
|
||||
return
|
||||
}
|
||||
const sectionId = this.data.sectionId
|
||||
const userId = app.globalData.userInfo.id
|
||||
const price = (this.data.section && this.data.section.price != null) ? this.data.section.price : (this.data.sectionPrice || 1)
|
||||
|
||||
wx.showModal({
|
||||
title: '代付分享',
|
||||
content: `为好友代付本章 ¥${price}\n支付后将生成代付链接,好友点击即可免费阅读`,
|
||||
confirmText: '确认代付',
|
||||
cancelText: '取消',
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
wx.showActionSheet({
|
||||
itemList: ['微信支付', '用余额支付'],
|
||||
success: async (actionRes) => {
|
||||
if (actionRes.tapIndex === 0) {
|
||||
this._giftPayViaWechat(sectionId, userId, price)
|
||||
} else if (actionRes.tapIndex === 1) {
|
||||
this._giftPayViaBalance(sectionId, userId, price)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async _giftPayViaWechat(sectionId, userId, price) {
|
||||
let openId = app.globalData.openId || wx.getStorageSync('openId')
|
||||
if (!openId) { openId = await app.getOpenId() }
|
||||
if (!openId) { wx.showToast({ title: '获取支付凭证失败,请重新登录', icon: 'none' }); return }
|
||||
wx.showLoading({ title: '创建订单...' })
|
||||
try {
|
||||
const payRes = await app.request({
|
||||
url: '/api/miniprogram/pay',
|
||||
method: 'POST',
|
||||
data: {
|
||||
openId: openId,
|
||||
productType: 'gift',
|
||||
productId: sectionId,
|
||||
amount: price,
|
||||
description: `代付解锁:${this.data.section?.title || sectionId}`,
|
||||
userId: userId,
|
||||
}
|
||||
})
|
||||
wx.hideLoading()
|
||||
const params = (payRes && payRes.data && payRes.data.payParams) ? payRes.data.payParams : (payRes && payRes.payParams ? payRes.payParams : null)
|
||||
if (params) {
|
||||
wx.requestPayment({
|
||||
...params,
|
||||
success: async () => {
|
||||
wx.showLoading({ title: '生成分享链接...' })
|
||||
try {
|
||||
const giftRes = await app.request({
|
||||
url: '/api/miniprogram/balance/gift',
|
||||
method: 'POST',
|
||||
data: { giverId: userId, sectionId, paidViaWechat: true }
|
||||
})
|
||||
wx.hideLoading()
|
||||
if (giftRes && giftRes.data && giftRes.data.giftCode) {
|
||||
this._giftCodeToShare = giftRes.data.giftCode
|
||||
wx.showModal({
|
||||
title: '代付成功',
|
||||
content: `已为好友代付 ¥${price},分享后好友可免费阅读`,
|
||||
confirmText: '分享给好友',
|
||||
cancelText: '稍后分享',
|
||||
success: (r) => { if (r.confirm) wx.shareAppMessage() }
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: '支付成功,请手动分享', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '支付成功,生成链接失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
fail: () => { wx.showToast({ title: '支付已取消', icon: 'none' }) }
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: '创建支付失败', icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
console.error('[GiftPay] WeChat pay error:', e)
|
||||
wx.showToast({ title: '支付失败,请重试', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
async _giftPayViaBalance(sectionId, userId, price) {
|
||||
const balRes = await app.request({ url: `/api/miniprogram/balance?userId=${userId}`, silent: true }).catch(() => null)
|
||||
const balance = (balRes && balRes.data) ? balRes.data.balance : 0
|
||||
|
||||
if (balance < price) {
|
||||
wx.showModal({
|
||||
title: '余额不足',
|
||||
content: `当前余额 ¥${balance.toFixed(2)},需要 ¥${price}\n请先充值`,
|
||||
confirmText: '去充值',
|
||||
cancelText: '取消',
|
||||
success: (r) => { if (r.confirm) wx.navigateTo({ url: '/pages/wallet/wallet' }) }
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '处理中...' })
|
||||
try {
|
||||
const giftRes = await app.request({
|
||||
url: '/api/miniprogram/balance/gift',
|
||||
method: 'POST',
|
||||
data: { giverId: userId, sectionId }
|
||||
})
|
||||
wx.hideLoading()
|
||||
if (giftRes && giftRes.data && giftRes.data.giftCode) {
|
||||
this._giftCodeToShare = giftRes.data.giftCode
|
||||
wx.showModal({
|
||||
title: '代付成功',
|
||||
content: `已从余额扣除 ¥${price},分享后好友可免费阅读`,
|
||||
confirmText: '分享给好友',
|
||||
cancelText: '稍后分享',
|
||||
success: (r) => { if (r.confirm) wx.shareAppMessage() }
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '代付失败', icon: 'none' })
|
||||
}
|
||||
},
|
||||
|
||||
// 领取礼物码解锁
|
||||
async _redeemGiftCode(giftCode) {
|
||||
if (!app.globalData.isLoggedIn || !app.globalData.userInfo) return
|
||||
try {
|
||||
const res = await app.request({
|
||||
url: '/api/miniprogram/balance/gift/redeem',
|
||||
method: 'POST',
|
||||
data: { giftCode, receiverId: app.globalData.userInfo.id }
|
||||
})
|
||||
if (res && res.data) {
|
||||
wx.showToast({ title: '好友已为你解锁!', icon: 'success' })
|
||||
this.onLoad({ id: this.data.sectionId })
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('[Gift] 领取失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
// 保存海报到相册
|
||||
savePoster() {
|
||||
wx.canvasToTempFilePath({
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
<!-- 阅读内容 -->
|
||||
<view class="read-content">
|
||||
<!-- 章节标题 -->
|
||||
<view class="chapter-header">
|
||||
<view class="chapter-header" wx:if="{{section}}">
|
||||
<view class="chapter-meta">
|
||||
<text class="chapter-id">{{section.id}}</text>
|
||||
<text class="tag tag-free" wx:if="{{section.isFree}}">免费</text>
|
||||
</view>
|
||||
<text class="chapter-title" user-select>{{section.title}}</text>
|
||||
<text class="chapter-title">{{section.title}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
@@ -46,9 +46,9 @@
|
||||
<view class="article" wx:if="{{accessState === 'free' || accessState === 'unlocked_purchased'}}">
|
||||
<view class="paragraph" wx:for="{{contentSegments}}" wx:key="index">
|
||||
<block wx:for="{{item}}" wx:key="index" wx:for-item="seg">
|
||||
<text wx:if="{{seg.type === 'text'}}" user-select>{{seg.text}}</text>
|
||||
<text wx:elif="{{seg.type === 'mention'}}" class="mention" user-select bindtap="onMentionTap" data-user-id="{{seg.userId}}" data-nickname="{{seg.nickname}}">@{{seg.nickname}}</text>
|
||||
<text wx:elif="{{seg.type === 'linkTag'}}" class="link-tag" user-select bindtap="onLinkTagTap" data-url="{{seg.url}}" data-label="{{seg.label}}" data-tag-type="{{seg.tagType}}" data-page-path="{{seg.pagePath}}" data-tag-id="{{seg.tagId}}" data-app-id="{{seg.appId}}" data-mp-key="{{seg.mpKey}}">#{{seg.label}}</text>
|
||||
<text wx:if="{{seg.type === 'text'}}">{{seg.text}}</text>
|
||||
<text wx:elif="{{seg.type === 'mention'}}" class="mention" bindtap="onMentionTap" data-user-id="{{seg.userId}}" data-nickname="{{seg.nickname}}">@{{seg.nickname}}</text>
|
||||
<text wx:elif="{{seg.type === 'linkTag'}}" class="link-tag" bindtap="onLinkTagTap" data-url="{{seg.url}}" data-label="{{seg.label}}" data-tag-type="{{seg.tagType}}" data-page-path="{{seg.pagePath}}" data-tag-id="{{seg.tagId}}" data-app-id="{{seg.appId}}" data-mp-key="{{seg.mpKey}}">#{{seg.label}}</text>
|
||||
<image wx:elif="{{seg.type === 'image'}}" class="content-image" src="{{seg.src}}" mode="widthFix" show-menu-by-longpress bindtap="onImageTap" data-src="{{seg.src}}"></image>
|
||||
</block>
|
||||
</view>
|
||||
@@ -85,18 +85,21 @@
|
||||
<!-- 分享操作区 -->
|
||||
<view class="action-section">
|
||||
<view class="action-row-inline">
|
||||
<view class="action-btn-inline btn-share-inline" bindtap="onShareTimelineTap">
|
||||
<button class="action-btn-inline btn-share-inline" open-type="share">
|
||||
<text class="action-icon-small">📣</text>
|
||||
<text class="action-text-small">分享到朋友圈</text>
|
||||
</view>
|
||||
<view class="action-btn-inline btn-poster-inline" bindtap="generatePoster">
|
||||
<text class="action-icon-small">🖼️</text>
|
||||
<text class="action-text-small">生成海报</text>
|
||||
</view>
|
||||
<view class="action-btn-inline btn-gift-inline" bindtap="showGiftShareModal" wx:if="{{isLoggedIn}}">
|
||||
<text class="action-text-small">分享给好友</text>
|
||||
</button>
|
||||
<view class="action-btn-inline btn-gift-inline" bindtap="handleGiftPay" wx:if="{{!auditMode}}">
|
||||
<text class="action-icon-small">🎁</text>
|
||||
<text class="action-text-small">代付分享</text>
|
||||
</view>
|
||||
<view class="action-btn-inline btn-poster-inline" bindtap="showPosterModal">
|
||||
<text class="action-icon-small">🖼️</text>
|
||||
<text class="action-text-small">生成海报</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="share-tip-inline" wx:if="{{!auditMode}}">
|
||||
<text class="share-tip-text">分享后好友购买,你可获得 90% 收益</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -106,7 +109,7 @@
|
||||
<!-- 预览内容 + 付费墙 - 未登录 -->
|
||||
<view class="article preview" wx:if="{{accessState === 'locked_not_login'}}">
|
||||
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
|
||||
<text user-select>{{item}}</text>
|
||||
{{item}}
|
||||
</view>
|
||||
|
||||
<!-- 渐变遮罩 -->
|
||||
@@ -157,7 +160,7 @@
|
||||
<!-- 预览内容 + 付费墙 - 已登录未购买 -->
|
||||
<view class="article preview" wx:if="{{accessState === 'locked_not_purchased'}}">
|
||||
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
|
||||
<text user-select>{{item}}</text>
|
||||
{{item}}
|
||||
</view>
|
||||
|
||||
<!-- 渐变遮罩 -->
|
||||
@@ -166,18 +169,18 @@
|
||||
<!-- 付费墙 - 已登录未购买 -->
|
||||
<view class="paywall">
|
||||
<view class="paywall-icon">🔒</view>
|
||||
<text class="paywall-title">解锁完整内容</text>
|
||||
<text class="paywall-desc">已阅读50%,购买后继续阅读</text>
|
||||
<text class="paywall-title" wx:if="{{!auditMode}}">解锁完整内容</text>
|
||||
<text class="paywall-title" wx:else>完整内容即将开放</text>
|
||||
<text class="paywall-desc" wx:if="{{!auditMode}}">已阅读50%,购买后继续阅读</text>
|
||||
<text class="paywall-desc" wx:else>该内容正在准备中,敬请期待</text>
|
||||
|
||||
<!-- 购买选项 -->
|
||||
<view class="purchase-options">
|
||||
<!-- 购买本章 - 直接调起支付 -->
|
||||
<!-- 购买选项(审核模式隐藏) -->
|
||||
<view class="purchase-options" wx:if="{{!auditMode}}">
|
||||
<view class="purchase-btn purchase-section" bindtap="handlePurchaseSection">
|
||||
<text class="btn-label">购买本章</text>
|
||||
<text class="btn-price brand-color">¥{{section && section.price != null ? section.price : sectionPrice}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 解锁全书 - 只有购买超过3章才显示 -->
|
||||
<view class="purchase-btn purchase-fullbook" bindtap="handlePurchaseFullBook" wx:if="{{purchasedCount >= 3}}">
|
||||
<view class="btn-left">
|
||||
<text class="btn-sparkle">✨</text>
|
||||
@@ -190,12 +193,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="paywall-tip">分享给好友一起学习,还能赚取佣金</text>
|
||||
<!-- 代付分享:让好友帮我买 -->
|
||||
<view class="gift-share-row" bindtap="showGiftShareModal" wx:if="{{isLoggedIn}}">
|
||||
<text class="gift-share-icon">🎁</text>
|
||||
<text class="gift-share-text">找好友代付</text>
|
||||
</view>
|
||||
<text class="paywall-tip" wx:if="{{!auditMode}}">分享给好友一起学习,还能赚取佣金</text>
|
||||
</view>
|
||||
|
||||
<!-- 章节导航 -->
|
||||
@@ -232,7 +230,7 @@
|
||||
<!-- 错误状态 - 网络异常 -->
|
||||
<view class="article preview" wx:if="{{accessState === 'error'}}">
|
||||
<view class="paragraph" wx:for="{{previewParagraphs}}" wx:key="index" wx:if="{{item}}">
|
||||
<text user-select>{{item}}</text>
|
||||
{{item}}
|
||||
</view>
|
||||
|
||||
<!-- 渐变遮罩 -->
|
||||
@@ -251,6 +249,14 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享提示浮层(阅读20%后下拉触发) -->
|
||||
<view class="share-float-tip {{showShareTip ? 'show' : ''}}" wx:if="{{showShareTip && !auditMode}}">
|
||||
<text class="share-float-icon">💰</text>
|
||||
<text class="share-float-text">分享给好友,好友购买你可获得 90% 收益</text>
|
||||
<button class="share-float-btn" open-type="share">立即分享</button>
|
||||
<view class="share-float-close" bindtap="closeShareTip">✕</view>
|
||||
</view>
|
||||
|
||||
<!-- 海报生成弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showPosterModal}}" bindtap="closePosterModal">
|
||||
<view class="modal-content poster-modal" catchtap="stopPropagation">
|
||||
@@ -306,8 +312,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 右下角悬浮分享按钮 -->
|
||||
<button class="fab-share" open-type="share">
|
||||
<image class="fab-icon" src="/assets/icons/share.svg" mode="aspectFit"></image>
|
||||
</button>
|
||||
<!-- 右下角悬浮按钮 - 分享到朋友圈 -->
|
||||
<view class="fab-share" bindtap="shareToMoments">
|
||||
<text class="fab-moments-icon">🌐</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
Reference in New Issue
Block a user