重构小程序图标组件,替换传统 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' })