Files
soul-yongping/miniprogram/pages/reading-records/reading-records.js
卡若 fa3da12b16 feat: 小程序阅读记录与资料链路、管理端用户规则、API/VIP/推荐与运营脚本
- miniprogram: reading-records、imageUrl/mpNavigate、多页资料与 VIP 展示调整
- soul-admin: Users/Settings/UserDetailModal、dist 构建产物更新
- soul-api: user/vip/referral/ckb/db、MBTI 头像管理、user_rule_completion、迁移 SQL
- .cursor: karuo-party 与飞书文档;.gitignore 忽略 .tmp_skill_bundle

Made-with: Cursor
2026-03-23 18:38:23 +08:00

179 lines
5.6 KiB
JavaScript

/**
* 阅读记录:最近阅读 + 已读章节(与「我的」数据源一致)
*/
const app = getApp()
const { cleanSingleLineField } = require('../../utils/contentParser.js')
function titleFromBookData(sectionId, bookFlat) {
const row = bookFlat.find((s) => s.id === sectionId)
return cleanSingleLineField(
row?.sectionTitle || row?.section_title || row?.title || row?.chapterTitle || ''
) || `章节 ${sectionId}`
}
function midFromBookData(sectionId, bookFlat) {
const row = bookFlat.find((s) => s.id === sectionId)
return row?.mid ?? row?.MID ?? 0
}
function mergeRecentFromLocal(apiList) {
const normalized = Array.isArray(apiList)
? apiList.map((item) => ({
id: item.id,
mid: item.mid,
title: cleanSingleLineField(item.title || '') || `章节 ${item.id}`
}))
: []
if (normalized.length > 0) return normalized
try {
const progressData = wx.getStorageSync('reading_progress') || {}
const bookFlat = Array.isArray(app.globalData.bookData) ? app.globalData.bookData : []
return Object.keys(progressData)
.map((id) => ({
id,
ts: progressData[id]?.lastOpenAt || progressData[id]?.last_open_at || 0
}))
.filter((e) => e.id)
.sort((a, b) => b.ts - a.ts)
.slice(0, 20)
.map((e) => ({
id: e.id,
mid: midFromBookData(e.id, bookFlat),
title: titleFromBookData(e.id, bookFlat)
}))
} catch (e) {
return []
}
}
Page({
data: {
statusBarHeight: 44,
isLoggedIn: false,
focus: 'all',
recentList: [],
readAllList: [],
recentSectionTitle: '最近阅读',
readSectionTitle: '已读章节'
},
onLoad(options) {
const focus = (options.focus === 'recent' || options.focus === 'read') ? options.focus : 'all'
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44,
focus
})
this._applyMpUiTitles()
},
onShow() {
this.setData({ isLoggedIn: !!(app.globalData.isLoggedIn && app.globalData.userInfo?.id) })
this._applyMpUiTitles()
if (this.data.isLoggedIn) this.loadData()
},
_applyMpUiTitles() {
const my = app.globalData.configCache?.mpConfig?.mpUi?.myPage || {}
this.setData({
recentSectionTitle: my.recentReadTitle || '最近阅读',
readSectionTitle: my.readStatLabel || '已读章节'
})
},
async _ensureBookFlat() {
let flat = Array.isArray(app.globalData.bookData) ? app.globalData.bookData : []
if (flat.length) return flat
try {
const r = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const list = r?.data
if (Array.isArray(list) && list.length) {
app.globalData.bookData = list
return list
}
} catch (_) {}
return []
},
async loadData() {
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request({
url: `/api/miniprogram/user/dashboard-stats?userId=${encodeURIComponent(userId)}`,
silent: true
})
const bookFlat = await this._ensureBookFlat()
let recent = []
let readIds = []
if (res?.success && res.data) {
const apiRecent = Array.isArray(res.data.recentChapters) ? res.data.recentChapters : []
recent = mergeRecentFromLocal(apiRecent)
readIds = Array.isArray(res.data.readSectionIds) ? res.data.readSectionIds.filter(Boolean) : []
} else {
recent = mergeRecentFromLocal([])
}
try {
const progressData = wx.getStorageSync('reading_progress') || {}
const fromKeys = Object.keys(progressData).filter(Boolean)
const stored = wx.getStorageSync('readSectionIds')
const fromStored = Array.isArray(stored) ? stored.filter(Boolean) : []
const fromGlobal = Array.isArray(app.globalData.readSectionIds)
? app.globalData.readSectionIds.filter(Boolean)
: []
readIds = [...new Set([...readIds, ...fromGlobal, ...fromStored, ...fromKeys])]
} catch (_) {}
if (readIds.length === 0 && recent.length > 0) {
readIds = recent.map((r) => r.id)
}
const readAllList = readIds.map((id) => ({
id,
mid: midFromBookData(id, bookFlat),
title: titleFromBookData(id, bookFlat)
}))
this.setData({ recentList: recent, readAllList })
} catch (e) {
console.warn('[reading-records]', e)
try {
const bookFlat = await this._ensureBookFlat()
let readIds = Array.isArray(app.globalData.readSectionIds) ? [...app.globalData.readSectionIds] : []
if (!readIds.length) {
try {
const stored = wx.getStorageSync('readSectionIds')
if (Array.isArray(stored)) readIds = [...stored]
} catch (_) {}
}
const recent = mergeRecentFromLocal([])
if (!readIds.length && recent.length) readIds = recent.map((r) => r.id)
const readAllList = readIds.map((id) => ({
id,
mid: midFromBookData(id, bookFlat),
title: titleFromBookData(id, bookFlat)
}))
this.setData({ recentList: recent, readAllList })
} catch (_) {
this.setData({ recentList: [], readAllList: [] })
}
}
},
goRead(e) {
const id = e.currentTarget.dataset.id
const mid = e.currentTarget.dataset.mid
if (!id) return
const q = mid ? `mid=${mid}` : `id=${id}`
wx.navigateTo({ url: `/pages/read/read?${q}` })
},
goChapters() {
wx.switchTab({ url: '/pages/chapters/chapters' })
},
goLogin() {
wx.switchTab({ url: '/pages/my/my' })
},
goBack() {
getApp().goBackOrToHome()
}
})