/** * Soul创业派对 - 目录页 * 开发: 卡若 * 技术支持: 存客宝 * 数据: 完整真实文章标题 */ const app = getApp() const { trackClick } = require('../../utils/trackClick') Page({ data: { // 系统信息 statusBarHeight: 44, navBarHeight: 88, // 用户状态 isLoggedIn: false, hasFullBook: false, isVip: false, purchasedSections: [], // 懒加载:篇章列表(不含章节详情),展开时再请求 chapters-by-part totalSections: 0, bookData: [], // 展开状态 expandedPart: null, // 已加载的篇章章节缓存 { partId: chapters } _loadedChapters: {}, // 固定模块 id -> mid(序言/尾声/附录,供 goToRead 传 mid) fixedSectionsMap: {}, // 附录 appendixList: [ { id: 'appendix-1', title: '附录1|Soul派对房精选对话' }, { id: 'appendix-2', title: '附录2|创业者自检清单' }, { id: 'appendix-3', title: '附录3|本书提到的工具和资源' } ], // book/parts 加载中 partsLoading: true, // 功能配置(搜索开关) searchEnabled: true }, onLoad() { wx.showShareMenu({ withShareTimeline: true }) this.setData({ statusBarHeight: app.globalData.statusBarHeight, navBarHeight: app.globalData.navBarHeight }) this.updateUserStatus() this.loadVipStatus() this.loadParts() this.loadFeatureConfig() }, async loadFeatureConfig() { try { if (app.globalData.features && typeof app.globalData.features.searchEnabled === 'boolean') { this.setData({ searchEnabled: app.globalData.features.searchEnabled }) return } const res = await app.getConfig() const features = (res && res.features) || {} const searchEnabled = features.searchEnabled !== false if (!app.globalData.features) app.globalData.features = {} app.globalData.features.searchEnabled = searchEnabled this.setData({ searchEnabled }) } catch (e) { this.setData({ searchEnabled: true }) } }, // 懒加载:仅拉取篇章列表 + totalSections + fixedSections(book/parts,不再用 all-chapters) async loadParts() { this.setData({ partsLoading: true }) try { const res = await app.request({ url: '/api/miniprogram/book/parts', silent: true }) let parts = [] let totalSections = 0 let fixedSections = [] if (res?.success && Array.isArray(res.parts) && res.parts.length > 0) { parts = res.parts totalSections = res.totalSections ?? 0 fixedSections = res.fixedSections || [] } const numbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'] const fixedMap = {} fixedSections.forEach(f => { fixedMap[f.id] = f.mid }) const appendixList = [ { id: 'appendix-1', title: '附录1|Soul派对房精选对话', mid: fixedMap['appendix-1'] }, { id: 'appendix-2', title: '附录2|创业者自检清单', mid: fixedMap['appendix-2'] }, { id: 'appendix-3', title: '附录3|本书提到的工具和资源', mid: fixedMap['appendix-3'] } ] const bookData = parts.map((p, idx) => ({ id: p.id, number: numbers[idx] || String(idx + 1), title: p.title, subtitle: p.subtitle || '', chapterCount: p.chapterCount || 0, chapters: [] // 展开时懒加载 })) app.globalData.totalSections = totalSections this.setData({ bookData, totalSections, fixedSectionsMap: fixedMap, appendixList, _loadedChapters: {}, partsLoading: false }) } catch (e) { console.log('[Chapters] 加载篇章失败:', e) this.setData({ bookData: [], totalSections: 0, partsLoading: false }) } }, // 展开时懒加载该篇章的章节(含 mid,供阅读页 by-mid 请求) async loadChaptersByPart(partId) { if (this.data._loadedChapters[partId]) return try { const res = await app.request({ url: `/api/miniprogram/book/chapters-by-part?partId=${encodeURIComponent(partId)}`, silent: true }) const rows = (res && res.data) || [] const chMap = new Map() rows.forEach(r => { const cid = r.chapterId || r.chapter_id || 'chapter-1' if (!chMap.has(cid)) { chMap.set(cid, { id: cid, title: r.chapterTitle || r.chapter_title || '未分类', sections: [] }) } const ch = chMap.get(cid) const isPremium = r.editionPremium === true || r.edition_premium === true || r.edition_premium === 1 || r.edition_premium === '1' ch.sections.push({ id: r.id, mid: r.mid ?? r.MID ?? 0, title: r.sectionTitle || r.section_title || r.title || '', isFree: r.isFree === true || (r.price !== undefined && r.price === 0), price: r.price ?? 1, isNew: r.isNew === true || r.is_new === true, isPremium }) }) const chapters = Array.from(chMap.values()) const loaded = { ...this.data._loadedChapters, [partId]: chapters } const bookData = this.data.bookData.map(p => p.id === partId ? { ...p, chapters } : p ) const bookDataFlat = app.globalData.bookData || [] rows.forEach(r => { const idx = bookDataFlat.findIndex(c => c.id === r.id) if (idx >= 0) bookDataFlat[idx] = { ...bookDataFlat[idx], ...r } else bookDataFlat.push(r) }) app.globalData.bookData = bookDataFlat wx.setStorage({ key: 'bookData', data: bookDataFlat }) // 异步写入,避免阻塞主线程 this.setData({ bookData, _loadedChapters: loaded }) } catch (e) { console.log('[Chapters] 加载章节失败:', e) } }, onPullDownRefresh() { this.loadParts() .then(() => wx.stopPullDownRefresh()) .catch(() => wx.stopPullDownRefresh()) }, onShow() { // 设置TabBar选中状态 if (typeof this.getTabBar === 'function' && this.getTabBar()) { const tabBar = this.getTabBar() if (tabBar.updateSelected) { tabBar.updateSelected() } else { tabBar.setData({ selected: 1 }) } } this.updateUserStatus() this.loadVipStatus() }, // 拉取 VIP 状态(isVip=会员,hasFullBook=9.9 买断) async loadVipStatus() { const userId = app.globalData.userInfo?.id if (!userId) return try { const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true, timeout: 3000 }) if (res?.success) { app.globalData.isVip = !!res.data?.isVip app.globalData.vipExpireDate = res.data?.expireDate || '' this.setData({ isVip: app.globalData.isVip }) const userInfo = app.globalData.userInfo || {} userInfo.isVip = app.globalData.isVip userInfo.vipExpireDate = app.globalData.vipExpireDate wx.setStorageSync('userInfo', userInfo) } } catch (e) { // 静默失败不影响目录展示 } }, // 更新用户状态 updateUserStatus() { const { isLoggedIn, hasFullBook, purchasedSections, isVip } = app.globalData this.setData({ isLoggedIn, hasFullBook, purchasedSections, isVip }) }, // 切换展开状态,展开时懒加载该篇章章节 async togglePart(e) { trackClick('chapters', 'tab_click', e.currentTarget.dataset.id || '篇章') const partId = e.currentTarget.dataset.id const isExpanding = this.data.expandedPart !== partId this.setData({ expandedPart: isExpanding ? partId : null }) if (isExpanding) await this.loadChaptersByPart(partId) }, // 跳转到阅读页(优先传 mid,与分享逻辑一致) goToRead(e) { const id = e.currentTarget.dataset.id const mid = e.currentTarget.dataset.mid trackClick('chapters', 'card_click', id || '章节') const q = mid ? `mid=${mid}` : `id=${id}` wx.navigateTo({ url: `/pages/read/read?${q}` }) }, // 检查是否已购买 hasPurchased(sectionId, isPremium) { if (this.data.isVip) return true if (!isPremium && this.data.hasFullBook) return true return this.data.purchasedSections.includes(sectionId) }, // 返回首页 goBack() { wx.switchTab({ url: '/pages/index/index' }) }, // 跳转到搜索页 goToSearch() { if (!this.data.searchEnabled) return trackClick('chapters', 'nav_click', '搜索') wx.navigateTo({ url: '/pages/search/search' }) }, onShareAppMessage() { const ref = app.getMyReferralCode() return { title: 'Soul创业派对 - 目录', path: ref ? `/pages/chapters/chapters?ref=${ref}` : '/pages/chapters/chapters' } }, onShareTimeline() { const ref = app.getMyReferralCode() return { title: 'Soul创业派对 - 真实商业故事', query: ref ? `ref=${ref}` : '' } } })