Files
soul-yongping/miniprogram/pages/chapters/chapters.js
Alex-larget a8c7dc9306 Merge branch 'yongxu' into devlop
# Conflicts:
#	.cursor/meeting/README.md   resolved by yongxu version
#	.gitignore   resolved by yongxu version
#	miniprogram/pages/index/index.js   resolved by yongxu version
#	miniprogram/pages/read/read.js   resolved by yongxu version
#	miniprogram/pages/read/read.wxml   resolved by yongxu version
#	soul-admin/dist/index.html   resolved by yongxu version
#	soul-admin/src/App.tsx   resolved by yongxu version
#	soul-admin/src/components/RichEditor.css   resolved by yongxu version
#	soul-admin/src/components/RichEditor.tsx   resolved by yongxu version
#	soul-admin/src/components/modules/user/UserDetailModal.tsx   resolved by yongxu version
#	soul-admin/src/layouts/AdminLayout.tsx   resolved by yongxu version
#	soul-admin/src/pages/chapters/ChaptersPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/content/ContentPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/dashboard/DashboardPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/FindPartnerPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/CKBConfigPanel.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/CKBStatsTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/FindPartnerTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/MatchPoolTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/MatchRecordsTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/MentorBookingTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/MentorTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/ResourceDockingTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/find-partner/tabs/TeamRecruitTab.tsx   resolved by yongxu version
#	soul-admin/src/pages/mentors/MentorsPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/referral-settings/ReferralSettingsPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/settings/SettingsPage.tsx   resolved by yongxu version
#	soul-admin/src/pages/users/UsersPage.tsx   resolved by yongxu version
#	soul-admin/tsconfig.tsbuildinfo   resolved by yongxu version
#	soul-api/internal/database/database.go   resolved by yongxu version
#	soul-api/internal/handler/admin_dashboard.go   resolved by yongxu version
#	soul-api/internal/handler/book.go   resolved by yongxu version
#	soul-api/internal/handler/ckb.go   resolved by yongxu version
#	soul-api/internal/handler/db_book.go   resolved by yongxu version
#	soul-api/internal/handler/db_person.go   resolved by yongxu version
#	soul-api/internal/handler/match_records.go   resolved by yongxu version
#	soul-api/internal/handler/user.go   resolved by yongxu version
#	soul-api/internal/model/chapter.go   resolved by yongxu version
#	soul-api/internal/model/person.go   resolved by yongxu version
#	soul-api/internal/router/router.go   resolved by yongxu version
#	开发文档/10、项目管理/运营与变更.md   resolved by yongxu version
#	开发文档/1、需求/需求汇总.md   resolved by yongxu version
#	开发文档/README.md   resolved by yongxu version
2026-03-10 20:20:59 +08:00

253 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Soul创业派对 - 目录页
* 开发: 卡若
* 技术支持: 存客宝
* 数据: 完整真实文章标题
*/
const app = getApp()
Page({
data: {
// 系统信息
statusBarHeight: 44,
navBarHeight: 88,
// 用户状态
isLoggedIn: false,
hasFullBook: false,
isVip: false,
purchasedSections: [],
// 书籍数据:以后台内容管理为准,仅用接口 /api/miniprogram/book/all-chapters 返回的数据
totalSections: 0,
bookData: [],
// 展开状态:默认不展开任何篇章,直接显示目录
expandedPart: null,
// 附录
appendixList: [
{ id: 'appendix-1', title: '附录1Soul派对房精选对话' },
{ id: 'appendix-2', title: '附录2创业者自检清单' },
{ id: 'appendix-3', title: '附录3本书提到的工具和资源' }
],
// 每日新增章节
dailyChapters: []
},
onLoad() {
wx.showShareMenu({ withShareTimeline: true })
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
navBarHeight: app.globalData.navBarHeight
})
this.updateUserStatus()
this.loadVipStatus()
this.loadChaptersOnce()
},
// 固定模块(序言、尾声、附录)不参与中间篇章
_isFixedPart(pt) {
if (!pt) return false
const p = String(pt).toLowerCase().replace(/[_\s|]/g, '')
return p.includes('序言') || p.includes('尾声') || p.includes('附录')
},
// 一次请求拉取全量目录,以后台内容管理为准;同时更新 totalSections / bookData / dailyChapters
async loadChaptersOnce() {
try {
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const rows = (res && res.data) || (res && res.chapters) || []
// 无数据时清空目录,避免展示旧数据
if (rows.length === 0) {
app.globalData.bookData = []
wx.setStorageSync('bookData', [])
this.setData({
bookData: [],
totalSections: 0,
dailyChapters: [],
expandedPart: null
})
return
}
const totalSections = res.total ?? rows.length
app.globalData.bookData = rows
wx.setStorageSync('bookData', rows)
// bookData过滤序言/尾声/附录,按 part 聚合,篇章顺序按 sort_order 与后台一致含「2026每日派对干货」等
const filtered = rows.filter(r => !this._isFixedPart(r.partTitle || r.part_title))
const partMap = new Map()
const numbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
filtered.forEach((r) => {
const pid = r.partId || r.part_id || 'part-1'
const cid = r.chapterId || r.chapter_id || 'chapter-1'
const sortOrder = r.sectionOrder ?? r.sort_order ?? 999999
if (!partMap.has(pid)) {
const partIdx = partMap.size
partMap.set(pid, {
id: pid,
number: numbers[partIdx] || String(partIdx + 1),
title: r.partTitle || r.part_title || '未分类',
subtitle: r.chapterTitle || r.chapter_title || '',
chapters: new Map(),
minSortOrder: sortOrder
})
}
const part = partMap.get(pid)
if (sortOrder < part.minSortOrder) part.minSortOrder = sortOrder
if (!part.chapters.has(cid)) {
part.chapters.set(cid, {
id: cid,
title: r.chapterTitle || r.chapter_title || '未分类',
sections: []
})
}
const ch = part.chapters.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 partList = Array.from(partMap.values())
partList.sort((a, b) => (a.minSortOrder ?? 999999) - (b.minSortOrder ?? 999999))
const bookData = partList.map((p, idx) => ({
id: p.id,
number: numbers[idx] || String(idx + 1),
title: p.title,
subtitle: p.subtitle,
chapters: Array.from(p.chapters.values())
}))
const baseSort = 62
const daily = rows
.filter(r => (r.sectionOrder ?? r.sort_order ?? 0) > baseSort)
.sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
.slice(0, 20)
.map(c => {
const d = new Date(c.updatedAt || c.updated_at || Date.now())
return {
id: c.id,
mid: c.mid ?? c.MID ?? 0,
title: c.section_title || c.title || c.sectionTitle,
price: c.price ?? 1,
dateStr: `${d.getMonth() + 1}/${d.getDate()}`
}
})
this.setData({
bookData,
totalSections,
dailyChapters: daily,
expandedPart: this.data.expandedPart
})
} catch (e) {
console.log('[Chapters] 加载目录失败:', e)
this.setData({ bookData: [], totalSections: 0 })
}
},
onPullDownRefresh() {
this.loadChaptersOnce().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 })
},
// 切换展开状态
togglePart(e) {
const partId = e.currentTarget.dataset.id
this.setData({
expandedPart: this.data.expandedPart === partId ? null : partId
})
},
// 跳转到阅读页(优先传 mid与分享逻辑一致
goToRead(e) {
const id = e.currentTarget.dataset.id
const mid = e.currentTarget.dataset.mid || app.getSectionMid(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() {
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}` : '' }
}
})