2026-01-21 15:49:12 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Soul创业实验 - 小程序入口
|
|
|
|
|
|
* 开发: 卡若
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2026-01-14 12:50:00 +08:00
|
|
|
|
App({
|
|
|
|
|
|
globalData: {
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// API基础地址
|
2026-01-23 05:47:09 +08:00
|
|
|
|
baseUrl: 'https://soul.quwanzhi.com',
|
2026-01-23 05:44:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 小程序配置
|
|
|
|
|
|
appId: 'wxb8bbb2b10dec74aa',
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户信息
|
2026-01-14 12:50:00 +08:00
|
|
|
|
userInfo: null,
|
2026-01-23 05:44:21 +08:00
|
|
|
|
openId: null, // 微信openId,支付必需
|
2026-01-21 15:49:12 +08:00
|
|
|
|
isLoggedIn: false,
|
|
|
|
|
|
|
|
|
|
|
|
// 书籍数据
|
2026-01-14 12:50:00 +08:00
|
|
|
|
bookData: null,
|
2026-01-21 15:49:12 +08:00
|
|
|
|
totalSections: 62,
|
|
|
|
|
|
|
|
|
|
|
|
// 购买记录
|
|
|
|
|
|
purchasedSections: [],
|
|
|
|
|
|
hasFullBook: false,
|
|
|
|
|
|
|
|
|
|
|
|
// 主题配置
|
|
|
|
|
|
theme: {
|
|
|
|
|
|
brandColor: '#00CED1',
|
|
|
|
|
|
brandSecondary: '#20B2AA',
|
|
|
|
|
|
goldColor: '#FFD700',
|
|
|
|
|
|
bgColor: '#000000',
|
|
|
|
|
|
cardBg: '#1c1c1e'
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 系统信息
|
|
|
|
|
|
systemInfo: null,
|
|
|
|
|
|
statusBarHeight: 44,
|
|
|
|
|
|
navBarHeight: 88,
|
|
|
|
|
|
|
|
|
|
|
|
// TabBar相关
|
|
|
|
|
|
currentTab: 0
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
onLaunch() {
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 获取系统信息
|
|
|
|
|
|
this.getSystemInfo()
|
2026-01-14 12:50:00 +08:00
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 检查登录状态
|
2026-01-14 12:50:00 +08:00
|
|
|
|
this.checkLoginStatus()
|
|
|
|
|
|
|
|
|
|
|
|
// 加载书籍数据
|
|
|
|
|
|
this.loadBookData()
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查更新
|
|
|
|
|
|
this.checkUpdate()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取系统信息
|
|
|
|
|
|
getSystemInfo() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const systemInfo = wx.getSystemInfoSync()
|
|
|
|
|
|
this.globalData.systemInfo = systemInfo
|
|
|
|
|
|
this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44
|
|
|
|
|
|
|
|
|
|
|
|
// 计算导航栏高度
|
|
|
|
|
|
const menuButton = wx.getMenuButtonBoundingClientRect()
|
|
|
|
|
|
if (menuButton) {
|
|
|
|
|
|
this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('获取系统信息失败:', e)
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 检查登录状态
|
|
|
|
|
|
checkLoginStatus() {
|
2026-01-21 15:49:12 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const userInfo = wx.getStorageSync('userInfo')
|
|
|
|
|
|
const token = wx.getStorageSync('token')
|
|
|
|
|
|
|
|
|
|
|
|
if (userInfo && token) {
|
|
|
|
|
|
this.globalData.userInfo = userInfo
|
|
|
|
|
|
this.globalData.isLoggedIn = true
|
|
|
|
|
|
this.globalData.purchasedSections = userInfo.purchasedSections || []
|
|
|
|
|
|
this.globalData.hasFullBook = userInfo.hasFullBook || false
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('检查登录状态失败:', e)
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 加载书籍数据
|
|
|
|
|
|
async loadBookData() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 先从缓存加载
|
|
|
|
|
|
const cachedData = wx.getStorageSync('bookData')
|
|
|
|
|
|
if (cachedData) {
|
|
|
|
|
|
this.globalData.bookData = cachedData
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 从服务器获取最新数据
|
|
|
|
|
|
const res = await this.request('/api/book/all-chapters')
|
|
|
|
|
|
if (res && res.data) {
|
|
|
|
|
|
this.globalData.bookData = res.data
|
|
|
|
|
|
wx.setStorageSync('bookData', res.data)
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('加载书籍数据失败:', e)
|
|
|
|
|
|
}
|
2026-01-14 12:50:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 检查更新
|
|
|
|
|
|
checkUpdate() {
|
|
|
|
|
|
if (wx.canIUse('getUpdateManager')) {
|
|
|
|
|
|
const updateManager = wx.getUpdateManager()
|
|
|
|
|
|
|
|
|
|
|
|
updateManager.onCheckForUpdate((res) => {
|
|
|
|
|
|
if (res.hasUpdate) {
|
|
|
|
|
|
console.log('发现新版本')
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
updateManager.onUpdateReady(() => {
|
|
|
|
|
|
wx.showModal({
|
|
|
|
|
|
title: '更新提示',
|
|
|
|
|
|
content: '新版本已准备好,是否重启应用?',
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.confirm) {
|
|
|
|
|
|
updateManager.applyUpdate()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
updateManager.onUpdateFailed(() => {
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
|
title: '更新失败,请稍后重试',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 统一请求方法
|
|
|
|
|
|
request(url, options = {}) {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
const token = wx.getStorageSync('token')
|
|
|
|
|
|
|
|
|
|
|
|
wx.request({
|
|
|
|
|
|
url: this.globalData.baseUrl + url,
|
|
|
|
|
|
method: options.method || 'GET',
|
|
|
|
|
|
data: options.data || {},
|
|
|
|
|
|
header: {
|
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
|
'Authorization': token ? `Bearer ${token}` : '',
|
|
|
|
|
|
...options.header
|
|
|
|
|
|
},
|
|
|
|
|
|
success: (res) => {
|
|
|
|
|
|
if (res.statusCode === 200) {
|
|
|
|
|
|
resolve(res.data)
|
|
|
|
|
|
} else if (res.statusCode === 401) {
|
|
|
|
|
|
// 未授权,清除登录状态
|
|
|
|
|
|
this.logout()
|
|
|
|
|
|
reject(new Error('未授权'))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
reject(new Error(res.data?.message || '请求失败'))
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
reject(err)
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
})
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-23 05:44:21 +08:00
|
|
|
|
// 登录方法 - 获取openId用于支付
|
2026-01-21 15:49:12 +08:00
|
|
|
|
async login() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取微信登录code
|
|
|
|
|
|
const loginRes = await new Promise((resolve, reject) => {
|
|
|
|
|
|
wx.login({
|
|
|
|
|
|
success: resolve,
|
|
|
|
|
|
fail: reject
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-01-23 05:44:21 +08:00
|
|
|
|
console.log('[App] 获取登录code成功')
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
try {
|
2026-01-23 05:44:21 +08:00
|
|
|
|
// 发送code到服务器获取openId
|
|
|
|
|
|
const res = await this.request('/api/miniprogram/login', {
|
2026-01-21 15:49:12 +08:00
|
|
|
|
method: 'POST',
|
|
|
|
|
|
data: { code: loginRes.code }
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (res.success && res.data) {
|
2026-01-23 05:44:21 +08:00
|
|
|
|
// 保存openId
|
|
|
|
|
|
if (res.data.openId) {
|
|
|
|
|
|
this.globalData.openId = res.data.openId
|
|
|
|
|
|
wx.setStorageSync('openId', res.data.openId)
|
|
|
|
|
|
console.log('[App] 获取openId成功')
|
|
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
2026-01-23 05:44:21 +08:00
|
|
|
|
// 保存用户信息
|
|
|
|
|
|
if (res.data.user) {
|
|
|
|
|
|
this.globalData.userInfo = res.data.user
|
|
|
|
|
|
this.globalData.isLoggedIn = true
|
|
|
|
|
|
this.globalData.purchasedSections = res.data.user.purchasedSections || []
|
|
|
|
|
|
this.globalData.hasFullBook = res.data.user.hasFullBook || false
|
|
|
|
|
|
|
|
|
|
|
|
wx.setStorageSync('userInfo', res.data.user)
|
|
|
|
|
|
wx.setStorageSync('token', res.data.token || '')
|
|
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
return res.data
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
} catch (apiError) {
|
2026-01-23 05:44:21 +08:00
|
|
|
|
console.log('[App] API登录失败,使用模拟登录:', apiError.message)
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
// API不可用时使用模拟登录
|
|
|
|
|
|
return this.mockLogin()
|
|
|
|
|
|
} catch (e) {
|
2026-01-23 05:44:21 +08:00
|
|
|
|
console.error('[App] 登录失败:', e)
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 最后尝试模拟登录
|
|
|
|
|
|
return this.mockLogin()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-01-23 05:44:21 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取openId (支付必需)
|
|
|
|
|
|
async getOpenId() {
|
|
|
|
|
|
// 先检查缓存
|
|
|
|
|
|
const cachedOpenId = wx.getStorageSync('openId')
|
|
|
|
|
|
if (cachedOpenId) {
|
|
|
|
|
|
this.globalData.openId = cachedOpenId
|
|
|
|
|
|
return cachedOpenId
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 没有缓存则登录获取
|
|
|
|
|
|
try {
|
|
|
|
|
|
const loginRes = await new Promise((resolve, reject) => {
|
|
|
|
|
|
wx.login({ success: resolve, fail: reject })
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const res = await this.request('/api/miniprogram/login', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
data: { code: loginRes.code }
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (res.success && res.data?.openId) {
|
|
|
|
|
|
this.globalData.openId = res.data.openId
|
|
|
|
|
|
wx.setStorageSync('openId', res.data.openId)
|
|
|
|
|
|
return res.data.openId
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('[App] 获取openId失败:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
},
|
2026-01-21 15:49:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 模拟登录(后端不可用时使用)
|
|
|
|
|
|
mockLogin() {
|
|
|
|
|
|
const mockUser = {
|
|
|
|
|
|
id: 'user_' + Date.now(),
|
|
|
|
|
|
nickname: '卡若',
|
|
|
|
|
|
phone: '15880802661',
|
|
|
|
|
|
avatar: '',
|
|
|
|
|
|
referralCode: 'SOUL' + Date.now().toString(36).toUpperCase().slice(-6),
|
|
|
|
|
|
purchasedSections: [],
|
|
|
|
|
|
hasFullBook: false,
|
|
|
|
|
|
earnings: 0,
|
|
|
|
|
|
pendingEarnings: 0,
|
|
|
|
|
|
referralCount: 0,
|
|
|
|
|
|
createdAt: new Date().toISOString()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const mockToken = 'mock_token_' + Date.now()
|
|
|
|
|
|
|
|
|
|
|
|
// 保存用户信息
|
|
|
|
|
|
this.globalData.userInfo = mockUser
|
|
|
|
|
|
this.globalData.isLoggedIn = true
|
|
|
|
|
|
this.globalData.purchasedSections = mockUser.purchasedSections
|
|
|
|
|
|
this.globalData.hasFullBook = mockUser.hasFullBook
|
|
|
|
|
|
|
|
|
|
|
|
wx.setStorageSync('userInfo', mockUser)
|
|
|
|
|
|
wx.setStorageSync('token', mockToken)
|
|
|
|
|
|
|
|
|
|
|
|
console.log('模拟登录成功:', mockUser)
|
|
|
|
|
|
|
|
|
|
|
|
return { user: mockUser, token: mockToken }
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 手机号登录
|
|
|
|
|
|
async loginWithPhone(phoneCode) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 尝试API登录
|
|
|
|
|
|
const res = await this.request('/api/wechat/phone-login', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
data: { code: phoneCode }
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (res.success && res.data) {
|
|
|
|
|
|
this.globalData.userInfo = res.data.user
|
|
|
|
|
|
this.globalData.isLoggedIn = true
|
|
|
|
|
|
this.globalData.purchasedSections = res.data.user.purchasedSections || []
|
|
|
|
|
|
this.globalData.hasFullBook = res.data.user.hasFullBook || false
|
|
|
|
|
|
|
|
|
|
|
|
wx.setStorageSync('userInfo', res.data.user)
|
|
|
|
|
|
wx.setStorageSync('token', res.data.token)
|
|
|
|
|
|
|
|
|
|
|
|
return res.data
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log('手机号API登录失败,使用模拟登录:', e)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 回退到模拟登录
|
|
|
|
|
|
return this.mockLogin()
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 退出登录
|
|
|
|
|
|
logout() {
|
|
|
|
|
|
this.globalData.userInfo = null
|
|
|
|
|
|
this.globalData.isLoggedIn = false
|
|
|
|
|
|
this.globalData.purchasedSections = []
|
|
|
|
|
|
this.globalData.hasFullBook = false
|
|
|
|
|
|
|
|
|
|
|
|
wx.removeStorageSync('userInfo')
|
|
|
|
|
|
wx.removeStorageSync('token')
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已购买章节
|
|
|
|
|
|
hasPurchased(sectionId) {
|
|
|
|
|
|
if (this.globalData.hasFullBook) return true
|
|
|
|
|
|
return this.globalData.purchasedSections.includes(sectionId)
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 获取章节总数
|
|
|
|
|
|
getTotalSections() {
|
|
|
|
|
|
return this.globalData.totalSections
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 切换TabBar
|
|
|
|
|
|
switchTab(index) {
|
|
|
|
|
|
this.globalData.currentTab = index
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 显示Toast
|
|
|
|
|
|
showToast(title, icon = 'none') {
|
|
|
|
|
|
wx.showToast({
|
|
|
|
|
|
title,
|
|
|
|
|
|
icon,
|
|
|
|
|
|
duration: 2000
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// 显示Loading
|
|
|
|
|
|
showLoading(title = '加载中...') {
|
|
|
|
|
|
wx.showLoading({
|
|
|
|
|
|
title,
|
|
|
|
|
|
mask: true
|
2026-01-14 12:50:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-01-21 15:49:12 +08:00
|
|
|
|
// 隐藏Loading
|
|
|
|
|
|
hideLoading() {
|
|
|
|
|
|
wx.hideLoading()
|
2026-01-14 12:50:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|