const apiClient = require('../../../utils/api-client.js'); const nimUserManager = require('../../../utils/nim-user-manager.js'); const { calculateConstellation, calculateConstellationFromBirthday } = require('../../../utils/formatDate.js'); const cosManager = require('../../../utils/cos-manager.js'); const occupationOptions = [ "初中生", "高中生", "大学生", "研究生", "留学生", "科研", "警察", "医生", "护士", "程序员", "老师", "化妆师", "摄影师", "音乐", "美术", "金融", "厨师", "工程师", "公务员", "互联网", "产品经理", "模特", "演员", "导演", "律师", "创业者", "其他" ]; const schoolOptions = ["博士", "硕士", "本科", "专科", "中专", "高中", "初中", "小学", "其他"]; const genderOptions = ["未知", "男", "女", "其他"]; const genderMap = { "未知": 0, "男": 1, "女": 2, "其他": 3 }; // 反向映射:数字转文字 const genderReverseMap = { 0: "未知", 1: "男", 2: "女", 3: "其他" }; const zodiacSignOptions = ["双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座", "水瓶座"]; const mbtiTypeOptions = ["INTJ", "INTP", "ENTJ", "INFP", "ENTP", "INFJ", "ENFP", "ENFJ", "ISTJ", "ISFJ", "ISTP", "ISFP", "ESTJ", "ESFJ", "ESTP", "ESFP"]; const sleepHabitOptions = ["早起鸟儿", "夜猫子", "规律型", "深度睡眠追求者", "碎片化睡眠者", "失眠困扰者", "咖啡因敏感型", "数字戒断者", "运动调节型", "挑战打卡型", "鼾声监测者", "生物钟调节者", "社区分享型"]; const socialActivityOptions = ["内容创作者", "观察者", "吃瓜者", "潜水者", "机器人", "社群型用户", "KOL", "KOC", "普通用户", "算法依赖型用户", "事件驱动型用户", "季节性活跃用户", "社交维系型用户", "兴趣社群型用户", "职业网络型用户", "娱乐消遣型用户", "购物种草型用户", "互动型用户"]; const heightOptions = Array.from({ length: 71 }, (_, i) => (140 + i)); Page({ data: { occupationOptions, schoolOptions, genderOptions, zodiacSignOptions, mbtiTypeOptions, sleepHabitOptions, socialActivityOptions, heightOptions, user: { nickname: "", avatar: "", bio: "", gender: 0, // 0=未知 1=男 2=女 3=其他 birthday: "", ethnicity: "", occupation: "", school: "", hometown: [], zodiacSign: "", height: 0, mbtiType: "", sleepHabit: "", socialActivity: "" }, // 性别显示文字(用于 WXML 显示) genderDisplayText: "请选择", // sheet 控制 showSheet: false, sheetOptions: [], sheetTitle: "", sheetKey: "", selectedValue: "", // 下拉刷新 refreshing: false }, getKeyByValue(obj, value) { return Object.keys(obj).find(key => obj[key] === value); }, // 转换用户数据格式:从旧格式转换为新格式 transformUserData(data) { if (!data) return this.clearObjectValues(this.data.user); const transformed = { nickname: data.nickname || "", avatar: data.avatar || "", bio: data.bio || "", gender: typeof data.gender === 'number' ? data.gender : (genderMap[data.gender] !== undefined ? genderMap[data.gender] : 0), birthday: data.birthday || "", ethnicity: data.ethnicity || "", occupation: data.occupation || "", school: data.school || "", // 兼容旧字段 education hometown: Array.isArray(data.hometown) ? data.hometown : [], zodiacSign: data.zodiacSign || "", // 兼容旧字段 constellation height: typeof data.height === 'number' ? data.height : (data.height ? parseInt(data.height) || 0 : 0), mbtiType: data.mbtiType || "", // 兼容旧字段 personality sleepHabit: data.sleepHabit || "", // 兼容旧字段 sleep socialActivity: data.socialActivity || "" // 兼容旧字段 social }; // 如果有生日但没有星座,根据生日自动生成星座 if (transformed.birthday && !transformed.zodiacSign) { const generatedZodiacSign = calculateConstellationFromBirthday(transformed.birthday); if (generatedZodiacSign) { transformed.zodiacSign = generatedZodiacSign; console.log('根据生日自动生成星座:', transformed.birthday, '->', generatedZodiacSign); } } // 更新性别显示文字 this.updateGenderDisplay(transformed.gender); return transformed; }, // 更新性别显示文字 updateGenderDisplay(gender) { let displayText = "请选择"; if (gender !== undefined && gender !== null) { if (typeof gender === 'number' && genderReverseMap[gender] !== undefined) { displayText = genderReverseMap[gender]; } else if (typeof gender === 'string') { displayText = gender; } } this.setData({ genderDisplayText: displayText }); }, async onLoad() { try { // 获取 app 实例 const app = getApp(); // 初始化屏幕适配 await app.initScreen(this); const pages = getCurrentPages(); const currPage = pages[pages.length - 1]; // 当前页面实例 const profile = pages[pages.length - 2]; // 上一个页面实例(profile) if (profile && profile.initSystemInfo) { profile.initSystemInfo(); } if (profile && profile.loadUserData) { profile.loadUserData(); } const clearData = this.clearObjectValues(this.data.user); // 获取用户信息 try { const appData = await apiClient.getUserInfo(); if (appData && appData.code === 0 && appData.data) { // 转换数据格式以匹配新的数据结构 const userData = this.transformUserData(appData.data); this.setData({ user: userData }); // app.globalData.userInfo = userData; // wx.setStorageSync('userInfo', userData); } else { // 如果获取失败,使用默认数据 this.setData({ user: clearData }); this.updateGenderDisplay(clearData.gender); } } catch (error) { console.error('获取用户信息失败:', error); // 使用默认数据 this.setData({ user: clearData }); this.updateGenderDisplay(clearData.gender); } } catch (error) { console.error('页面加载失败:', error); } }, onUnload() { // 移除自动保存功能,改为点击保存按钮才保存 // 用户可以通过保存按钮主动保存,或者直接返回(不保存) }, clearObjectValues(obj) { Object.keys(obj).forEach(key => { const value = obj[key]; if (Array.isArray(value)) { // 清空为新数组 obj[key] = []; } else { const map = { 'object': {}, 'string': '', 'number': 0, 'boolean': false, } const type = typeof value; obj[key] = map[type] ?? null; } }); return obj }, /* ---------- 头像 -------------- */ async chooseAvatar() { try { // 选择图片 const res = await new Promise((resolve, reject) => { wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: resolve, fail: reject }); }); if (!res.tempFilePaths || res.tempFilePaths.length === 0) { return; } const filePath = res.tempFilePaths[0]; // 显示上传中提示 wx.showLoading({ title: '上传中...', mask: true }); // 使用 COS 上传头像 // const uploadResult = await cosManager.upload(filePath, { // fileType: 'avatar', // enableDedup: false, // 启用去重,节省存储空间 // onProgress: (percent) => { // console.log('上传进度:', percent + '%'); // } // }); // 使用 COS 上传头像 const uploadResult = await cosManager.upload(filePath, { fileType: 'avatar', enableDedup: true, // 启用去重,节省存储空间 onProgress: (percent) => { console.log('上传进度:', percent + '%'); }, enableCompress: false, compressOptions: { maxWidth: 400, maxHeight: 400, targetSize: 30 * 1024 } }); console.log('上传结果:', uploadResult); // 检查上传结果 if (!uploadResult) { throw new Error('上传失败:未获取到上传结果'); } if (uploadResult.success !== true) { const errorMsg = uploadResult?.error || uploadResult?.message || '上传失败'; console.error('上传失败:', errorMsg, uploadResult); throw new Error(errorMsg); } // 获取上传后的文件URL const avatarUrl = uploadResult.fileUrl || uploadResult.originalUrl; if (!avatarUrl) { console.error('上传返回数据:', uploadResult); throw new Error('上传失败:未获取到文件URL'); } // 更新本地头像 const user = Object.assign({}, this.data.user, { avatar: avatarUrl }); this.setData({ user }); // app.globalData.userInfo.user= { // ...app.globalData.userInfo.user, // avatar:avatarUrl // }; // 立即同步头像到 NIM try { await nimUserManager.updateSelfUserInfo({ avatar: avatarUrl }); console.log('✅ 头像已同步到 NIM'); } catch (nimError) { console.error('⚠️ 同步头像到 NIM 失败:', nimError); // 不影响主流程,静默失败 } // 立即同步头像到业务后端 try { await apiClient.updateUserProfile({ avatar: avatarUrl }); console.log('✅ 头像已同步到后端'); } catch (apiError) { console.error('⚠️ 同步头像到后端失败:', apiError); // 不影响主流程,静默失败 } // 通知 profile 页面更新头像 try { const pages = getCurrentPages(); const profilePage = pages[pages.length - 2]; if (profilePage) { profilePage.onLoad(); } } catch (error) { console.error('通知 profile 页面更新头像失败:', error); // 不影响主流程,静默失败 } wx.hideLoading(); wx.showToast({ title: '上传成功', icon: 'success', duration: 2000 }); } catch (error) { wx.hideLoading(); console.error('上传头像失败:', error); wx.showToast({ title: error.message || '上传失败,请重试', icon: 'none', duration: 2000 }); } }, // chooseImage(useCamera) { // const self = this; // wx.chooseImage({ // count: 1, // sizeType: ['compressed'], // sourceType: useCamera ? ['camera'] : ['album'], // success(res) { // const tempPath = res.tempFilePaths[0]; // const user = Object.assign({}, self.data.user, { avatar: tempPath }); // self.setData({ user }); // wx.setStorageSync('user_profile_v1', user); // } // }); // }, // 昵称输入 onNicknameInput(e) { const user = Object.assign({}, this.data.user, { nickname: e.detail.value }); this.setData({ user }); }, /* ---------- 个人简介 ---------- */ onIntroInput(e) { // 直接更新 user.bio const user = Object.assign({}, this.data.user, { bio: e.detail.value }); this.setData({ user }); }, /* ---------- 种族 ---------- */ onEthnicityInput(e) { const user = Object.assign({}, this.data.user, { ethnicity: e.detail.value }); this.setData({ user }); }, /* ---------- 毕业院校 ---------- */ onSchoolInput(e) { const user = Object.assign({}, this.data.user, { school: e.detail.value }); this.setData({ user }); }, /* ---------- bottom sheet 通用打开 ---------- */ openSheet(e) { // data-options 会以字符串形式传入,如果直接传对象需要自定义组件 // 这里我们支持两种写法:如果元素上有 data-options 属性且为 JSON 字符串,则尝试解析(注意:wxml 中直接传数组会被字符串化) const dataset = e.currentTarget.dataset || {}; const key = dataset.key; const title = dataset.title || '请选择'; let options = dataset.options; // dataset.options 在 wxml 传递数组时通常会变成字符串 "[object Object]" —— 我们优先根据 key 在定义好的数组中取 if (!options || typeof options === 'string') { const map = { occupation: occupationOptions, school: schoolOptions, gender: genderOptions, zodiacSign: zodiacSignOptions, mbtiType: mbtiTypeOptions, sleepHabit: sleepHabitOptions, socialActivity: socialActivityOptions, height: heightOptions, } options = map[key] || []; } // 获取当前选中值,如果是性别需要转换为文字显示 let selectedValue = ""; if (key === 'gender') { const genderNum = this.data.user[key]; if (typeof genderNum === 'number' && genderReverseMap[genderNum] !== undefined) { selectedValue = genderReverseMap[genderNum]; } else if (typeof genderNum === 'string') { selectedValue = genderNum; } } else { selectedValue = this.data.user[key] || ""; } this.setData({ showSheet: true, sheetOptions: options, sheetTitle: title, sheetKey: key, selectedValue }); }, closeSheet() { this.setData({ showSheet: false, sheetOptions: [], sheetTitle: "", sheetKey: "", selectedValue: "" }); }, onSheetSelect(e) { const idx = e.currentTarget.dataset.index; const val = this.data.sheetOptions[idx]; const key = this.data.sheetKey; if (!key) return this.closeSheet(); const user = Object.assign({}, this.data.user); // 如果是性别选择,需要转换为数字 if (key === 'gender') { user[key] = genderMap[val] !== undefined ? genderMap[val] : 0; // 更新性别显示文字 this.updateGenderDisplay(user[key]); } else { user[key] = val; } this.setData({ user, selectedValue: val }, () => { this.closeSheet(); }); }, /* ---------- 生日 ---------- */ onBirthdayChange(e) { const val = e.detail.value; const user = Object.assign({}, this.data.user, { birthday: val }); // 更新星座(使用公共方法) user.zodiacSign = calculateConstellationFromBirthday(val); this.setData({ user }); }, /* ---------- 家乡 ---------- */ onHometownChange(e) { const nick = e.detail?.value ?? this.data.user.hometown; const user = Object.assign({}, this.data.user, { hometown: nick }); this.setData({ user }); // 更新用户信息 // apiClient.updateUserProfile(user) // .then(res => { // console.log('更新用户信息成功', res); // wx.showToast({ title: '更新用户信息成功', icon: 'success' }); // this.setData({ user }); // }) // .catch(err => { // console.error('更新用户信息失败', err); // wx.showToast({ title: '更新用户信息失败', icon: 'none' }); // }); }, /* ---------- 下拉刷新 ---------- */ onRefresh() { this.setData({ refreshing: true }); // 重新加载用户信息 setTimeout(async () => { try { const clearData = this.clearObjectValues(this.data.user); const appData = await apiClient.getUserInfo(); if (appData && appData.code === 0 && appData.data) { // 转换数据格式以匹配新的数据结构 const userData = this.transformUserData(appData.data); this.setData({ user: userData }); } else { this.setData({ user: clearData }); this.updateGenderDisplay(clearData.gender); } } catch (error) { console.error('刷新用户信息失败:', error); } finally { this.setData({ refreshing: false }); } }, 500); }, /* ---------- 保存按钮 ---------- */ async handleSave() { try { // 延迟一下,确保失焦完成 await new Promise(resolve => setTimeout(resolve, 100)); wx.showLoading({ title: '保存中...', mask: true }); // 更新用户信息 const user = Object.assign({}, this.data.user); // 确保性别是数字格式(0=未知 1=男 2=女 3=其他) if (typeof user.gender === 'string') { user.gender = genderMap[user.gender] !== undefined ? genderMap[user.gender] : 0; } else if (typeof user.gender !== 'number') { user.gender = 0; } // 确保身高是数字 if (typeof user.height === 'string') { user.height = parseInt(user.height) || 0; } if (user.height === 0) {//==0提交会报错 所以删除 delete user.height; } const res = await apiClient.updateUserProfile(user); // 同步昵称和头像到 NIM if (user.nickname) { try { await nimUserManager.updateSelfUserInfo({ nickname: user.nickname, avatar: user.avatar || undefined }); console.log('✅ 用户资料已同步到 NIM'); } catch (nimError) { console.error('⚠️ 同步用户资料到 NIM 失败:', nimError); // 不影响主流程,静默失败 } } wx.hideLoading(); wx.showToast({ title: '保存成功', icon: 'success', duration: 2000 }); // 刷新全局用户信息缓存 try { await apiClient.refreshUserInfoCache(); } catch (error) { console.error('刷新全局用户信息缓存失败:', error); } // 更新全局用户信息缓存 try { const app = getApp(); if (app.globalData.userInfo) { // 更新全局用户信息,包括所有字段 const globalUserInfo = app.globalData.userInfo; if (globalUserInfo.user) { // 更新所有字段到全局用户信息(使用新字段名) Object.assign(globalUserInfo.user, { nickname: user.nickname, avatar: user.avatar, bio: user.bio, gender: user.gender, birthday: user.birthday, ethnicity: user.ethnicity, occupation: user.occupation, school: user.school, hometown: user.hometown, zodiacSign: user.zodiacSign, height: user.height, mbtiType: user.mbtiType, sleepHabit: user.sleepHabit, socialActivity: user.socialActivity }); } } } catch (error) { console.error('更新全局用户信息失败:', error); } // 通知 profile 页面更新用户信息 try { const pages = getCurrentPages(); // 查找 profile 页面(上一个页面) const profilePage = pages[pages.length - 2]; if (profilePage) { // 重新加载用户数据(会从全局缓存读取最新数据) if (typeof profilePage.loadUserData === 'function') { profilePage.loadUserData(); } // 直接更新 profile 页面的 userInfo,包括所有字段 const profileUserInfo = profilePage.data.userInfo || {}; if (!profileUserInfo.user) { profileUserInfo.user = {}; } // 重新计算年龄(根据生日) let calculatedAge = null; if (user.birthday && typeof profilePage.calculateAge === 'function') { calculatedAge = profilePage.calculateAge(user.birthday); } // 更新所有字段 const updateData = { 'userInfo.user.nickname': user.nickname, 'userInfo.user.avatar': user.avatar, 'userInfo.user.bio': user.bio, 'userInfo.user.gender': user.gender, 'userInfo.user.birthday': user.birthday, 'userInfo.user.ethnicity': user.ethnicity, 'userInfo.user.occupation': user.occupation, 'userInfo.user.school': user.school, 'userInfo.user.hometown': user.hometown, 'userInfo.user.zodiacSign': user.zodiacSign, 'userInfo.user.height': user.height, 'userInfo.user.mbtiType': user.mbtiType, 'userInfo.user.sleepHabit': user.sleepHabit, 'userInfo.user.socialActivity': user.socialActivity, 'userInfo.avatar': user.avatar, 'userInfo.avatarUrl': user.avatar, 'userInfo.nickname': user.nickname, 'userInfo.bio': user.bio, 'userInfo.gender': user.gender, 'userInfo.birthday': user.birthday, 'userInfo.ethnicity': user.ethnicity, 'userInfo.occupation': user.occupation, 'userInfo.school': user.school, 'userInfo.hometown': user.hometown, 'userInfo.zodiacSign': user.zodiacSign, 'userInfo.height': user.height, 'userInfo.mbtiType': user.mbtiType, 'userInfo.sleepHabit': user.sleepHabit, 'userInfo.socialActivity': user.socialActivity }; // 如果有计算出的年龄,也更新 if (calculatedAge !== null) { updateData.calculatedAge = calculatedAge; } profilePage.setData(updateData); } } catch (error) { console.error('通知 profile 页面更新失败:', error); // 不影响主流程,静默失败 } // 保存成功后,延迟返回上一页 setTimeout(() => { wx.navigateBack(); }, 1500); } catch (err) { wx.hideLoading(); console.error('保存用户信息失败', err); wx.showToast({ title: err.message || '保存失败,请重试', icon: 'none', duration: 2000 }); } } });