重构小程序图标组件,替换传统 emoji 为 SVG 图标,提升视觉一致性和可维护性。更新多个页面以使用新图标组件,优化用户界面体验。同时,调整了数据加载逻辑,确保更高效的状态管理和用户交互。
This commit is contained in:
@@ -91,16 +91,21 @@ Page({
|
||||
async onLoad(options) {
|
||||
wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'] })
|
||||
|
||||
// 预加载 config:linkTags、auditMode 等(阅读页直接进入时需主动拉取最新审核状态)
|
||||
app.request({ url: '/api/miniprogram/config', silent: true }).then(cfg => {
|
||||
// 预加载:core+auditMode(getConfig)+ read-extras 懒加载(linkTags、linkedMiniprograms)
|
||||
Promise.all([
|
||||
app.getConfig(),
|
||||
app.getReadExtras()
|
||||
]).then(([cfg, extras]) => {
|
||||
if (cfg) {
|
||||
if (Array.isArray(cfg.linkTags)) app.globalData.linkTagsConfig = cfg.linkTags
|
||||
if (Array.isArray(cfg.linkedMiniprograms)) app.globalData.linkedMiniprograms = cfg.linkedMiniprograms
|
||||
const mp = (cfg && cfg.mpConfig) || {}
|
||||
const auditMode = !!mp.auditMode
|
||||
app.globalData.auditMode = auditMode
|
||||
if (typeof this.setData === 'function') this.setData({ auditMode })
|
||||
}
|
||||
if (extras && Array.isArray(extras.linkTags)) {
|
||||
app.globalData.linkTagsConfig = extras.linkTags
|
||||
app.globalData.linkedMiniprograms = extras.linkedMiniprograms || []
|
||||
}
|
||||
}).catch(() => {})
|
||||
|
||||
// 支持 scene(扫码)、mid、id、ref、gift(代付)
|
||||
@@ -120,21 +125,15 @@ Page({
|
||||
|
||||
console.log("页面:",mid);
|
||||
|
||||
// mid 有值但无 id 时,从 bookData 或 API 解析 id
|
||||
// 兼容:mid 有值但无 id 时,用 by-mid 解析 id;有 id 无 mid 时,后续用 by-id 请求
|
||||
if (mid && !id) {
|
||||
const bookData = app.globalData.bookData || []
|
||||
const ch = bookData.find(c => c.mid == mid || (c.mid && Number(c.mid) === Number(mid)))
|
||||
if (ch?.id) {
|
||||
id = ch.id
|
||||
} else {
|
||||
try {
|
||||
const resolveUrl = `/api/miniprogram/book/chapter/by-mid/${mid}`
|
||||
const uid = app.globalData.userInfo?.id
|
||||
const chRes = await app.request({ url: uid ? resolveUrl + '?userId=' + encodeURIComponent(uid) : resolveUrl, silent: true })
|
||||
if (chRes && chRes.id) id = chRes.id
|
||||
} catch (e) {
|
||||
console.warn('[Read] by-mid 解析失败:', e)
|
||||
}
|
||||
try {
|
||||
const resolveUrl = `/api/miniprogram/book/chapter/by-mid/${mid}`
|
||||
const uid = app.globalData.userInfo?.id
|
||||
const chRes = await app.request({ url: uid ? resolveUrl + '?userId=' + encodeURIComponent(uid) : resolveUrl, silent: true })
|
||||
if (chRes && chRes.id) id = chRes.id
|
||||
} catch (e) {
|
||||
console.warn('[Read] by-mid 解析失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +185,8 @@ Page({
|
||||
readingTracker.init(id)
|
||||
}
|
||||
|
||||
// 5. 加载导航
|
||||
this.loadNavigation(id)
|
||||
// 5. 导航:文章详情已带 prev/next
|
||||
this._applyPrevNext(chapterRes)
|
||||
|
||||
} catch (e) {
|
||||
console.error('[Read] 初始化失败:', e)
|
||||
@@ -342,7 +341,7 @@ Page({
|
||||
return titles[id] || `章节 ${id}`
|
||||
},
|
||||
|
||||
// 根据 id/mid 构造章节接口路径(优先使用 mid)。必须带 userId 才能让后端正确判断付费用户并返回完整内容
|
||||
// 根据 id/mid 构造章节接口路径:优先 mid(by-mid),否则用 id(by-id,兼容旧链接)
|
||||
_getChapterUrl(params = {}) {
|
||||
const { id, mid } = params
|
||||
const finalMid = (mid !== undefined && mid !== null) ? mid : this.data.sectionMid
|
||||
@@ -351,7 +350,7 @@ Page({
|
||||
url = `/api/miniprogram/book/chapter/by-mid/${finalMid}`
|
||||
} else {
|
||||
const finalId = id || this.data.sectionId
|
||||
url = `/api/miniprogram/book/chapter/${finalId}`
|
||||
url = `/api/miniprogram/book/chapter/by-id/${encodeURIComponent(finalId)}`
|
||||
}
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (userId) url += (url.includes('?') ? '&' : '?') + 'userId=' + encodeURIComponent(userId)
|
||||
@@ -446,47 +445,21 @@ Page({
|
||||
},
|
||||
|
||||
|
||||
// 加载导航:基于后端章节顺序计算上一篇/下一篇
|
||||
async loadNavigation(id) {
|
||||
try {
|
||||
// 优先使用全局缓存的 bookData
|
||||
let chapters = app.globalData.bookData || []
|
||||
if (!chapters || !Array.isArray(chapters) || chapters.length === 0) {
|
||||
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
||||
chapters = (res && (res.data || res.chapters)) || []
|
||||
}
|
||||
if (!chapters || chapters.length === 0) {
|
||||
this.setData({ prevSection: null, nextSection: null })
|
||||
return
|
||||
}
|
||||
// 过滤掉没有 id 的记录,并按 sort_order + id 排序
|
||||
const ordered = chapters
|
||||
.filter(c => c.id)
|
||||
.sort((a, b) => {
|
||||
const soA = typeof a.sort_order === 'number' ? a.sort_order : (typeof a.sortOrder === 'number' ? a.sortOrder : 0)
|
||||
const soB = typeof b.sort_order === 'number' ? b.sort_order : (typeof b.sortOrder === 'number' ? b.sortOrder : 0)
|
||||
if (soA !== soB) return soA - soB
|
||||
return String(a.id).localeCompare(String(b.id), 'zh-Hans-CN')
|
||||
})
|
||||
const index = ordered.findIndex(c => String(c.id) === String(id))
|
||||
const prev = index > 0 ? ordered[index - 1] : null
|
||||
const next = index >= 0 && index < ordered.length - 1 ? ordered[index + 1] : null
|
||||
this.setData({
|
||||
prevSection: prev ? {
|
||||
id: prev.id,
|
||||
mid: prev.mid ?? prev.MID ?? null,
|
||||
title: prev.section_title || prev.sectionTitle || prev.title || this.getSectionTitle(prev.id),
|
||||
} : null,
|
||||
nextSection: next ? {
|
||||
id: next.id,
|
||||
mid: next.mid ?? next.MID ?? null,
|
||||
title: next.section_title || next.sectionTitle || next.title || this.getSectionTitle(next.id),
|
||||
} : null,
|
||||
})
|
||||
} catch (e) {
|
||||
console.warn('[Read] loadNavigation failed:', e)
|
||||
this.setData({ prevSection: null, nextSection: null })
|
||||
}
|
||||
_applyPrevNext(res) {
|
||||
const prev = res?.prev
|
||||
const next = res?.next
|
||||
this.setData({
|
||||
prevSection: prev ? {
|
||||
id: prev.id,
|
||||
mid: prev.mid ?? null,
|
||||
title: prev.title || this.getSectionTitle(prev.id),
|
||||
} : null,
|
||||
nextSection: next ? {
|
||||
id: next.id,
|
||||
mid: next.mid ?? null,
|
||||
title: next.title || this.getSectionTitle(next.id),
|
||||
} : null,
|
||||
})
|
||||
},
|
||||
|
||||
// 返回(从分享进入无栈时回首页)
|
||||
@@ -1304,14 +1277,15 @@ Page({
|
||||
|
||||
try {
|
||||
const ctx = wx.createCanvasContext('posterCanvas', this)
|
||||
const { section, contentParagraphs, sectionId } = this.data
|
||||
const { section, contentParagraphs, sectionId, sectionMid } = this.data
|
||||
const userInfo = app.globalData.userInfo
|
||||
const userId = userInfo?.id || ''
|
||||
|
||||
// 获取小程序码(带推荐人参数)
|
||||
// 获取小程序码(带推荐人参数,优先 mid 与新链接一致)
|
||||
let qrcodeImage = null
|
||||
try {
|
||||
const scene = userId ? `id=${sectionId}&ref=${userId.slice(0,10)}` : `id=${sectionId}`
|
||||
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
|
||||
const scene = userId ? `${q}&ref=${userId.slice(0,10)}` : q
|
||||
const qrRes = await app.request('/api/miniprogram/qrcode', {
|
||||
method: 'POST',
|
||||
data: { scene, page: 'pages/read/read', width: 280 }
|
||||
@@ -1545,8 +1519,7 @@ Page({
|
||||
readingTracker.init(this.data.sectionId)
|
||||
}
|
||||
|
||||
// 加载导航
|
||||
this.loadNavigation(this.data.sectionId)
|
||||
this._applyPrevNext(chapterRes)
|
||||
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '加载成功', icon: 'success' })
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="nav-btn nav-end" wx:else>
|
||||
<text class="btn-end-text">已是最后一篇 🎉</text>
|
||||
<text class="btn-end-text">已是最后一篇</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -95,15 +95,15 @@
|
||||
<view class="action-section">
|
||||
<view class="action-row-inline">
|
||||
<view class="action-btn-inline btn-share-inline" bindtap="onShareTimelineTap">
|
||||
<text class="action-icon-small">📣</text>
|
||||
<icon name="megaphone" size="32" color="#00CED1" customClass="action-icon-small"></icon>
|
||||
<text class="action-text-small">分享到朋友圈</text>
|
||||
</view>
|
||||
<view class="action-btn-inline btn-poster-inline" bindtap="generatePoster">
|
||||
<text class="action-icon-small">🖼️</text>
|
||||
<icon name="image" size="32" color="#00CED1" customClass="action-icon-small"></icon>
|
||||
<text class="action-text-small">生成海报</text>
|
||||
</view>
|
||||
<view class="action-btn-inline btn-gift-inline" bindtap="showGiftShareModal" wx:if="{{isLoggedIn && !auditMode}}">
|
||||
<text class="action-icon-small">🎁</text>
|
||||
<icon name="gift" size="32" color="#00CED1" customClass="action-icon-small"></icon>
|
||||
<text class="action-text-small">代付分享</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -126,7 +126,7 @@
|
||||
|
||||
<!-- 付费墙 - 未登录 -->
|
||||
<view class="paywall">
|
||||
<view class="paywall-icon">🔒</view>
|
||||
<view class="paywall-icon"><icon name="lock" size="80" color="#00CED1"></icon></view>
|
||||
<text class="paywall-title">登录后继续阅读</text>
|
||||
<text class="paywall-desc">已阅读50%,登录后查看完整内容</text>
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="nav-btn nav-end" wx:else>
|
||||
<text class="btn-end-text">已是最后一篇 🎉</text>
|
||||
<text class="btn-end-text">已是最后一篇</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -177,7 +177,7 @@
|
||||
|
||||
<!-- 付费墙 - 已登录未购买 -->
|
||||
<view class="paywall">
|
||||
<view class="paywall-icon">🔒</view>
|
||||
<view class="paywall-icon"><icon name="lock" size="80" color="#00CED1"></icon></view>
|
||||
<text class="paywall-title">解锁完整内容</text>
|
||||
<text class="paywall-desc">已阅读50%,购买后继续阅读</text>
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
<!-- 解锁全书 - 只有购买超过3章才显示 -->
|
||||
<view class="purchase-btn purchase-fullbook" bindtap="handlePurchaseFullBook" wx:if="{{purchasedCount >= 3}}">
|
||||
<view class="btn-left">
|
||||
<text class="btn-sparkle">✨</text>
|
||||
<icon name="sparkles" size="32" color="#FFD700" customClass="btn-sparkle"></icon>
|
||||
<text class="btn-label">解锁全部 {{totalSections}} 章</text>
|
||||
</view>
|
||||
<view class="btn-right">
|
||||
@@ -206,7 +206,7 @@
|
||||
<text class="paywall-tip" wx:if="{{!auditMode}}">分享给好友一起学习,还能赚取佣金</text>
|
||||
<!-- 代付分享:帮好友购买(审核模式隐藏) -->
|
||||
<view class="gift-share-row" bindtap="showGiftShareModal" wx:if="{{isLoggedIn && !auditMode}}">
|
||||
<text class="gift-share-icon">🎁</text>
|
||||
<icon name="gift" size="40" color="#00CED1" customClass="gift-share-icon"></icon>
|
||||
<text class="gift-share-text">代付分享</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -236,7 +236,7 @@
|
||||
</view>
|
||||
</view>
|
||||
<view class="nav-btn nav-end" wx:else>
|
||||
<text class="btn-end-text">已是最后一篇 🎉</text>
|
||||
<text class="btn-end-text">已是最后一篇</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -279,7 +279,7 @@
|
||||
|
||||
<view class="poster-actions">
|
||||
<view class="poster-btn btn-save" bindtap="savePoster">
|
||||
<text class="btn-icon">💾</text>
|
||||
<icon name="save" size="36" color="#8e8e93" customClass="btn-icon"></icon>
|
||||
<text>保存到相册</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -292,7 +292,7 @@
|
||||
<view class="modal-overlay" wx:if="{{showLoginModal}}" bindtap="closeLoginModal">
|
||||
<view class="modal-content login-modal" catchtap="stopPropagation">
|
||||
<view class="modal-close" bindtap="closeLoginModal">✕</view>
|
||||
<view class="login-icon">🔐</view>
|
||||
<view class="login-icon"><icon name="lock" size="80" color="#00CED1"></icon></view>
|
||||
<text class="login-title">登录 Soul创业派对</text>
|
||||
<text class="login-desc">登录后可购买章节、解锁更多内容</text>
|
||||
|
||||
@@ -321,6 +321,6 @@
|
||||
|
||||
<!-- 右下角悬浮按钮 - 分享到朋友圈 -->
|
||||
<view class="fab-share" bindtap="shareToMoments">
|
||||
<text class="fab-moments-icon">🌐</text>
|
||||
<icon name="globe" size="40" color="#ffffff" customClass="fab-moments-icon"></icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
Reference in New Issue
Block a user