1587 lines
44 KiB
JavaScript
1587 lines
44 KiB
JavaScript
// pages/index/index.js
|
||
// 首页 - 角色列表展示(卡片滑动)
|
||
|
||
const app = getApp()
|
||
const api = require('../../utils/api')
|
||
const util = require('../../utils/util')
|
||
const config = require('../../config/index')
|
||
const proactiveMessage = require('../../utils/proactiveMessage')
|
||
|
||
// 获取静态资源基础URL(去掉/api后缀)
|
||
const getStaticBaseUrl = () => {
|
||
const apiUrl = config.API_BASE_URL
|
||
return apiUrl.replace(/\/api$/, '')
|
||
}
|
||
|
||
// 好友故事数据 - 将在加载角色后动态更新
|
||
let STORY_USERS = []
|
||
|
||
Page({
|
||
data: {
|
||
statusBarHeight: 44,
|
||
navHeight: 96,
|
||
|
||
// 角色数据
|
||
profiles: [],
|
||
currentIndex: 0,
|
||
loading: true,
|
||
error: null,
|
||
|
||
// 故事用户
|
||
storyUsers: STORY_USERS,
|
||
|
||
// 首页 Banner
|
||
homeBanner: '',
|
||
bannerHeight: 240,
|
||
|
||
// 交互状态
|
||
likedProfiles: {},
|
||
unlockedProfiles: {},
|
||
showVipModal: false,
|
||
|
||
// 爱心弹窗
|
||
showHeartPopup: false,
|
||
currentCharacter: null,
|
||
heartCount: 0,
|
||
heartPackages: [
|
||
{ id: 1, hearts: 10, price: 6, tag: '' },
|
||
{ id: 2, hearts: 30, price: 18, tag: '热门' },
|
||
{ id: 3, hearts: 50, price: 28, tag: '' },
|
||
{ id: 4, hearts: 100, price: 50, tag: '超值' },
|
||
{ id: 5, hearts: 200, price: 98, tag: '' },
|
||
{ id: 6, hearts: 500, price: 238, tag: '最划算' }
|
||
],
|
||
selectedHeartPackage: 1, // 默认选中第二个(热门)
|
||
purchasing: false,
|
||
|
||
// 未读消息数
|
||
totalUnread: 0,
|
||
|
||
// 滑动相关
|
||
startX: 0,
|
||
startY: 0,
|
||
offsetX: 0,
|
||
rotation: 0,
|
||
swipeDirection: '',
|
||
|
||
// 分享配置
|
||
shareConfig: null,
|
||
|
||
// 注册奖励
|
||
showRegistrationReward: false,
|
||
registrationRewardAmount: 0,
|
||
claiming: false,
|
||
auditStatus: 0,
|
||
|
||
// GF100 弹窗
|
||
showGf100Popup: false,
|
||
gf100ImageUrl: '',
|
||
|
||
// 解锁配置
|
||
unlockHeartsCost: 500 // 默认解锁爱心成本
|
||
},
|
||
|
||
async onLoad() {
|
||
// 获取系统信息
|
||
const { statusBarHeight, navHeight, auditStatus } = app.globalData
|
||
this.setData({ statusBarHeight, navHeight, auditStatus })
|
||
|
||
// 如果是审核状态,重定向到文娱页面
|
||
if (auditStatus === 1) {
|
||
wx.switchTab({
|
||
url: '/pages/entertainment/entertainment'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 加载首页素材 (Banner等)
|
||
this.loadHomeAssets()
|
||
|
||
// 加载分享配置
|
||
this.loadShareConfig()
|
||
|
||
// 加载角色列表
|
||
this.loadCharacters()
|
||
|
||
// 加载用户爱心余额
|
||
this.loadHeartBalance()
|
||
|
||
// 加载解锁配置
|
||
this.loadUnlockConfig()
|
||
},
|
||
|
||
/**
|
||
* 加载解锁配置
|
||
*/
|
||
async loadUnlockConfig() {
|
||
try {
|
||
// 获取当前列表中的第一个角色ID用于查询配置(配置是全局的,任一ID即可)
|
||
const charId = this.data.profiles[0]?.id || 'default'
|
||
const res = await api.chat.getQuota(charId)
|
||
|
||
if (res.success && res.data && res.data.unlock_config) {
|
||
const cost = res.data.unlock_config.hearts_cost
|
||
if (typeof cost === 'number') {
|
||
this.setData({
|
||
unlockHeartsCost: cost
|
||
})
|
||
console.log('[index] 已从后端同步解锁成本:', cost)
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.log('[index] 加载解锁配置失败,使用默认值', err)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 加载分享配置
|
||
*/
|
||
async loadShareConfig() {
|
||
try {
|
||
const res = await api.promotion.getShareConfig('index')
|
||
if (res.success && res.data) {
|
||
// 处理图片URL,确保是完整路径
|
||
const shareConfig = {
|
||
...res.data,
|
||
imageUrl: res.data.imageUrl ? util.getFullImageUrl(res.data.imageUrl) : ''
|
||
}
|
||
this.setData({
|
||
shareConfig
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.error('加载分享配置失败:', error)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 处理图片URL,如果是相对路径则拼接域名,并设置清晰度为85
|
||
*/
|
||
processImageUrl(url) {
|
||
return util.getFullImageUrl(url)
|
||
},
|
||
|
||
/**
|
||
* 轮播图图片加载完成,自适应高度
|
||
*/
|
||
onBannerLoad(e) {
|
||
const { width, height } = e.detail;
|
||
const sysInfo = wx.getSystemInfoSync();
|
||
// 减去左右padding (32rpx * 2)
|
||
const swiperWidth = sysInfo.windowWidth - (32 * 2 / 750 * sysInfo.windowWidth);
|
||
const ratio = width / height;
|
||
const bannerHeight = swiperWidth / ratio;
|
||
const bannerHeightRpx = bannerHeight * (750 / sysInfo.windowWidth);
|
||
|
||
this.setData({
|
||
bannerHeight: bannerHeightRpx
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 加载首页素材
|
||
*/
|
||
async loadHomeAssets() {
|
||
try {
|
||
const res = await api.pageAssets.getAssets('banners')
|
||
console.log('首页素材 API响应:', res)
|
||
|
||
if (res.success && res.data) {
|
||
// 优先使用 home_banner,其次使用 companion_banner
|
||
const bannerUrl = res.data.home_banner || res.data.companion_banner
|
||
if (bannerUrl) {
|
||
this.setData({
|
||
homeBanner: this.processImageUrl(bannerUrl)
|
||
})
|
||
console.log('已加载首页Banner')
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.error('加载首页素材失败', err)
|
||
}
|
||
},
|
||
|
||
onShow() {
|
||
// 隐藏默认tabbar
|
||
wx.hideTabBar({ animation: false })
|
||
|
||
const app = getApp()
|
||
this.setData({
|
||
auditStatus: app.globalData.auditStatus
|
||
})
|
||
|
||
// 如果是审核状态,重定向到文娱页面
|
||
if (app.globalData.auditStatus === 1) {
|
||
wx.switchTab({
|
||
url: '/pages/entertainment/entertainment'
|
||
})
|
||
return
|
||
}
|
||
|
||
// 刷新爱心余额
|
||
this.loadHeartBalance()
|
||
|
||
// 加载未读消息数
|
||
this.loadUnreadCount()
|
||
|
||
// 检查AI角色主动推送消息
|
||
this.checkProactiveMessages()
|
||
|
||
// 检查注册奖励
|
||
this.checkRegistrationReward()
|
||
|
||
// 检查 GF100 弹窗
|
||
this.checkGf100Popup()
|
||
},
|
||
|
||
onPullDownRefresh() {
|
||
Promise.all([
|
||
this.loadCharacters(),
|
||
this.loadHeartBalance()
|
||
]).then(() => {
|
||
wx.stopPullDownRefresh()
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 加载用户爱心值
|
||
* 使用 /api/auth/me 接口,该接口从 im_users.grass_balance 读取余额
|
||
*/
|
||
async loadHeartBalance() {
|
||
try {
|
||
const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
|
||
if (!token) {
|
||
this.setData({ heartCount: 0 })
|
||
return
|
||
}
|
||
|
||
// 使用 auth.getCurrentUser 接口,该接口返回 grass_balance 字段
|
||
const res = await api.auth.getCurrentUser()
|
||
if (res.success && res.data) {
|
||
this.setData({
|
||
heartCount: res.data.grass_balance || 0
|
||
})
|
||
console.log('[index] 爱心值加载成功:', res.data.grass_balance)
|
||
}
|
||
} catch (err) {
|
||
console.log('加载爱心值失败', err)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 加载未读消息数
|
||
* 包含会话未读数 + 主动推送消息数
|
||
*/
|
||
async loadUnreadCount() {
|
||
if (!app.globalData.isLoggedIn) {
|
||
this.setData({ totalUnread: 0 })
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 并行获取会话列表和主动推送消息
|
||
const [convRes, proactiveRes] = await Promise.all([
|
||
api.chat.getConversations(),
|
||
api.proactiveMessage.getPending()
|
||
])
|
||
|
||
let totalUnread = 0
|
||
|
||
// 计算会话未读数
|
||
if (convRes.success && convRes.data) {
|
||
totalUnread = convRes.data.reduce((sum, conv) => sum + (conv.unread_count || 0), 0)
|
||
}
|
||
|
||
// 加上主动推送消息数
|
||
if (proactiveRes.success && proactiveRes.data && Array.isArray(proactiveRes.data)) {
|
||
totalUnread += proactiveRes.data.length
|
||
console.log('[index] 主动推送消息数:', proactiveRes.data.length)
|
||
}
|
||
|
||
this.setData({ totalUnread })
|
||
console.log('[index] 总未读消息数:', totalUnread)
|
||
} catch (err) {
|
||
console.log('获取未读消息数失败', err)
|
||
this.setData({ totalUnread: 0 })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 检查AI角色主动推送消息
|
||
* 注意:未读数已在 loadUnreadCount 中计算,不在首页显示Toast提示
|
||
*/
|
||
async checkProactiveMessages() {
|
||
if (!app.globalData.isLoggedIn) {
|
||
return
|
||
}
|
||
|
||
try {
|
||
const messages = await proactiveMessage.checkAndShowMessages({
|
||
force: true // 首次进入强制检查
|
||
})
|
||
|
||
console.log('[index] 主动推送消息检查完成,消息数:', messages.length)
|
||
} catch (err) {
|
||
console.log('[index] 检查主动推送消息失败', err)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 加载角色列表
|
||
*/
|
||
async loadCharacters() {
|
||
this.setData({ loading: true, error: null })
|
||
|
||
try {
|
||
console.log('[index] 开始加载角色列表...')
|
||
const res = await api.character.getRandom(10)
|
||
console.log('[index] API响应:', JSON.stringify(res))
|
||
|
||
// 兼容两种返回格式:
|
||
// 1. { code: 0, data: { list: [...] } } - 新格式
|
||
// 2. { success: true, data: [...] } - 旧格式
|
||
let characters = []
|
||
if (res.code === 0 && res.data) {
|
||
characters = res.data.list || res.data
|
||
if (!Array.isArray(characters)) {
|
||
characters = []
|
||
}
|
||
} else if (res.success && res.data) {
|
||
characters = Array.isArray(res.data) ? res.data : (res.data.list || [])
|
||
}
|
||
|
||
console.log('[index] 解析到角色数量:', characters.length)
|
||
|
||
if (characters.length > 0) {
|
||
// 转换数据格式:后端格式 -> 前端格式
|
||
const profiles = characters.map(char => this.transformCharacter(char))
|
||
|
||
// 更新好友故事区域(取前8个角色)
|
||
const storyUsers = profiles.slice(0, 8).map(p => ({
|
||
id: p.id,
|
||
name: p.name,
|
||
img: p.avatar
|
||
}))
|
||
|
||
this.setData({
|
||
profiles,
|
||
storyUsers,
|
||
currentIndex: 0,
|
||
loading: false
|
||
})
|
||
|
||
console.log('[index] 角色加载成功,数量:', profiles.length)
|
||
} else {
|
||
console.log('[index] 没有角色数据')
|
||
this.setData({
|
||
loading: false,
|
||
profiles: [],
|
||
currentIndex: 0,
|
||
error: '暂无角色数据'
|
||
})
|
||
}
|
||
} catch (err) {
|
||
console.error('[index] 加载角色失败', err)
|
||
this.setData({
|
||
loading: false,
|
||
profiles: [],
|
||
currentIndex: 0,
|
||
error: err.message || '加载失败'
|
||
})
|
||
// 不显示toast,让用户看到空状态页面
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 转换角色数据格式
|
||
* @param {object} char - 后端角色数据
|
||
*/
|
||
transformCharacter(char) {
|
||
// 静态资源基础URL
|
||
const staticBaseUrl = getStaticBaseUrl()
|
||
|
||
// 转换照片路径为完整URL
|
||
const convertPhotoUrl = (url) => {
|
||
if (!url) return ''
|
||
// 如果已经是完整URL,直接返回
|
||
if (url.startsWith('http://') || url.startsWith('https://')) {
|
||
return url
|
||
}
|
||
// 如果是相对路径,拼接基础URL
|
||
if (url.startsWith('/characters/')) {
|
||
return staticBaseUrl + url
|
||
}
|
||
return url
|
||
}
|
||
|
||
// 头像URL(用于推荐栏圆形小头像)- 优先使用 avatar/logo
|
||
const avatarUrl = convertPhotoUrl(char.avatar || char.logo || char.image)
|
||
|
||
// 宣传图URL(用于卡片大图)- 优先使用 promoImage
|
||
const promoImageUrl = convertPhotoUrl(char.promoImage || char.promo_image || char.avatar || char.image)
|
||
|
||
return {
|
||
id: char.id,
|
||
name: char.name || '未知',
|
||
height: char.height || '',
|
||
location: char.location || char.province || '',
|
||
occupation: char.occupation || '',
|
||
hobbies: char.hobbies || '',
|
||
tags: char.tags || [],
|
||
bio: char.bio || char.description || char.self_introduction || '',
|
||
image: promoImageUrl, // 卡片大图使用宣传图
|
||
avatar: avatarUrl, // 小头像使用头像
|
||
gender: char.gender || 'female',
|
||
age: char.age || '',
|
||
voiceId: char.voice_id,
|
||
isVipOnly: char.is_vip_only || false,
|
||
// 开场白音频URL
|
||
greetingAudioUrl: char.greetingAudioUrl || char.greeting_audio_url || ''
|
||
}
|
||
},
|
||
|
||
// ==================== 滑动交互 ====================
|
||
|
||
onTouchStart(e) {
|
||
this.setData({
|
||
startX: e.touches[0].clientX,
|
||
startY: e.touches[0].clientY,
|
||
swipeDirection: ''
|
||
})
|
||
},
|
||
|
||
onTouchMove(e) {
|
||
const moveX = e.touches[0].clientX - this.data.startX
|
||
const rotation = moveX * 0.05
|
||
|
||
this.setData({
|
||
offsetX: moveX,
|
||
rotation: Math.max(-15, Math.min(15, rotation))
|
||
})
|
||
},
|
||
|
||
onTouchEnd(e) {
|
||
const threshold = 80
|
||
const { offsetX } = this.data
|
||
|
||
if (offsetX > threshold) {
|
||
// 右滑 - 喜欢
|
||
this.handleSwipeLike()
|
||
} else if (offsetX < -threshold) {
|
||
// 左滑 - 跳过
|
||
this.handlePass()
|
||
} else {
|
||
// 重置位置
|
||
this.setData({
|
||
offsetX: 0,
|
||
rotation: 0
|
||
})
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 处理喜欢操作
|
||
*/
|
||
async handleSwipeLike() {
|
||
const { currentIndex, profiles, likedProfiles } = this.data
|
||
|
||
if (currentIndex >= profiles.length) return
|
||
|
||
const currentProfile = profiles[currentIndex]
|
||
const currentId = currentProfile.id
|
||
|
||
// 更新本地状态
|
||
const newLiked = { ...likedProfiles }
|
||
newLiked[currentId] = true
|
||
|
||
this.setData({
|
||
swipeDirection: 'swipe-right',
|
||
likedProfiles: newLiked
|
||
})
|
||
|
||
// 调用API(静默操作,不显示提示)
|
||
try {
|
||
await api.character.toggleLike(currentId)
|
||
} catch (err) {
|
||
console.log('喜欢操作失败', err)
|
||
}
|
||
|
||
// 移动到下一张
|
||
setTimeout(() => {
|
||
this.moveToNext()
|
||
}, 300)
|
||
},
|
||
|
||
/**
|
||
* 处理跳过操作
|
||
*/
|
||
handlePass() {
|
||
this.setData({ swipeDirection: 'swipe-left' })
|
||
|
||
setTimeout(() => {
|
||
this.moveToNext()
|
||
}, 300)
|
||
},
|
||
|
||
/**
|
||
* 移动到下一张卡片
|
||
*/
|
||
moveToNext() {
|
||
const { currentIndex, profiles } = this.data
|
||
const nextIndex = currentIndex + 1
|
||
|
||
// 如果快到末尾,加载更多
|
||
if (nextIndex >= profiles.length - 2) {
|
||
this.loadMoreCharacters()
|
||
}
|
||
|
||
this.setData({
|
||
currentIndex: nextIndex,
|
||
offsetX: 0,
|
||
rotation: 0,
|
||
swipeDirection: ''
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 加载更多角色
|
||
* 传递已显示的角色ID,避免重复
|
||
*/
|
||
async loadMoreCharacters() {
|
||
try {
|
||
// 获取已显示的角色ID列表
|
||
const existingIds = this.data.profiles.map(p => p.id).filter(id => id)
|
||
|
||
const res = await api.character.getRandom(6, { excludeIds: existingIds })
|
||
|
||
// 兼容两种返回格式
|
||
let characters = []
|
||
if (res.code === 0 && res.data) {
|
||
characters = res.data.list || res.data
|
||
} else if (res.success && res.data) {
|
||
characters = Array.isArray(res.data) ? res.data : (res.data.list || [])
|
||
}
|
||
|
||
if (characters.length > 0) {
|
||
// 再次过滤,确保不重复
|
||
const existingIdSet = new Set(existingIds)
|
||
const filteredCharacters = characters.filter(char => !existingIdSet.has(char.id))
|
||
|
||
if (filteredCharacters.length > 0) {
|
||
const newProfiles = filteredCharacters.map(char => this.transformCharacter(char))
|
||
|
||
this.setData({
|
||
profiles: [...this.data.profiles, ...newProfiles]
|
||
})
|
||
}
|
||
}
|
||
} catch (err) {
|
||
console.log('加载更多失败', err)
|
||
}
|
||
},
|
||
|
||
// ==================== 按钮操作 ====================
|
||
|
||
/**
|
||
* 切换喜欢状态 - 显示爱心套餐弹窗
|
||
*/
|
||
onToggleLike() {
|
||
const { currentIndex, profiles } = this.data
|
||
|
||
if (currentIndex >= profiles.length) return
|
||
|
||
const currentProfile = profiles[currentIndex]
|
||
|
||
// 保存当前角色信息并显示弹窗
|
||
this.setData({
|
||
showHeartPopup: true,
|
||
currentCharacter: currentProfile
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 播放语音(优先使用预录制的开场白音频)
|
||
*/
|
||
async onPlayVoice() {
|
||
const { currentIndex, profiles } = this.data
|
||
|
||
if (currentIndex >= profiles.length) return
|
||
|
||
const currentProfile = profiles[currentIndex]
|
||
|
||
// 优先使用预录制的开场白音频
|
||
const greetingAudioUrl = currentProfile.greetingAudioUrl || currentProfile.greeting_audio_url
|
||
|
||
if (greetingAudioUrl) {
|
||
// 处理相对路径,拼接完整URL
|
||
let audioUrl = greetingAudioUrl
|
||
if (audioUrl.startsWith('/')) {
|
||
audioUrl = getStaticBaseUrl() + audioUrl
|
||
}
|
||
|
||
console.log('[index] 播放开场白音频:', audioUrl)
|
||
|
||
// 停止之前的音频
|
||
if (this.audioContext) {
|
||
try {
|
||
this.audioContext.stop()
|
||
this.audioContext.destroy()
|
||
} catch (e) {}
|
||
this.audioContext = null
|
||
}
|
||
|
||
// 先检查音频文件是否存在
|
||
wx.request({
|
||
url: audioUrl,
|
||
method: 'HEAD',
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
// 文件存在,播放音频
|
||
this.playAudioFile(audioUrl)
|
||
} else {
|
||
console.log('[index] 音频文件不存在:', res.statusCode)
|
||
wx.showToast({ title: '该角色暂无独白音频', icon: 'none' })
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log('[index] 检查音频文件失败:', err)
|
||
// 即使HEAD请求失败,也尝试播放(有些服务器不支持HEAD)
|
||
this.playAudioFile(audioUrl)
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
// 没有预录制音频,提示用户
|
||
wx.showToast({
|
||
title: '该角色暂无独白音频',
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 播放音频文件
|
||
*/
|
||
playAudioFile(audioUrl) {
|
||
// 显示播放中提示
|
||
wx.showToast({
|
||
title: '播放独白中...',
|
||
icon: 'none',
|
||
duration: 5000
|
||
})
|
||
|
||
// 创建音频上下文
|
||
const innerAudioContext = wx.createInnerAudioContext()
|
||
this.audioContext = innerAudioContext
|
||
|
||
// 配置音频属性
|
||
innerAudioContext.src = audioUrl
|
||
innerAudioContext.volume = 1.0
|
||
innerAudioContext.obeyMuteSwitch = false // 不受系统静音开关影响
|
||
innerAudioContext.autoplay = false
|
||
|
||
// 确保在 iOS 上即使在静音模式下也能播放(双重保险)
|
||
if (wx.setInnerAudioOption) {
|
||
wx.setInnerAudioOption({
|
||
obeyMuteSwitch: false,
|
||
speakerOn: true
|
||
})
|
||
}
|
||
|
||
innerAudioContext.onCanplay(() => {
|
||
console.log('[index] 音频可以播放, duration:', innerAudioContext.duration)
|
||
})
|
||
|
||
innerAudioContext.onPlay(() => {
|
||
console.log('[index] 音频开始播放, volume:', innerAudioContext.volume)
|
||
})
|
||
|
||
innerAudioContext.onTimeUpdate(() => {
|
||
// 每秒打印一次进度
|
||
const currentTime = Math.floor(innerAudioContext.currentTime)
|
||
if (currentTime !== this._lastLogTime) {
|
||
console.log('[index] 播放进度:', currentTime, '/', Math.floor(innerAudioContext.duration || 0))
|
||
this._lastLogTime = currentTime
|
||
}
|
||
})
|
||
|
||
innerAudioContext.onError((err) => {
|
||
console.error('[index] 音频播放错误:', JSON.stringify(err))
|
||
wx.hideToast()
|
||
let errMsg = '播放失败'
|
||
if (err.errCode === 10001 || err.errCode === -1) {
|
||
errMsg = '音频文件不存在'
|
||
} else if (err.errCode === 10002) {
|
||
errMsg = '网络错误'
|
||
} else if (err.errCode === 10003 || err.errCode === 10004) {
|
||
errMsg = '音频格式不支持'
|
||
}
|
||
wx.showToast({ title: errMsg, icon: 'none' })
|
||
})
|
||
|
||
innerAudioContext.onEnded(() => {
|
||
console.log('[index] 音频播放结束')
|
||
wx.hideToast()
|
||
})
|
||
|
||
// 延迟播放
|
||
setTimeout(() => {
|
||
console.log('[index] 调用 play()')
|
||
innerAudioContext.play()
|
||
}, 100)
|
||
},
|
||
|
||
/**
|
||
* 选择角色(进入详情)
|
||
*/
|
||
onSelectCharacter() {
|
||
const { currentIndex, profiles } = this.data
|
||
|
||
if (currentIndex >= profiles.length) return
|
||
|
||
const profile = profiles[currentIndex]
|
||
|
||
wx.navigateTo({
|
||
url: `/pages/character-detail/character-detail?id=${profile.id}`
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 点击卡片进入详情
|
||
*/
|
||
onCardTap() {
|
||
// 如果有滑动偏移,不触发点击
|
||
if (Math.abs(this.data.offsetX) > 10) {
|
||
return
|
||
}
|
||
|
||
this.onSelectCharacter()
|
||
},
|
||
|
||
/**
|
||
* 刷新角色列表
|
||
*/
|
||
onRefresh() {
|
||
this.loadCharacters()
|
||
|
||
wx.showToast({
|
||
title: '已为您换一批',
|
||
icon: 'success'
|
||
})
|
||
},
|
||
|
||
// ==================== VIP弹窗 ====================
|
||
|
||
closeVipModal() {
|
||
this.setData({ showVipModal: false })
|
||
},
|
||
|
||
preventBubble() {
|
||
// 阻止事件冒泡
|
||
},
|
||
|
||
preventTouchMove() {
|
||
// 阻止滚动穿透
|
||
},
|
||
|
||
// ==================== 爱心弹窗 ====================
|
||
|
||
/**
|
||
* 显示爱心弹窗
|
||
*/
|
||
showHeartPopup() {
|
||
this.setData({ showHeartPopup: true })
|
||
},
|
||
|
||
/**
|
||
* 关闭爱心弹窗
|
||
*/
|
||
closeHeartPopup() {
|
||
this.setData({
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 选择爱心套餐
|
||
*/
|
||
selectHeartPackage(e) {
|
||
const index = e.currentTarget.dataset.index
|
||
this.setData({ selectedHeartPackage: index })
|
||
},
|
||
|
||
/**
|
||
* 购买爱心
|
||
* 使用 /api/payment/unified-order 接口
|
||
* 测试模式下返回 testMode: true,订单直接完成,无需调用微信支付
|
||
*/
|
||
async buyHearts() {
|
||
const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
|
||
if (!token) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||
setTimeout(() => {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
const selectedPackage = this.data.heartPackages[this.data.selectedHeartPackage]
|
||
if (!selectedPackage) {
|
||
wx.showToast({ title: '请选择套餐', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
this.setData({ purchasing: true })
|
||
|
||
try {
|
||
wx.showLoading({ title: '创建订单中...' })
|
||
|
||
// 调用统一支付订单接口
|
||
const res = await api.payment.createUnifiedOrder({
|
||
type: 'recharge',
|
||
amount: selectedPackage.price,
|
||
rechargeValue: selectedPackage.hearts
|
||
})
|
||
|
||
wx.hideLoading()
|
||
|
||
console.log('[buyHearts] API返回:', JSON.stringify(res))
|
||
|
||
if (res.success) {
|
||
// 检查是否为测试模式(兼容多种判断方式)
|
||
const isTestMode = res.testMode || res.test_mode || res.data?.testMode || res.data?.test_mode ||
|
||
// 如果 payParams.package 包含 mock_prepay,也认为是测试模式
|
||
(res.payParams?.package && res.payParams.package.includes('mock_prepay'))
|
||
|
||
if (isTestMode) {
|
||
// 测试模式:订单已直接完成,无需调用微信支付
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
await this.loadHeartBalance()
|
||
this.closeHeartPopup()
|
||
} else {
|
||
// 正式模式:调用微信支付
|
||
const payParams = res.payParams || res.pay_params || res.data?.payParams || res.data?.pay_params
|
||
|
||
if (!payParams || !payParams.timeStamp) {
|
||
wx.showToast({ title: '支付参数错误', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.requestPayment({
|
||
timeStamp: payParams.timeStamp,
|
||
nonceStr: payParams.nonceStr,
|
||
package: payParams.package,
|
||
signType: payParams.signType || 'RSA',
|
||
paySign: payParams.paySign,
|
||
success: async () => {
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
await this.loadHeartBalance()
|
||
this.closeHeartPopup()
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||
wx.showToast({ title: '支付失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
} else {
|
||
wx.showToast({ title: res.message || '创建订单失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('购买爱心失败', err)
|
||
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||
} finally {
|
||
this.setData({ purchasing: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 分享解锁
|
||
*/
|
||
onShareUnlock() {
|
||
wx.showToast({
|
||
title: '分享功能开发中',
|
||
icon: 'none'
|
||
})
|
||
// TODO: 实现分享解锁逻辑
|
||
},
|
||
|
||
/**
|
||
* 爱心兑换解锁
|
||
*/
|
||
async onExchangeHearts() {
|
||
const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
|
||
if (!token) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||
setTimeout(() => {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
const { currentCharacter, heartCount, unlockHeartsCost } = this.data
|
||
if (!currentCharacter) return
|
||
|
||
// 检查爱心值,不足时提示并跳转充值页面
|
||
if (heartCount < unlockHeartsCost) {
|
||
wx.showToast({ title: '爱心值不足,去充值', icon: 'none' })
|
||
setTimeout(() => {
|
||
this.setData({ showHeartPopup: false })
|
||
wx.navigateTo({ url: '/pages/recharge/recharge' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
this.setData({ purchasing: true })
|
||
|
||
try {
|
||
wx.showLoading({ title: '兑换中...' })
|
||
|
||
const res = await api.character.unlock({
|
||
character_id: currentCharacter.id,
|
||
unlock_type: 'hearts'
|
||
})
|
||
|
||
wx.hideLoading()
|
||
|
||
if (res.success || res.code === 0) {
|
||
wx.showToast({ title: '解锁成功', icon: 'success' })
|
||
|
||
// 更新爱心余额(优先使用后端返回的余额,否则本地扣减)
|
||
const newBalance = res.data?.remaining_hearts ?? (this.data.heartCount - unlockHeartsCost)
|
||
this.setData({
|
||
heartCount: newBalance
|
||
})
|
||
|
||
// 记录已解锁
|
||
const newUnlocked = { ...this.data.unlockedProfiles }
|
||
newUnlocked[currentCharacter.id] = true
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
|
||
// 延迟后跳转到聊天页面
|
||
setTimeout(() => {
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${currentCharacter.id}&name=${encodeURIComponent(currentCharacter.name)}`
|
||
})
|
||
}, 1000)
|
||
} else {
|
||
wx.showToast({ title: res.message || '兑换失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('爱心兑换失败', err)
|
||
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||
} finally {
|
||
this.setData({ purchasing: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 直接购买解锁(9.9元)
|
||
* 使用 /api/payment/unified-order 接口
|
||
* 测试模式下返回 testMode: true,订单直接完成,无需调用微信支付
|
||
*/
|
||
async onPurchaseDirect() {
|
||
const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
|
||
if (!token) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||
setTimeout(() => {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
const { currentCharacter } = this.data
|
||
if (!currentCharacter) return
|
||
|
||
this.setData({ purchasing: true })
|
||
|
||
try {
|
||
wx.showLoading({ title: '创建订单中...' })
|
||
|
||
// 调用统一支付订单接口
|
||
const res = await api.payment.createUnifiedOrder({
|
||
type: 'character_unlock',
|
||
character_id: currentCharacter.id,
|
||
amount: 9.9
|
||
})
|
||
|
||
wx.hideLoading()
|
||
|
||
console.log('[onPurchaseDirect] API返回:', JSON.stringify(res))
|
||
|
||
if (res.success) {
|
||
// 检查是否为测试模式(兼容多种判断方式)
|
||
const isTestMode = res.testMode || res.test_mode || res.data?.testMode || res.data?.test_mode ||
|
||
// 如果 payParams.package 包含 mock_prepay,也认为是测试模式
|
||
(res.payParams?.package && res.payParams.package.includes('mock_prepay'))
|
||
|
||
if (isTestMode) {
|
||
// 测试模式:订单已直接完成,无需调用微信支付
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
|
||
// 记录已解锁
|
||
const newUnlocked = { ...this.data.unlockedProfiles }
|
||
newUnlocked[currentCharacter.id] = true
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
|
||
// 延迟后跳转到聊天页面
|
||
setTimeout(() => {
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${currentCharacter.id}&name=${encodeURIComponent(currentCharacter.name)}`
|
||
})
|
||
}, 1000)
|
||
} else {
|
||
// 正式模式:调用微信支付
|
||
const payParams = res.payParams || res.pay_params || res.data?.payParams || res.data?.pay_params
|
||
|
||
if (!payParams || !payParams.timeStamp) {
|
||
wx.showToast({ title: '支付参数错误', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.requestPayment({
|
||
timeStamp: payParams.timeStamp,
|
||
nonceStr: payParams.nonceStr,
|
||
package: payParams.package,
|
||
signType: payParams.signType || 'RSA',
|
||
paySign: payParams.paySign,
|
||
success: () => {
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
|
||
// 记录已解锁
|
||
const newUnlocked = { ...this.data.unlockedProfiles }
|
||
newUnlocked[currentCharacter.id] = true
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
|
||
// 延迟后跳转到聊天页面
|
||
setTimeout(() => {
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${currentCharacter.id}&name=${encodeURIComponent(currentCharacter.name)}`
|
||
})
|
||
}, 1000)
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||
wx.showToast({ title: '支付失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
} else {
|
||
wx.showToast({ title: res.message || '创建订单失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('购买解锁失败', err)
|
||
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||
} finally {
|
||
this.setData({ purchasing: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 购买并解锁角色聊天(套餐购买)
|
||
* 使用 /api/payment/unified-order 接口
|
||
* 测试模式下返回 testMode: true,订单直接完成,无需调用微信支付
|
||
*/
|
||
async buyAndUnlock() {
|
||
const token = wx.getStorageSync(config.STORAGE_KEYS.TOKEN)
|
||
if (!token) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||
setTimeout(() => {
|
||
wx.navigateTo({ url: '/pages/login/login' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
const { currentCharacter, heartPackages, selectedHeartPackage } = this.data
|
||
if (!currentCharacter) return
|
||
|
||
const selectedPackage = heartPackages[selectedHeartPackage]
|
||
|
||
if (!selectedPackage) {
|
||
wx.showToast({ title: '请选择套餐', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
this.setData({ purchasing: true })
|
||
|
||
try {
|
||
wx.showLoading({ title: '创建订单中...' })
|
||
|
||
// 调用统一支付订单接口
|
||
const res = await api.payment.createUnifiedOrder({
|
||
type: 'character_unlock',
|
||
character_id: currentCharacter.id,
|
||
amount: selectedPackage.price
|
||
})
|
||
|
||
wx.hideLoading()
|
||
|
||
console.log('[buyAndUnlock] API返回:', JSON.stringify(res))
|
||
|
||
if (res.success) {
|
||
// 检查是否为测试模式(兼容多种判断方式)
|
||
const isTestMode = res.testMode || res.test_mode || res.data?.testMode || res.data?.test_mode ||
|
||
// 如果 payParams.package 包含 mock_prepay,也认为是测试模式
|
||
(res.payParams?.package && res.payParams.package.includes('mock_prepay'))
|
||
|
||
if (isTestMode) {
|
||
// 测试模式:订单已直接完成,无需调用微信支付
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
|
||
// 刷新爱心余额
|
||
this.loadHeartBalance()
|
||
|
||
// 记录已解锁
|
||
const newUnlocked = { ...this.data.unlockedProfiles }
|
||
newUnlocked[currentCharacter.id] = true
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
|
||
// 延迟后跳转到聊天页面
|
||
setTimeout(() => {
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${currentCharacter.id}&name=${encodeURIComponent(currentCharacter.name)}`
|
||
})
|
||
}, 1000)
|
||
} else {
|
||
// 正式模式:调用微信支付
|
||
const payParams = res.payParams || res.pay_params || res.data?.payParams || res.data?.pay_params
|
||
|
||
if (!payParams || !payParams.timeStamp) {
|
||
wx.showToast({ title: '支付参数错误', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
wx.requestPayment({
|
||
timeStamp: payParams.timeStamp,
|
||
nonceStr: payParams.nonceStr,
|
||
package: payParams.package,
|
||
signType: payParams.signType || 'RSA',
|
||
paySign: payParams.paySign,
|
||
success: async () => {
|
||
wx.showToast({ title: '购买成功', icon: 'success' })
|
||
|
||
// 刷新爱心余额
|
||
this.loadHeartBalance()
|
||
|
||
// 记录已解锁
|
||
const newUnlocked = { ...this.data.unlockedProfiles }
|
||
newUnlocked[currentCharacter.id] = true
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showHeartPopup: false,
|
||
currentCharacter: null
|
||
})
|
||
|
||
// 延迟后跳转到聊天页面
|
||
setTimeout(() => {
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${currentCharacter.id}&name=${encodeURIComponent(currentCharacter.name)}`
|
||
})
|
||
}, 1000)
|
||
},
|
||
fail: (err) => {
|
||
if (err.errMsg !== 'requestPayment:fail cancel') {
|
||
wx.showToast({ title: '支付失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
}
|
||
} else {
|
||
wx.showToast({ title: res.message || '创建订单失败', icon: 'none' })
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('解锁角色失败', err)
|
||
wx.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||
} finally {
|
||
this.setData({ purchasing: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 跳转到用户协议
|
||
*/
|
||
goToUserAgreement() {
|
||
wx.navigateTo({ url: '/pages/agreement/agreement?code=user-agreement' })
|
||
},
|
||
|
||
/**
|
||
* 跳转到隐私政策
|
||
*/
|
||
goToPrivacyPolicy() {
|
||
wx.navigateTo({ url: '/pages/agreement/agreement?code=privacy-policy' })
|
||
},
|
||
|
||
onUpgrade() {
|
||
const { currentIndex, profiles, unlockedProfiles, heartCount, unlockHeartsCost } = this.data
|
||
|
||
if (currentIndex >= profiles.length) return
|
||
|
||
// 检查爱心值,不足时提示并跳转充值页面
|
||
if (heartCount < unlockHeartsCost) {
|
||
wx.showToast({ title: '爱心值不足,去充值', icon: 'none' })
|
||
setTimeout(() => {
|
||
this.setData({ showVipModal: false })
|
||
wx.navigateTo({ url: '/pages/recharge/recharge' })
|
||
}, 1500)
|
||
return
|
||
}
|
||
|
||
const currentId = profiles[currentIndex].id
|
||
|
||
const newUnlocked = { ...unlockedProfiles }
|
||
newUnlocked[currentId] = true
|
||
|
||
// 扣减爱心值
|
||
this.setData({
|
||
unlockedProfiles: newUnlocked,
|
||
showVipModal: false,
|
||
heartCount: heartCount - unlockHeartsCost
|
||
})
|
||
|
||
wx.showToast({
|
||
title: '兑换成功!',
|
||
icon: 'success'
|
||
})
|
||
},
|
||
|
||
onPurchase() {
|
||
// 跳转到充值页面
|
||
wx.navigateTo({
|
||
url: '/pages/recharge/recharge'
|
||
})
|
||
},
|
||
|
||
// ==================== 其他操作 ====================
|
||
|
||
onStoryTap(e) {
|
||
const index = e.currentTarget.dataset.index
|
||
const storyUser = this.data.storyUsers[index]
|
||
if (storyUser && storyUser.id) {
|
||
// 跳转到角色详情页
|
||
wx.navigateTo({
|
||
url: `/pages/character-detail/character-detail?id=${storyUser.id}`
|
||
})
|
||
} else {
|
||
wx.showToast({
|
||
title: storyUser.name + '的故事',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
},
|
||
|
||
onNotification() {
|
||
wx.showToast({
|
||
title: '暂无新消息',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
|
||
// Tab bar navigation - 需要登录的页面检查登录状态
|
||
switchTab(e) {
|
||
const path = e.currentTarget.dataset.path
|
||
const app = getApp()
|
||
|
||
// 消息页面需要登录
|
||
if (path === '/pages/chat/chat') {
|
||
if (!app.globalData.isLoggedIn) {
|
||
wx.navigateTo({
|
||
url: '/pages/login/login?redirect=' + encodeURIComponent(path)
|
||
})
|
||
return
|
||
}
|
||
}
|
||
wx.switchTab({ url: path })
|
||
},
|
||
|
||
onTest() {
|
||
wx.showToast({
|
||
title: '测试功能',
|
||
icon: 'none'
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 检查注册奖励领取资格
|
||
*/
|
||
async checkRegistrationReward() {
|
||
if (!app.globalData.isLoggedIn) return
|
||
|
||
try {
|
||
const res = await api.lovePoints.checkRegistrationReward()
|
||
console.log('[index] 注册奖励检查结果:', res)
|
||
if (res.success && res.data && res.data.eligible) {
|
||
this.setData({
|
||
showRegistrationReward: true,
|
||
registrationRewardAmount: res.data.amount || 0
|
||
})
|
||
}
|
||
} catch (err) {
|
||
console.error('[index] 检查注册奖励失败:', err)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 领取注册奖励
|
||
*/
|
||
async onClaimReward() {
|
||
if (this.data.claiming) return
|
||
|
||
this.setData({ claiming: true })
|
||
wx.showLoading({ title: '领取中...' })
|
||
|
||
try {
|
||
const res = await api.lovePoints.claimRegistrationReward()
|
||
wx.hideLoading()
|
||
|
||
if (res.success) {
|
||
wx.showToast({
|
||
title: '领取成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
this.setData({
|
||
showRegistrationReward: false
|
||
})
|
||
|
||
// 如果后端返回了免费畅聊时间,可以做提示
|
||
if (res.data && res.data.free_chat_time) {
|
||
console.log('[index] 获得免费畅聊时间:', res.data.free_chat_time);
|
||
setTimeout(() => {
|
||
wx.showModal({
|
||
title: '领取成功',
|
||
content: '恭喜获得 100 爱心 + 60 分钟免费畅聊时间!',
|
||
confirmText: '去聊天',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
// 如果有当前正在查看的角色,直接跳过去
|
||
const { currentIndex, profiles } = this.data
|
||
if (profiles && profiles[currentIndex]) {
|
||
const char = profiles[currentIndex]
|
||
wx.navigateTo({
|
||
url: `/pages/chat-detail/chat-detail?id=${char.id}&name=${encodeURIComponent(char.name)}`
|
||
})
|
||
} else {
|
||
wx.switchTab({ url: '/pages/chat/chat' })
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}, 2000);
|
||
}
|
||
|
||
// 刷新余额
|
||
this.loadHeartBalance()
|
||
} else {
|
||
wx.showToast({
|
||
title: res.message || '领取失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('[index] 领取注册奖励失败:', err)
|
||
wx.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.setData({ claiming: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 关闭注册奖励弹窗
|
||
*/
|
||
closeRewardPopup() {
|
||
this.setData({
|
||
showRegistrationReward: false
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 检查 GF100 弹窗状态
|
||
*/
|
||
async checkGf100Popup() {
|
||
if (!app.globalData.isLoggedIn) return
|
||
|
||
try {
|
||
const res = await api.lovePoints.checkGf100Status()
|
||
console.log('[index] GF100 检查结果:', res)
|
||
if (res.success && res.data && res.data.showPopup) {
|
||
const imageUrl = res.data.imageUrl;
|
||
this.setData({
|
||
showGf100Popup: true,
|
||
gf100ImageUrl: imageUrl ? util.getFullImageUrl(imageUrl) : '/images/gf100.png'
|
||
})
|
||
}
|
||
} catch (err) {
|
||
console.error('[index] 检查 GF100 弹窗失败:', err)
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 领取 GF100 奖励
|
||
*/
|
||
async onClaimGf100() {
|
||
if (this.data.claiming) return
|
||
|
||
this.setData({ claiming: true })
|
||
wx.showLoading({ title: '领取中...', mask: true })
|
||
|
||
try {
|
||
const res = await api.lovePoints.claimGf100()
|
||
wx.hideLoading()
|
||
|
||
if (res.success) {
|
||
wx.showToast({
|
||
title: '领取成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
this.setData({
|
||
showGf100Popup: false
|
||
})
|
||
|
||
// 弹出获得 60 分钟免费畅聊的提示
|
||
setTimeout(() => {
|
||
wx.showModal({
|
||
title: '领取成功',
|
||
content: '恭喜获得 100 爱心 + 60 分钟免费畅聊时间!',
|
||
confirmText: '去聊天',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
wx.switchTab({ url: '/pages/chat/chat' })
|
||
}
|
||
}
|
||
});
|
||
}, 500);
|
||
|
||
// 刷新余额
|
||
this.loadHeartBalance()
|
||
} else {
|
||
wx.showToast({
|
||
title: res.message || '领取失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
console.error('[index] 领取 GF100 失败:', err)
|
||
wx.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.setData({ claiming: false })
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 关闭 GF100 弹窗
|
||
*/
|
||
closeGf100Popup() {
|
||
this.setData({
|
||
showGf100Popup: false
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 用户点击右上角分享
|
||
*/
|
||
onShareAppMessage() {
|
||
const { shareConfig } = this.data
|
||
const referralCode = wx.getStorageSync('referralCode') || ''
|
||
|
||
api.promotion.recordShare({
|
||
type: 'app_message',
|
||
page: '/pages/index/index',
|
||
referralCode: referralCode
|
||
}).catch(err => console.error('记录分享失败:', err))
|
||
|
||
this.recordShareReward()
|
||
|
||
if (shareConfig) {
|
||
return {
|
||
title: shareConfig.title,
|
||
path: `${shareConfig.path}?referralCode=${referralCode}`,
|
||
imageUrl: shareConfig.imageUrl
|
||
}
|
||
}
|
||
|
||
return {
|
||
title: '欢迎来到心伴俱乐部',
|
||
desc: '随时可聊 一直陪伴',
|
||
path: `/pages/index/index?referralCode=${referralCode}`,
|
||
imageUrl: '/images/icon-heart-new.png'
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 用户分享到朋友圈
|
||
*/
|
||
onShareTimeline() {
|
||
const { shareConfig } = this.data
|
||
const referralCode = wx.getStorageSync('referralCode') || ''
|
||
|
||
api.promotion.recordShare({
|
||
type: 'timeline',
|
||
page: '/pages/index/index',
|
||
referralCode: referralCode
|
||
}).catch(err => console.error('记录分享失败:', err))
|
||
|
||
this.recordShareReward()
|
||
|
||
if (shareConfig) {
|
||
return {
|
||
title: shareConfig.title,
|
||
query: `referralCode=${referralCode}`,
|
||
imageUrl: shareConfig.imageUrl
|
||
}
|
||
}
|
||
|
||
return {
|
||
title: '心伴俱乐部 - 随时可聊 一直陪伴',
|
||
query: `referralCode=${referralCode}`,
|
||
imageUrl: '/images/icon-heart-new.png'
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 静默记录分享奖励(分享人A获得+100爱心值)
|
||
*/
|
||
async recordShareReward() {
|
||
try {
|
||
const res = await api.lovePoints.share()
|
||
console.log('[index] 分享爱心值奖励:', res)
|
||
} catch (err) {
|
||
console.error('[index] 记录分享奖励失败:', err)
|
||
}
|
||
}
|
||
})
|