- 后端: users表新增VIP字段, 4个VIP API (purchase/status/profile/members) - 后端: hot接口改按user_tracks阅读量排序 - 后端: orders表支持vip产品类型, migrate新增vip_fields迁移 - 小程序「我的」: 推广中心改为我的收益, 头像VIP标识, VIP入口卡片 - 小程序「我的」: 最近阅读显示真实章节名称 - 小程序首页: 去掉内容概览, 新增创业老板排行(4列网格) - 小程序首页: 精选推荐从hot接口获取, goToRead增加track记录 - 新增页面: VIP详情页, 会员详情页 - 开发文档精简为10个标准目录, 创建SKILL.md, 需求日志规范化 Co-authored-by: Cursor <cursoragent@cursor.com>
170 lines
4.6 KiB
JavaScript
170 lines
4.6 KiB
JavaScript
/**
|
||
* Soul创业派对 - 首页
|
||
* 开发: 卡若
|
||
*/
|
||
|
||
const app = getApp()
|
||
|
||
Page({
|
||
data: {
|
||
statusBarHeight: 44,
|
||
navBarHeight: 88,
|
||
isLoggedIn: false,
|
||
hasFullBook: false,
|
||
purchasedCount: 0,
|
||
totalSections: 62,
|
||
bookData: [],
|
||
|
||
featuredSections: [],
|
||
latestSection: null,
|
||
latestLabel: '最新更新',
|
||
|
||
// 创业老板排行
|
||
vipMembers: [],
|
||
|
||
loading: true
|
||
},
|
||
|
||
onLoad(options) {
|
||
this.setData({
|
||
statusBarHeight: app.globalData.statusBarHeight,
|
||
navBarHeight: app.globalData.navBarHeight
|
||
})
|
||
if (options && options.ref) {
|
||
app.handleReferralCode({ query: options })
|
||
}
|
||
this.initData()
|
||
},
|
||
|
||
onShow() {
|
||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||
this.getTabBar().setData({ selected: 0 })
|
||
}
|
||
this.updateUserStatus()
|
||
},
|
||
|
||
async initData() {
|
||
this.setData({ loading: true })
|
||
try {
|
||
await Promise.all([
|
||
this.loadBookData(),
|
||
this.loadLatestSection(),
|
||
this.loadHotSections(),
|
||
this.loadVipMembers()
|
||
])
|
||
} catch (e) {
|
||
console.error('初始化失败:', e)
|
||
this.computeLatestSectionFallback()
|
||
} finally {
|
||
this.setData({ loading: false })
|
||
}
|
||
},
|
||
|
||
// 从hot接口获取精选推荐(按阅读量排序)
|
||
async loadHotSections() {
|
||
try {
|
||
const res = await app.request('/api/book/hot')
|
||
if (res?.success && res.chapters?.length) {
|
||
this.setData({ featuredSections: res.chapters.slice(0, 5) })
|
||
}
|
||
} catch (e) {
|
||
console.log('[Index] 热门章节加载失败', e)
|
||
}
|
||
},
|
||
|
||
// 加载VIP会员列表
|
||
async loadVipMembers() {
|
||
try {
|
||
const res = await app.request('/api/vip/members?limit=8')
|
||
if (res?.success && res.data?.length) {
|
||
this.setData({ vipMembers: res.data })
|
||
}
|
||
} catch (e) {
|
||
console.log('[Index] VIP会员加载失败', e)
|
||
}
|
||
},
|
||
|
||
async loadLatestSection() {
|
||
try {
|
||
const res = await app.request('/api/book/latest-chapters')
|
||
if (res?.success && res.banner) {
|
||
this.setData({ latestSection: res.banner, latestLabel: res.label || '最新更新' })
|
||
return
|
||
}
|
||
} catch (e) {
|
||
console.warn('latest-chapters API 失败:', e.message)
|
||
}
|
||
this.computeLatestSectionFallback()
|
||
},
|
||
|
||
computeLatestSectionFallback() {
|
||
const bookData = app.globalData.bookData || this.data.bookData || []
|
||
let sections = []
|
||
if (Array.isArray(bookData)) {
|
||
sections = bookData.map(s => ({
|
||
id: s.id, title: s.title || s.sectionTitle,
|
||
part: s.part || s.sectionTitle || '真实的行业',
|
||
isFree: s.isFree, price: s.price
|
||
}))
|
||
}
|
||
const free = sections.filter(s => s.isFree !== false && (s.price === 0 || !s.price))
|
||
const candidates = free.length > 0 ? free : sections
|
||
if (!candidates.length) {
|
||
this.setData({ latestSection: { id: '1.1', title: '开始阅读', part: '真实的人' }, latestLabel: '为你推荐' })
|
||
return
|
||
}
|
||
const idx = Math.floor(Math.random() * candidates.length)
|
||
this.setData({ latestSection: candidates[idx], latestLabel: '为你推荐' })
|
||
},
|
||
|
||
async loadBookData() {
|
||
try {
|
||
const res = await app.request('/api/book/all-chapters')
|
||
if (res?.data) {
|
||
this.setData({
|
||
bookData: res.data,
|
||
totalSections: res.totalSections || res.data?.length || 62
|
||
})
|
||
}
|
||
} catch (e) { console.error('加载书籍数据失败:', e) }
|
||
},
|
||
|
||
updateUserStatus() {
|
||
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
|
||
this.setData({
|
||
isLoggedIn, hasFullBook,
|
||
purchasedCount: hasFullBook ? this.data.totalSections : (purchasedSections?.length || 0)
|
||
})
|
||
},
|
||
|
||
// 阅读时记录行为轨迹
|
||
goToRead(e) {
|
||
const id = e.currentTarget.dataset.id
|
||
// 记录阅读行为(异步,不阻塞跳转)
|
||
const userId = app.globalData.userInfo?.id
|
||
if (userId) {
|
||
app.request('/api/user/track', {
|
||
method: 'POST',
|
||
data: { userId, action: 'view_chapter', target: id }
|
||
}).catch(() => {})
|
||
}
|
||
wx.navigateTo({ url: `/pages/read/read?id=${id}` })
|
||
},
|
||
|
||
goToChapters() { wx.switchTab({ url: '/pages/chapters/chapters' }) },
|
||
goToSearch() { wx.navigateTo({ url: '/pages/search/search' }) },
|
||
goToMatch() { wx.switchTab({ url: '/pages/match/match' }) },
|
||
goToMy() { wx.switchTab({ url: '/pages/my/my' }) },
|
||
|
||
goToMemberDetail(e) {
|
||
const id = e.currentTarget.dataset.id
|
||
wx.navigateTo({ url: `/pages/member-detail/member-detail?id=${id}` })
|
||
},
|
||
|
||
async onPullDownRefresh() {
|
||
await this.initData()
|
||
this.updateUserStatus()
|
||
wx.stopPullDownRefresh()
|
||
}
|
||
})
|