重构小程序图标组件,替换传统 emoji 为 SVG 图标,提升视觉一致性和可维护性。更新多个页面以使用新图标组件,优化用户界面体验。同时,调整了数据加载逻辑,确保更高效的状态管理和用户交互。

This commit is contained in:
Alex-larget
2026-03-18 16:00:57 +08:00
parent 46f94a9c81
commit c55e54efbd
62 changed files with 2033 additions and 1270 deletions

View File

@@ -91,16 +91,21 @@ Page({
async onLoad(options) {
wx.showShareMenu({ menus: ['shareAppMessage', 'shareTimeline'] })
// 预加载 configlinkTags、auditMode 等(阅读页直接进入时需主动拉取最新审核状态
app.request({ url: '/api/miniprogram/config', silent: true }).then(cfg => {
// 预加载core+auditModegetConfig+ 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 构造章节接口路径优先 midby-mid否则用 idby-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' })

View File

@@ -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>