641 lines
17 KiB
JavaScript
641 lines
17 KiB
JavaScript
const api = require('../../utils/api');
|
||
const util = require('../../utils/util');
|
||
const app = getApp();
|
||
|
||
Page({
|
||
data: {
|
||
defaultAvatar: '/images/default-avatar.svg',
|
||
me: {
|
||
id: '',
|
||
idShort: '',
|
||
nickname: '未登录',
|
||
avatar: '',
|
||
phone: ''
|
||
},
|
||
vip: {
|
||
levelText: '',
|
||
expireText: ''
|
||
},
|
||
balances: {
|
||
grass: 0,
|
||
commission: '0.00'
|
||
},
|
||
counts: {
|
||
orders: 0,
|
||
team: 0,
|
||
performance: 0
|
||
},
|
||
isLoggedIn: false,
|
||
isDistributor: false,
|
||
referralCode: '',
|
||
statusBarHeight: 20,
|
||
totalUnread: 0,
|
||
|
||
// Modal States
|
||
showPromoterModal: false,
|
||
showPromoterSuccess: false,
|
||
|
||
// 注册奖励
|
||
showRegistrationReward: false,
|
||
registrationRewardAmount: 0,
|
||
claiming: false,
|
||
auditStatus: 0,
|
||
|
||
// GF100 弹窗
|
||
showGf100Popup: false,
|
||
gf100ImageUrl: ''
|
||
},
|
||
|
||
onShow() {
|
||
this.setData({
|
||
statusBarHeight: wx.getSystemInfoSync().statusBarHeight,
|
||
auditStatus: app.globalData.auditStatus
|
||
});
|
||
wx.hideTabBar({ animation: false });
|
||
this.loadAll();
|
||
},
|
||
|
||
async loadAll() {
|
||
const isLoggedIn = app.globalData.isLoggedIn || !!wx.getStorageSync('auth_token');
|
||
this.setData({ isLoggedIn });
|
||
|
||
wx.showNavigationBarLoading();
|
||
try {
|
||
if (isLoggedIn) {
|
||
await Promise.all([
|
||
this.loadMe(),
|
||
this.loadBalance(),
|
||
this.loadCommission(),
|
||
this.loadCounts(),
|
||
this.loadUnreadCount()
|
||
]);
|
||
this.checkRegistrationReward();
|
||
this.checkGf100Popup();
|
||
} else {
|
||
this.setData({
|
||
me: { nickname: '未登录', avatar: this.data.defaultAvatar },
|
||
balances: { grass: 0, commission: '0.00' },
|
||
counts: { orders: 0, team: 0, performance: 0 },
|
||
totalUnread: 0
|
||
});
|
||
}
|
||
} catch (err) {
|
||
console.error('Load profile failed', err);
|
||
} finally {
|
||
wx.hideNavigationBarLoading();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 检查是否需要登录
|
||
* @returns {boolean} 是否已登录
|
||
*/
|
||
requireLogin() {
|
||
const isLoggedIn = app.globalData.isLoggedIn || !!wx.getStorageSync('auth_token');
|
||
if (!isLoggedIn) {
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: '请先登录后再操作',
|
||
confirmText: '去登录',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
wx.navigateTo({
|
||
url: '/pages/login/login'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
|
||
async loadMe() {
|
||
try {
|
||
// Use direct request with cache busting to ensure fresh data
|
||
const res = await api.request('/auth/me', { data: { _t: Date.now() } });
|
||
// Correctly unwrap the data object from the response
|
||
const user = (res && res.data) ? res.data : {};
|
||
const id = user.id || '';
|
||
const idShort = id ? String(id).substring(0, 8).toUpperCase() : '';
|
||
const nickname = user.nickname || user.name || '微信用户';
|
||
|
||
// 处理头像:如果是默认头像则不处理,否则拼接完整路径
|
||
let avatar = user.avatar || user.avatar_url;
|
||
if (avatar) {
|
||
avatar = util.getFullImageUrl(avatar);
|
||
} else {
|
||
avatar = this.data.defaultAvatar;
|
||
}
|
||
|
||
const phone = user.phone || user.mobile || '';
|
||
|
||
// Determine Display Role
|
||
const distributorRole = user.distributorRole || user.role;
|
||
let roleText = '';
|
||
let roleClass = '';
|
||
|
||
const roleMap = {
|
||
'soulmate': { text: '心伴会员', class: 'vip-soulmate' },
|
||
'guardian': { text: '守护会员', class: 'vip-guardian' },
|
||
'companion': { text: '陪伴会员', class: 'vip-companion' },
|
||
'listener': { text: '倾听会员', class: 'vip-listener' },
|
||
'partner': { text: '城市合伙人', class: 'vip-partner' }
|
||
};
|
||
|
||
if (user.isDistributor && roleMap[distributorRole]) {
|
||
const info = roleMap[distributorRole];
|
||
roleText = info.text;
|
||
roleClass = info.class;
|
||
} else {
|
||
// Fallback to VIP
|
||
const vipLevel = Number(user.vip_level || 0);
|
||
if (vipLevel > 0) {
|
||
roleText = vipLevel >= 2 ? 'SVIP' : 'VIP';
|
||
roleClass = 'vip-normal';
|
||
}
|
||
}
|
||
|
||
this.setData({
|
||
me: { id, idShort, nickname, avatar, phone },
|
||
vip: { levelText: roleText || '', levelClass: roleClass },
|
||
isDistributor: !!user.isDistributor
|
||
});
|
||
|
||
// CRITICAL: Update global data and local storage to ensure avatar consistency across pages
|
||
if (app && app.setUserInfo) {
|
||
app.setUserInfo(user);
|
||
} else {
|
||
// Fallback if app.setUserInfo is not available
|
||
app.globalData.userInfo = user;
|
||
app.globalData.userId = user.id;
|
||
wx.setStorageSync('user_info', user);
|
||
}
|
||
} catch (e) {
|
||
console.error('loadMe error', e);
|
||
}
|
||
},
|
||
|
||
async loadBalance() {
|
||
try {
|
||
const res = await api.user.getBalance();
|
||
const data = (res && res.data) ? res.data : res;
|
||
const balance = data.flower_balance || data.balance || 0;
|
||
this.setData({
|
||
'balances.grass': Number(balance)
|
||
});
|
||
} catch (e) {
|
||
console.error('loadBalance error', e);
|
||
}
|
||
},
|
||
|
||
async loadCommission() {
|
||
try {
|
||
const res = await api.commission.getStats();
|
||
const data = (res && res.data) ? res.data : res;
|
||
const commission = data.commissionBalance || data.commission_balance || 0;
|
||
// Use individualPerformance for "Performance Data" card as per documentation
|
||
const performance = data.individualPerformance || data.individual_performance || data.totalContribution || data.total_contribution || 0;
|
||
|
||
this.setData({
|
||
'balances.commission': Number(commission).toFixed(2),
|
||
'counts.performance': this.formatAmount(performance)
|
||
});
|
||
} catch (e) {
|
||
console.error('loadCommission error', e);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 格式化金额,超过一万显示为“x.x万”
|
||
* @param {number|string} val
|
||
*/
|
||
formatAmount(val) {
|
||
const num = Number(val || 0);
|
||
if (num >= 10000) {
|
||
return (num / 10000).toFixed(1) + '万';
|
||
}
|
||
return num.toFixed(2);
|
||
},
|
||
|
||
async loadCounts() {
|
||
try {
|
||
// 1. Fetch self orders (all statuses)
|
||
const selfOrdersRes = await api.payment.getOrders({ page: 1, pageSize: 1 });
|
||
const selfCount = (selfOrdersRes.data && selfOrdersRes.data.total) ? selfOrdersRes.data.total : 0;
|
||
|
||
// 2. Fetch promotion orders (records) - usually pageSize=1 is enough to get total
|
||
// Note: check backend response structure for total count
|
||
const promoRes = await api.commission.getRecords({ page: 1, pageSize: 1 });
|
||
const promoCount = (promoRes.data && promoRes.data.total) ? promoRes.data.total : 0;
|
||
|
||
// 3. Fetch team members count
|
||
const teamRes = await api.commission.getReferrals({ page: 1, pageSize: 1 });
|
||
const teamCount = (teamRes.data && teamRes.data.total) ? teamRes.data.total : 0;
|
||
|
||
this.setData({
|
||
'counts.orders': selfCount + promoCount,
|
||
'counts.team': teamCount
|
||
});
|
||
} catch (err) {
|
||
console.error('loadCounts failed', err);
|
||
// Keep existing data or set to 0 on error
|
||
// this.setData({ 'counts.orders': 0, 'counts.team': 0 });
|
||
}
|
||
},
|
||
|
||
async loadUnreadCount() {
|
||
if (!this.data.isLoggedIn) {
|
||
this.setData({ totalUnread: 0 });
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const [convRes, proactiveRes] = await Promise.all([
|
||
api.chat.getConversations(),
|
||
api.proactiveMessage.getPending ? api.proactiveMessage.getPending() : Promise.resolve({ success: true, data: [] })
|
||
]);
|
||
|
||
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;
|
||
}
|
||
|
||
this.setData({ totalUnread });
|
||
} catch (err) {
|
||
console.log('获取未读消息数失败', err);
|
||
this.setData({ totalUnread: 0 });
|
||
}
|
||
},
|
||
|
||
goSettings() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/settings/settings' });
|
||
}
|
||
},
|
||
goEdit() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/edit-profile/edit-profile' });
|
||
}
|
||
},
|
||
goRecharge() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/recharge/recharge' });
|
||
}
|
||
},
|
||
goOrders() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/orders/orders' });
|
||
}
|
||
},
|
||
goTeam() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/team/team' });
|
||
}
|
||
},
|
||
goGiftShop() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/gift-shop/gift-shop' });
|
||
}
|
||
},
|
||
goBackpack() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/backpack/backpack' });
|
||
}
|
||
},
|
||
goMyActivities() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/my-activities/my-activities' });
|
||
}
|
||
},
|
||
goWithdraw() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/withdraw/withdraw' });
|
||
}
|
||
},
|
||
goCommission() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/commission/commission' });
|
||
}
|
||
},
|
||
goPerformance() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/performance/performance' });
|
||
}
|
||
},
|
||
goCooperation() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/subpackages/cooperation/pages/cooperation/cooperation' });
|
||
}
|
||
},
|
||
goSupport() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/support/support' });
|
||
}
|
||
},
|
||
goPromote() {
|
||
if (this.requireLogin()) {
|
||
wx.navigateTo({ url: '/pages/promote/promote' });
|
||
}
|
||
},
|
||
|
||
onAvatarError() {
|
||
this.setData({
|
||
'me.avatar': this.data.defaultAvatar
|
||
});
|
||
},
|
||
|
||
async handleLogout() {
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: '确定要退出登录吗?',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
await api.auth.logout();
|
||
} catch (e) {
|
||
console.error('logout api error', e);
|
||
}
|
||
|
||
wx.removeStorageSync('auth_token');
|
||
wx.removeStorageSync('user_info');
|
||
|
||
app.globalData.isLoggedIn = false;
|
||
app.globalData.userInfo = null;
|
||
|
||
this.loadAll();
|
||
|
||
wx.showToast({
|
||
title: '已退出登录',
|
||
icon: 'success'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 检查注册奖励领取资格
|
||
*/
|
||
async checkRegistrationReward() {
|
||
try {
|
||
if (!api.lovePoints || typeof api.lovePoints.checkRegistrationReward !== 'function') {
|
||
return;
|
||
}
|
||
// 检查注册奖励领取资格 - 增加静默处理,避免 404 时控制台报错
|
||
const res = await api.lovePoints.checkRegistrationReward();
|
||
if (res && res.success && res.data && res.data.eligible) {
|
||
this.setData({
|
||
showRegistrationReward: true,
|
||
registrationRewardAmount: res.data.amount || 100
|
||
});
|
||
}
|
||
} catch (err) {
|
||
// 生产环境可能未部署此接口,静默处理
|
||
if (err.code !== 404) {
|
||
console.warn('[profile] 检查注册奖励静默失败:', err.message || '接口可能未部署');
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 领取注册奖励
|
||
*/
|
||
async onClaimReward() {
|
||
if (this.data.claiming) return;
|
||
|
||
if (!api.lovePoints || typeof api.lovePoints.claimRegistrationReward !== 'function') {
|
||
wx.showToast({ title: '功能暂不可用', icon: 'none' });
|
||
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('[profile] 获得免费畅聊时间:', res.data.free_chat_time);
|
||
setTimeout(() => {
|
||
wx.showModal({
|
||
title: '额外奖励',
|
||
content: '恭喜获得 60 分钟免费畅聊时间,现在就去和 AI 角色聊天吧!',
|
||
confirmText: '去聊天',
|
||
success: (modalRes) => {
|
||
if (modalRes.confirm) {
|
||
wx.switchTab({ url: '/pages/index/index' });
|
||
}
|
||
}
|
||
});
|
||
}, 2000);
|
||
}
|
||
|
||
// 刷新余额
|
||
this.loadBalance();
|
||
} else {
|
||
wx.showToast({
|
||
title: res.message || '领取失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading();
|
||
console.error('[profile] 领取注册奖励失败:', err);
|
||
wx.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
this.setData({ claiming: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 关闭注册奖励弹窗
|
||
*/
|
||
closeRewardPopup() {
|
||
this.setData({
|
||
showRegistrationReward: false
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 检查 GF100 弹窗状态
|
||
*/
|
||
async checkGf100Popup() {
|
||
if (!this.data.isLoggedIn) return;
|
||
|
||
try {
|
||
const res = await api.lovePoints.checkGf100Status();
|
||
console.log('[profile] 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('[profile] 检查 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.loadBalance();
|
||
} else {
|
||
wx.showToast({
|
||
title: res.message || '领取失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
} catch (err) {
|
||
wx.hideLoading();
|
||
console.error('[profile] 领取 GF100 失败:', err);
|
||
wx.showToast({
|
||
title: '网络错误,请重试',
|
||
icon: 'none'
|
||
});
|
||
} finally {
|
||
this.setData({ claiming: false });
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 关闭 GF100 弹窗
|
||
*/
|
||
closeGf100Popup() {
|
||
this.setData({
|
||
showGf100Popup: false
|
||
});
|
||
},
|
||
|
||
preventBubble() { },
|
||
preventTouchMove() { },
|
||
|
||
async onLoad() {
|
||
await this.loadReferralCode()
|
||
},
|
||
|
||
async loadReferralCode() {
|
||
if (!this.data.isLoggedIn) return
|
||
try {
|
||
const res = await api.commission.getStats()
|
||
if (res.success && res.data) {
|
||
this.setData({ referralCode: res.data.referralCode || '' })
|
||
}
|
||
} catch (err) {
|
||
console.error('加载推荐码失败:', err)
|
||
}
|
||
},
|
||
|
||
onShareAppMessage() {
|
||
const { referralCode, isDistributor } = this.data
|
||
const referralCodeParam = referralCode ? `?referralCode=${referralCode}` : ''
|
||
|
||
this.recordShareReward()
|
||
|
||
return {
|
||
title: isDistributor ? `我的推荐码:${referralCode},注册即可享受优惠!` : '欢迎来到心伴俱乐部 - 随时可聊 一直陪伴',
|
||
path: `/pages/index/index${referralCodeParam}`,
|
||
imageUrl: isDistributor ? '/images/share-commission.png' : '/images/icon-heart-new.png'
|
||
}
|
||
},
|
||
|
||
onShareTimeline() {
|
||
const { referralCode, isDistributor } = this.data
|
||
const query = referralCode ? `referralCode=${referralCode}` : ''
|
||
|
||
this.recordShareReward()
|
||
|
||
return {
|
||
title: isDistributor ? `推荐码:${referralCode},注册即可享受优惠!` : '心伴俱乐部 - 随时可聊 一直陪伴',
|
||
query: query,
|
||
imageUrl: isDistributor ? '/images/share-commission.png' : '/images/icon-heart-new.png'
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 静默记录分享奖励(分享人A获得+100爱心值)
|
||
*/
|
||
async recordShareReward() {
|
||
try {
|
||
const res = await api.lovePoints.share()
|
||
console.log('[profile] 分享爱心值奖励:', res)
|
||
} catch (err) {
|
||
console.error('[profile] 记录分享奖励失败:', err)
|
||
}
|
||
},
|
||
|
||
// Tab Bar Switching
|
||
switchTab(e) {
|
||
const path = e.currentTarget.dataset.path;
|
||
const app = getApp();
|
||
|
||
if (path === '/pages/chat/chat') {
|
||
if (!app.globalData.isLoggedIn && !wx.getStorageSync('auth_token')) {
|
||
wx.navigateTo({
|
||
url: '/pages/login/login?redirect=' + encodeURIComponent(path)
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
wx.switchTab({ url: path });
|
||
}
|
||
});
|