- 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
179 lines
5.6 KiB
JavaScript
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()
|
|
}
|
|
})
|