2026-01-21 15:49:12 +08:00
|
|
|
|
/**
|
2026-01-25 19:37:59 +08:00
|
|
|
|
* Soul创业派对 - 首页
|
2026-01-21 15:49:12 +08:00
|
|
|
|
* 开发: 卡若
|
2026-02-24 14:35:58 +08:00
|
|
|
|
* 技术支持: 存客宝
|
2026-01-21 15:49:12 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
console.log('[Index] ===== 首页文件开始加载 =====')
|
|
|
|
|
|
|
2026-01-14 12:50:00 +08:00
|
|
|
|
const app = getApp()
|
|
|
|
|
|
|
|
|
|
|
|
Page({
|
|
|
|
|
|
data: {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 系统信息
|
2026-01-21 15:49:12 +08:00
|
|
|
|
statusBarHeight: 44,
|
|
|
|
|
|
navBarHeight: 88,
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户信息
|
2026-01-21 15:49:12 +08:00
|
|
|
|
isLoggedIn: false,
|
|
|
|
|
|
hasFullBook: false,
|
2026-02-24 14:35:58 +08:00
|
|
|
|
readCount: 0,
|
|
|
|
|
|
|
|
|
|
|
|
// 书籍数据
|
2026-01-21 15:49:12 +08:00
|
|
|
|
totalSections: 62,
|
|
|
|
|
|
bookData: [],
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 推荐章节
|
|
|
|
|
|
featuredSections: [
|
|
|
|
|
|
{ id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', tagClass: 'tag-free', part: '真实的人' },
|
|
|
|
|
|
{ id: '3.1', title: '3000万流水如何跑出来', tag: '热门', tagClass: 'tag-pink', part: '真实的行业' },
|
|
|
|
|
|
{ id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', tagClass: 'tag-purple', part: '真实的赚钱' }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 最新章节(动态计算)
|
2026-01-25 19:37:59 +08:00
|
|
|
|
latestSection: null,
|
|
|
|
|
|
latestLabel: '最新更新',
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 内容概览
|
|
|
|
|
|
partsList: [
|
|
|
|
|
|
{ id: 'part-1', number: '一', title: '真实的人', subtitle: '人与人之间的底层逻辑' },
|
|
|
|
|
|
{ id: 'part-2', number: '二', title: '真实的行业', subtitle: '电商、内容、传统行业解析' },
|
|
|
|
|
|
{ id: 'part-3', number: '三', title: '真实的错误', subtitle: '我和别人犯过的错' },
|
|
|
|
|
|
{ id: 'part-4', number: '四', title: '真实的赚钱', subtitle: '底层结构与真实案例' },
|
|
|
|
|
|
{ id: 'part-5', number: '五', title: '真实的社会', subtitle: '未来职业与商业生态' }
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
// 超级个体(VIP会员)
|
|
|
|
|
|
superMembers: [],
|
2026-02-23 14:07:41 +08:00
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 最新新增章节
|
|
|
|
|
|
latestChapters: [],
|
|
|
|
|
|
|
|
|
|
|
|
// 加载状态
|
2026-01-14 12:50:00 +08:00
|
|
|
|
loading: true
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-25 19:37:59 +08:00
|
|
|
|
onLoad(options) {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
console.log('[Index] ===== onLoad 触发 =====')
|
|
|
|
|
|
|
|
|
|
|
|
// 获取系统信息
|
2026-01-21 15:49:12 +08:00
|
|
|
|
this.setData({
|
|
|
|
|
|
statusBarHeight: app.globalData.statusBarHeight,
|
|
|
|
|
|
navBarHeight: app.globalData.navBarHeight
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理分享参数(推荐码绑定)
|
2026-01-25 19:37:59 +08:00
|
|
|
|
if (options && options.ref) {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
console.log('[Index] 检测到推荐码:', options.ref)
|
2026-01-25 19:37:59 +08:00
|
|
|
|
app.handleReferralCode({ query: options })
|
|
|
|
|
|
}
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 初始化数据
|
2026-01-21 15:49:12 +08:00
|
|
|
|
this.initData()
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
onShow() {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
console.log('[Index] onShow 触发')
|
|
|
|
|
|
|
|
|
|
|
|
// 设置TabBar选中状态
|
2026-01-21 15:49:12 +08:00
|
|
|
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
const tabBar = this.getTabBar()
|
|
|
|
|
|
console.log('[Index] TabBar 组件:', tabBar ? '已找到' : '未找到')
|
|
|
|
|
|
|
|
|
|
|
|
// 主动触发配置加载
|
|
|
|
|
|
if (tabBar && tabBar.loadFeatureConfig) {
|
|
|
|
|
|
console.log('[Index] 主动调用 TabBar.loadFeatureConfig()')
|
|
|
|
|
|
tabBar.loadFeatureConfig()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新选中状态
|
|
|
|
|
|
if (tabBar && tabBar.updateSelected) {
|
|
|
|
|
|
tabBar.updateSelected()
|
|
|
|
|
|
} else if (tabBar) {
|
|
|
|
|
|
tabBar.setData({ selected: 0 })
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('[Index] TabBar 组件未找到或 getTabBar 方法不存在')
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-02-24 14:35:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 更新用户状态
|
2026-01-21 15:49:12 +08:00
|
|
|
|
this.updateUserStatus()
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 初始化数据
|
2026-01-21 15:49:12 +08:00
|
|
|
|
async initData() {
|
|
|
|
|
|
this.setData({ loading: true })
|
|
|
|
|
|
try {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
await this.loadBookData()
|
|
|
|
|
|
await this.loadFeaturedFromServer()
|
|
|
|
|
|
this.loadSuperMembers()
|
|
|
|
|
|
this.loadLatestChapters()
|
2026-01-21 15:49:12 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('初始化失败:', e)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
this.setData({ loading: false })
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
async loadSuperMembers() {
|
2026-02-23 14:07:41 +08:00
|
|
|
|
try {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 优先加载VIP会员
|
|
|
|
|
|
let members = []
|
|
|
|
|
|
try {
|
2026-02-25 11:50:07 +08:00
|
|
|
|
const res = await app.request({ url: '/api/miniprogram/vip/members', silent: true })
|
2026-02-24 14:35:58 +08:00
|
|
|
|
if (res && res.success && res.data) {
|
|
|
|
|
|
members = res.data.filter(u => u.avatar || u.vip_avatar).slice(0, 4).map(u => ({
|
|
|
|
|
|
id: u.id, name: u.vip_name || u.nickname || '会员',
|
|
|
|
|
|
avatar: u.vip_avatar || u.avatar, isVip: true
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
// 不足4个则用有头像的普通用户补充
|
|
|
|
|
|
if (members.length < 4) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const dbRes = await app.request({ url: '/api/miniprogram/users?limit=20', silent: true })
|
|
|
|
|
|
if (dbRes && dbRes.success && dbRes.data) {
|
|
|
|
|
|
const existIds = new Set(members.map(m => m.id))
|
|
|
|
|
|
const extra = dbRes.data
|
|
|
|
|
|
.filter(u => u.avatar && u.nickname && !existIds.has(u.id))
|
|
|
|
|
|
.slice(0, 4 - members.length)
|
|
|
|
|
|
.map(u => ({ id: u.id, name: u.nickname, avatar: u.avatar, isVip: u.is_vip === 1 }))
|
|
|
|
|
|
members = members.concat(extra)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {}
|
2026-02-23 14:07:41 +08:00
|
|
|
|
}
|
2026-02-24 14:35:58 +08:00
|
|
|
|
this.setData({ superMembers: members })
|
|
|
|
|
|
} catch (e) { console.log('[Index] 加载超级个体失败:', e) }
|
2026-02-23 14:07:41 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 从服务端获取精选推荐(加权算法:阅读量50% + 时效30% + 付款率20%)和最新更新
|
|
|
|
|
|
async loadFeaturedFromServer() {
|
2026-02-23 14:07:41 +08:00
|
|
|
|
try {
|
2026-02-25 11:47:36 +08:00
|
|
|
|
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
2026-02-24 14:35:58 +08:00
|
|
|
|
const chapters = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
|
|
|
|
|
|
let featured = (res && res.featuredSections) ? res.featuredSections : []
|
|
|
|
|
|
// 服务端未返回精选时,从前端按更新时间取前3条有效章节作为回退
|
|
|
|
|
|
if (featured.length === 0 && chapters.length > 0) {
|
|
|
|
|
|
const valid = chapters.filter(c => {
|
|
|
|
|
|
const id = (c.id || '').toLowerCase()
|
|
|
|
|
|
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
|
|
|
|
|
return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
|
|
|
|
|
|
&& !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
|
|
|
|
|
})
|
|
|
|
|
|
featured = valid
|
|
|
|
|
|
.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
|
|
|
|
|
.slice(0, 5)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (featured.length > 0) {
|
|
|
|
|
|
this.setData({
|
|
|
|
|
|
featuredSections: featured.slice(0, 3).map(s => ({
|
|
|
|
|
|
id: s.id || s.section_id,
|
|
|
|
|
|
title: s.section_title || s.title,
|
|
|
|
|
|
part: (s.cleanPartTitle || s.part_title || '').replace(/[_||]/g, ' ').trim()
|
|
|
|
|
|
}))
|
|
|
|
|
|
})
|
2026-02-23 14:07:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 最新更新 = 按 updated_at 排序第1篇(排除序言/尾声/附录)
|
|
|
|
|
|
const validChapters = chapters.filter(c => {
|
|
|
|
|
|
const id = (c.id || '').toLowerCase()
|
|
|
|
|
|
const pt = (c.part_title || c.partTitle || '').toLowerCase()
|
|
|
|
|
|
return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
|
|
|
|
|
|
&& !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
|
|
|
|
|
|
})
|
|
|
|
|
|
if (validChapters.length > 0) {
|
|
|
|
|
|
validChapters.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
|
|
|
|
|
|
const latest = validChapters[0]
|
|
|
|
|
|
this.setData({
|
|
|
|
|
|
latestSection: {
|
|
|
|
|
|
id: latest.id || latest.section_id,
|
|
|
|
|
|
title: latest.section_title || latest.title,
|
|
|
|
|
|
part: latest.cleanPartTitle || latest.part_title || ''
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-01-25 19:37:59 +08:00
|
|
|
|
}
|
2026-02-21 20:44:38 +08:00
|
|
|
|
} catch (e) {
|
2026-02-24 14:35:58 +08:00
|
|
|
|
console.log('[Index] 从服务端加载推荐失败,使用默认:', e)
|
2026-02-21 20:44:38 +08:00
|
|
|
|
}
|
2026-01-25 19:37:59 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
async loadBookData() {
|
|
|
|
|
|
try {
|
2026-02-25 11:47:36 +08:00
|
|
|
|
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
2026-02-24 14:35:58 +08:00
|
|
|
|
if (res && (res.data || res.chapters)) {
|
|
|
|
|
|
const chapters = res.data || res.chapters || []
|
2026-02-23 14:07:41 +08:00
|
|
|
|
this.setData({
|
2026-02-24 14:35:58 +08:00
|
|
|
|
bookData: chapters,
|
|
|
|
|
|
totalSections: res.total || chapters.length || 62
|
2026-02-23 14:07:41 +08:00
|
|
|
|
})
|
2026-01-21 15:49:12 +08:00
|
|
|
|
}
|
2026-02-24 14:35:58 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('加载书籍数据失败:', e)
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 更新用户状态(已读数 = 用户实际打开过的章节数,仅统计有权限阅读的)
|
2026-01-21 15:49:12 +08:00
|
|
|
|
updateUserStatus() {
|
|
|
|
|
|
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
|
2026-02-24 14:35:58 +08:00
|
|
|
|
const readCount = Math.min(app.getReadCount(), this.data.totalSections || 62)
|
2026-01-21 15:49:12 +08:00
|
|
|
|
this.setData({
|
2026-02-24 14:35:58 +08:00
|
|
|
|
isLoggedIn,
|
|
|
|
|
|
hasFullBook,
|
|
|
|
|
|
readCount
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 跳转到目录
|
|
|
|
|
|
goToChapters() {
|
|
|
|
|
|
wx.switchTab({ url: '/pages/chapters/chapters' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 跳转到搜索页
|
|
|
|
|
|
goToSearch() {
|
|
|
|
|
|
wx.navigateTo({ url: '/pages/search/search' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 跳转到阅读页
|
2026-01-21 15:49:12 +08:00
|
|
|
|
goToRead(e) {
|
|
|
|
|
|
const id = e.currentTarget.dataset.id
|
|
|
|
|
|
wx.navigateTo({ url: `/pages/read/read?id=${id}` })
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 跳转到匹配页
|
|
|
|
|
|
goToMatch() {
|
|
|
|
|
|
wx.switchTab({ url: '/pages/match/match' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
goToVip() {
|
|
|
|
|
|
wx.navigateTo({ url: '/pages/vip/vip' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
goToSuperList() {
|
|
|
|
|
|
wx.switchTab({ url: '/pages/match/match' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
async loadLatestChapters() {
|
|
|
|
|
|
try {
|
2026-02-25 11:47:36 +08:00
|
|
|
|
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
|
2026-02-24 14:35:58 +08:00
|
|
|
|
const chapters = (res && res.data) || (res && res.chapters) || []
|
|
|
|
|
|
const latest = chapters
|
|
|
|
|
|
.filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
|
|
|
|
|
|
.sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
|
|
|
|
|
|
.slice(0, 10)
|
|
|
|
|
|
.map(c => {
|
|
|
|
|
|
const d = new Date(c.updatedAt || c.updated_at || Date.now())
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: c.id,
|
|
|
|
|
|
title: c.section_title || c.title || c.sectionTitle,
|
|
|
|
|
|
price: c.price || 1,
|
|
|
|
|
|
dateStr: `${d.getMonth() + 1}/${d.getDate()}`
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
this.setData({ latestChapters: latest })
|
|
|
|
|
|
} catch (e) { console.log('[Index] 加载最新新增失败:', e) }
|
|
|
|
|
|
},
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-02-23 14:07:41 +08:00
|
|
|
|
goToMemberDetail(e) {
|
|
|
|
|
|
const id = e.currentTarget.dataset.id
|
|
|
|
|
|
wx.navigateTo({ url: `/pages/member-detail/member-detail?id=${id}` })
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-02-24 14:35:58 +08:00
|
|
|
|
// 跳转到我的页面
|
|
|
|
|
|
goToMy() {
|
|
|
|
|
|
wx.switchTab({ url: '/pages/my/my' })
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 下拉刷新
|
2026-01-21 15:49:12 +08:00
|
|
|
|
async onPullDownRefresh() {
|
|
|
|
|
|
await this.initData()
|
|
|
|
|
|
this.updateUserStatus()
|
|
|
|
|
|
wx.stopPullDownRefresh()
|
2026-02-25 11:47:36 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onShareAppMessage() {
|
|
|
|
|
|
const ref = app.getMyReferralCode()
|
|
|
|
|
|
return {
|
|
|
|
|
|
title: 'Soul创业派对 - 真实商业故事',
|
|
|
|
|
|
path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index'
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|