Files
soul/miniprogram/pages/vip/vip.js
卡若 afc2376e96 v1.19 全面改版:VIP会员系统、我的收益、创业老板排行、阅读量排序
- 后端: 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>
2026-02-23 14:07:41 +08:00

108 lines
3.7 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.

const app = getApp()
Page({
data: {
statusBarHeight: 44,
isVip: false,
daysRemaining: 0,
expireDateStr: '',
price: 1980,
rights: [],
profile: { name: '', project: '', contact: '', bio: '' },
purchasing: false
},
onLoad() {
this.setData({ statusBarHeight: app.globalData.statusBarHeight })
this.loadVipInfo()
},
async loadVipInfo() {
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request(`/api/vip/status?userId=${userId}`)
if (res?.success) {
const d = res.data
let expStr = ''
if (d.expireDate) {
const dt = new Date(d.expireDate)
expStr = `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`
}
this.setData({
isVip: d.isVip,
daysRemaining: d.daysRemaining,
expireDateStr: expStr,
price: d.price || 1980,
rights: d.rights || ['解锁全部章节内容365天','匹配所有创业伙伴','创业老板排行榜展示','专属VIP标识']
})
if (d.isVip) this.loadProfile(userId)
}
} catch (e) {
console.log('[VIP] 加载失败', e)
this.setData({ rights: ['解锁全部章节内容365天','匹配所有创业伙伴','创业老板排行榜展示','专属VIP标识'] })
}
},
async loadProfile(userId) {
try {
const res = await app.request(`/api/vip/profile?userId=${userId}`)
if (res?.success) this.setData({ profile: res.data })
} catch (e) { console.log('[VIP] 资料加载失败', e) }
},
async handlePurchase() {
const userId = app.globalData.userInfo?.id
if (!userId) { wx.showToast({ title: '请先登录', icon: 'none' }); return }
this.setData({ purchasing: true })
try {
const res = await app.request('/api/vip/purchase', { method: 'POST', data: { userId } })
if (res?.success) {
// 调用微信支付
const payRes = await app.request('/api/miniprogram/pay', {
method: 'POST',
data: { orderSn: res.data.orderSn, openId: app.globalData.openId }
})
if (payRes?.success && payRes.payParams) {
wx.requestPayment({
...payRes.payParams,
success: () => {
wx.showToast({ title: 'VIP开通成功', icon: 'success' })
this.loadVipInfo()
},
fail: () => wx.showToast({ title: '支付取消', icon: 'none' })
})
} else {
wx.showToast({ title: '支付参数获取失败', icon: 'none' })
}
} else {
wx.showToast({ title: res?.error || '创建订单失败', icon: 'none' })
}
} catch (e) {
console.error('[VIP] 购买失败', e)
wx.showToast({ title: '购买失败', icon: 'none' })
} finally { this.setData({ purchasing: false }) }
},
onNameInput(e) { this.setData({ 'profile.name': e.detail.value }) },
onProjectInput(e) { this.setData({ 'profile.project': e.detail.value }) },
onContactInput(e) { this.setData({ 'profile.contact': e.detail.value }) },
onBioInput(e) { this.setData({ 'profile.bio': e.detail.value }) },
async saveProfile() {
const userId = app.globalData.userInfo?.id
if (!userId) return
const p = this.data.profile
try {
const res = await app.request('/api/vip/profile', {
method: 'POST',
data: { userId, name: p.name, project: p.project, contact: p.contact, bio: p.bio }
})
if (res?.success) wx.showToast({ title: '资料已保存', icon: 'success' })
else wx.showToast({ title: res?.error || '保存失败', icon: 'none' })
} catch (e) { wx.showToast({ title: '保存失败', icon: 'none' }) }
},
goBack() { wx.navigateBack() }
})