292 lines
8.5 KiB
JavaScript
292 lines
8.5 KiB
JavaScript
// pages/profile/index.js
|
|
import request from '../../utils/request';
|
|
import { calculateDaysSince } from '../../utils/dateUtil';
|
|
|
|
const app = getApp();
|
|
|
|
Page({
|
|
data: {
|
|
view: 'profile', // profile, favorites, posts, about
|
|
|
|
// User Info
|
|
userName: '植物爱好者',
|
|
userAvatar: '',
|
|
userLevel: '', // Reserved for future level system
|
|
userLevelTag: '', // e.g. 'Lv.4 资深植人'
|
|
|
|
// Stats
|
|
plantCount: 0,
|
|
taskDoneCount: 0,
|
|
postCount: 0,
|
|
|
|
// Favorites
|
|
favTab: 'all',
|
|
favorites: [],
|
|
filteredFavorites: [],
|
|
|
|
// Posts
|
|
postsTab: 'published',
|
|
myPublishedPosts: [],
|
|
myDrafts: [],
|
|
|
|
// App version
|
|
// App version
|
|
appVersion: '1.0.0',
|
|
scrollTop: 0
|
|
},
|
|
|
|
onLoad() {
|
|
this.loadUserInfo();
|
|
},
|
|
|
|
onShow() {
|
|
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
|
this.getTabBar().setData({ selected: 4 });
|
|
}
|
|
// Always fetch fresh profile data
|
|
this.loadUserInfo();
|
|
},
|
|
|
|
onTabItemTap() {
|
|
this.setData({
|
|
view: 'profile',
|
|
scrollTop: Math.random() * 0.01
|
|
});
|
|
},
|
|
|
|
// ======== User Info ========
|
|
loadUserInfo() {
|
|
request.get('/profile/detail').then(res => {
|
|
if (!res) return;
|
|
|
|
// Map stats and level info
|
|
const avatarUrl = res.avatar && res.avatar.url ? res.avatar.url : '';
|
|
const levelInfo = res.level || {};
|
|
const levelTag = levelInfo.level ? `Lv.${levelInfo.level} ${levelInfo.title || ''}` : '';
|
|
|
|
this.setData({
|
|
userName: res.nickname || '植物爱好者',
|
|
userAvatar: avatarUrl,
|
|
currentAvatarId: res.avatarId || (res.avatar ? res.avatar.id : ''),
|
|
|
|
// Stats
|
|
plantCount: res.plantCount || 0,
|
|
taskDoneCount: res.careCount || 0,
|
|
postCount: res.postCount || 0,
|
|
|
|
// Level (if available)
|
|
userLevel: levelInfo.level || 0,
|
|
userLevelTag: levelTag,
|
|
|
|
// EXP / Sunlight
|
|
userExp: res.currentSunlight || 0,
|
|
userSunlight: res.currentSunlight || 0,
|
|
joinedDays: calculateDaysSince(res.createdAt)
|
|
});
|
|
|
|
// Update global cache
|
|
const info = {
|
|
...res,
|
|
avatarId: res.avatarId || (res.avatar ? res.avatar.id : '')
|
|
};
|
|
app.globalData.userInfo = info;
|
|
wx.setStorageSync('userInfo', info);
|
|
|
|
}).catch(err => {
|
|
console.error('Load profile failed', err);
|
|
});
|
|
},
|
|
|
|
// ======== Stats ========
|
|
|
|
|
|
// ======== Navigation ========
|
|
goBack() {
|
|
this.setData({ view: 'profile' });
|
|
},
|
|
|
|
goToFavorites() {
|
|
wx.navigateTo({ url: '/pages/profile/favorites/index' });
|
|
},
|
|
|
|
goToPosts() {
|
|
wx.navigateTo({ url: '/pages/profile/posts/index' });
|
|
},
|
|
|
|
// ======== Menu Actions ========
|
|
goToExchange() {
|
|
wx.navigateTo({ url: '/pages/profile/exchange/index' });
|
|
},
|
|
|
|
goToIdentifyHistory() {
|
|
wx.navigateTo({ url: '/pages/profile/identify-history/index' });
|
|
},
|
|
|
|
goToBadges() {
|
|
wx.navigateTo({ url: '/pages/profile/badges/index' });
|
|
},
|
|
|
|
goToNotificationSettings() {
|
|
// Open WeChat notification settings
|
|
wx.openSetting({
|
|
success: (res) => {
|
|
|
|
}
|
|
});
|
|
},
|
|
|
|
goToAbout() {
|
|
wx.navigateTo({ url: '/pages/profile/about/index' });
|
|
},
|
|
|
|
openDoc(e) {
|
|
if (wx.openPrivacyContract) {
|
|
wx.openPrivacyContract({
|
|
fail: () => {
|
|
wx.showToast({ title: '无法打开协议', icon: 'none' });
|
|
}
|
|
});
|
|
} else {
|
|
wx.showToast({ title: '当前微信版本不支持查看', icon: 'none' });
|
|
}
|
|
},
|
|
|
|
// ======== Profile Editor Popup ========
|
|
openProfileEditor() {
|
|
this.setData({
|
|
showProfileEditor: true,
|
|
tempAvatar: '',
|
|
tempNickname: this.data.userName === '植物爱好者' ? '' : this.data.userName
|
|
});
|
|
},
|
|
|
|
closeProfileEditor() {
|
|
this.setData({ showProfileEditor: false });
|
|
},
|
|
|
|
onProfilePopupChange(e) {
|
|
if (!e.detail.visible) {
|
|
this.setData({ showProfileEditor: false });
|
|
}
|
|
},
|
|
|
|
// WeChat native chooseAvatar callback
|
|
onChooseAvatar(e) {
|
|
const avatarUrl = e.detail.avatarUrl;
|
|
if (avatarUrl) {
|
|
this.setData({ tempAvatar: avatarUrl });
|
|
}
|
|
},
|
|
|
|
onNicknameInput(e) {
|
|
this.setData({ tempNickname: e.detail.value });
|
|
},
|
|
|
|
onNicknameBlur(e) {
|
|
// WeChat nickname type may return value on blur
|
|
if (e.detail.value) {
|
|
this.setData({ tempNickname: e.detail.value });
|
|
}
|
|
},
|
|
|
|
async saveProfile() {
|
|
const { tempAvatar, tempNickname, userName, userAvatar, currentAvatarId } = this.data;
|
|
|
|
// Determine if there are changes
|
|
// tempNickname might be undefined if user didn't edit nickname input
|
|
const finalNickname = tempNickname !== undefined ? tempNickname : userName;
|
|
const isNameChanged = finalNickname !== userName;
|
|
const isAvatarChanged = tempAvatar && tempAvatar !== userAvatar;
|
|
|
|
if (!isNameChanged && !isAvatarChanged) {
|
|
this.setData({ showProfileEditor: false });
|
|
return;
|
|
}
|
|
|
|
if (!finalNickname.trim()) {
|
|
wx.showToast({ title: '昵称不能为空', icon: 'none' });
|
|
return;
|
|
}
|
|
|
|
wx.showLoading({ title: '保存中...', mask: true });
|
|
|
|
try {
|
|
let finalAvatarId = currentAvatarId;
|
|
|
|
// Upload new avatar if changed
|
|
if (isAvatarChanged) {
|
|
const uploadRes = await request.upload(tempAvatar);
|
|
if (uploadRes && uploadRes.file && uploadRes.file.id) {
|
|
finalAvatarId = uploadRes.file.id;
|
|
} else if (uploadRes && uploadRes.id) {
|
|
finalAvatarId = uploadRes.id;
|
|
} else {
|
|
throw new Error('Avatar upload failed, no ID returned');
|
|
}
|
|
}
|
|
|
|
// Construct Full Payload
|
|
const updatePayload = {
|
|
nickname: finalNickname,
|
|
avatarId: finalAvatarId
|
|
};
|
|
|
|
// Call API
|
|
await request.post('/profile/update', updatePayload);
|
|
|
|
// Update UI State
|
|
this.setData({
|
|
userName: finalNickname,
|
|
userAvatar: tempAvatar || userAvatar,
|
|
currentAvatarId: finalAvatarId,
|
|
showProfileEditor: false
|
|
});
|
|
|
|
wx.showToast({ title: '资料已更新', icon: 'success' });
|
|
|
|
// Update Global Data
|
|
const userInfo = app.globalData.userInfo || {};
|
|
userInfo.nickname = finalNickname;
|
|
userInfo.name = finalNickname;
|
|
// Update avatar structure in global store too
|
|
if (isAvatarChanged) {
|
|
userInfo.avatar = { ...(userInfo.avatar || {}), url: tempAvatar, id: finalAvatarId };
|
|
}
|
|
userInfo.avatarId = finalAvatarId;
|
|
|
|
app.globalData.userInfo = userInfo;
|
|
wx.setStorageSync('userInfo', userInfo);
|
|
|
|
} catch (err) {
|
|
console.error('Save profile failed', err);
|
|
wx.showToast({ title: '保存失败', icon: 'none' });
|
|
} finally {
|
|
wx.hideLoading();
|
|
}
|
|
},
|
|
|
|
// ======== Utilities ========
|
|
_formatTime(dateStr) {
|
|
if (!dateStr) return '';
|
|
const d = new Date(dateStr);
|
|
const now = new Date();
|
|
const diffMs = now - d;
|
|
const diffMin = Math.floor(diffMs / 60000);
|
|
if (diffMin < 1) return '刚刚';
|
|
if (diffMin < 60) return diffMin + '分钟前';
|
|
const diffHour = Math.floor(diffMin / 60);
|
|
if (diffHour < 24) return diffHour + '小时前';
|
|
const diffDay = Math.floor(diffHour / 24);
|
|
if (diffDay < 7) return diffDay + '天前';
|
|
|
|
const month = (d.getMonth() + 1).toString().padStart(2, '0');
|
|
const day = d.getDate().toString().padStart(2, '0');
|
|
return `${month}-${day}`;
|
|
},
|
|
|
|
// ======== Reserved: Future Level/Badge System ========
|
|
// These methods will be implemented when the backend supports level/badge APIs
|
|
// loadLevelInfo() { request.get('/user/level').then(...) },
|
|
// loadBadges() { request.get('/user/badges').then(...) },
|
|
})
|