/** * Soul创业派对 - 资料编辑完整版(comprehensive_profile_editor_v1_1) * 温馨提示、头像、基本信息、核心联系方式、个人故事、互助需求、项目介绍 * * 接口约定(/api/miniprogram/user/profile): * - GET ?userId= 返回 data: { avatar, nickname, mbti, region, industry, businessScale, position, skills, phone, wechatId, ... },空值统一为 "" * - POST body:普通用户提交基础字段;VIP 提交全部字段(含 skills、个人故事、互助需求、项目介绍) * * 表单展示:普通用户仅展示 温馨提示、头像、昵称、MBTI、地区、行业、业务体量、职位、核心联系方式;VIP 展示全部 */ const app = getApp() const MBTI_OPTIONS = ['INTJ', 'INFP', 'INTP', 'ENTP', 'ENFP', 'ENTJ', 'ENFJ', 'INFJ', 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ', 'ISTP', 'ISFP', 'ESTP', 'ESFP'] Page({ data: { statusBarHeight: 44, isVip: false, avatar: '', nickname: '', mbti: '', mbtiIndex: 0, region: '', industry: '', businessScale: '', position: '', skills: '', phone: '', wechatId: '', storyBestMonth: '', storyAchievement: '', storyTurning: '', helpOffer: '', helpNeed: '', projectIntro: '', mbtiOptions: MBTI_OPTIONS, showMbtiPicker: false, saving: false, loading: true, showAvatarModal: false, }, onLoad() { this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 }) this.loadProfile() }, async loadProfile() { const userInfo = app.globalData.userInfo if (!app.globalData.isLoggedIn || !userInfo?.id) { this.setData({ loading: false }) wx.showToast({ title: '请先登录', icon: 'none' }) setTimeout(() => getApp().goBackOrToHome(), 1500) return } try { const userId = userInfo.id const [profileRes, vipRes] = await Promise.all([ app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true }), app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true }), ]) this.setData({ isVip: vipRes?.data?.isVip || false }) const res = profileRes if (res?.success && res.data) { const d = res.data const v = (k) => (d[k] != null && d[k] !== '') ? String(d[k]) : '' const mbtiIndex = MBTI_OPTIONS.indexOf(v('mbti')) >= 0 ? MBTI_OPTIONS.indexOf(v('mbti')) : 0 this.setData({ avatar: v('avatar'), nickname: v('nickname'), mbti: v('mbti'), mbtiIndex, region: v('region'), industry: v('industry'), businessScale: v('businessScale'), position: v('position'), skills: v('skills'), phone: v('phone'), wechatId: v('wechatId') || wx.getStorageSync('user_wechat') || '', storyBestMonth: v('storyBestMonth'), storyAchievement: v('storyAchievement'), storyTurning: v('storyTurning'), helpOffer: v('helpOffer'), helpNeed: v('helpNeed'), projectIntro: v('projectIntro'), loading: false, }) } else { this.setData({ loading: false }) } } catch (e) { this.setData({ loading: false }) } }, goBack() { getApp().goBackOrToHome() }, onNicknameInput(e) { this.setData({ nickname: e.detail.value }) }, onNicknameChange(e) { this.setData({ nickname: e.detail.value }) }, onRegionInput(e) { this.setData({ region: e.detail.value }) }, onIndustryInput(e) { this.setData({ industry: e.detail.value }) }, onBusinessScaleInput(e) { this.setData({ businessScale: e.detail.value }) }, onPositionInput(e) { this.setData({ position: e.detail.value }) }, onSkillsInput(e) { this.setData({ skills: e.detail.value }) }, onPhoneInput(e) { this.setData({ phone: e.detail.value }) }, onWechatInput(e) { this.setData({ wechatId: e.detail.value }) }, onStoryBestMonthInput(e) { this.setData({ storyBestMonth: e.detail.value }) }, onStoryAchievementInput(e) { this.setData({ storyAchievement: e.detail.value }) }, onStoryTurningInput(e) { this.setData({ storyTurning: e.detail.value }) }, onHelpOfferInput(e) { this.setData({ helpOffer: e.detail.value }) }, onHelpNeedInput(e) { this.setData({ helpNeed: e.detail.value }) }, onProjectIntroInput(e) { this.setData({ projectIntro: e.detail.value }) }, onMbtiPickerChange(e) { const i = parseInt(e.detail.value, 10) this.setData({ mbtiIndex: i, mbti: MBTI_OPTIONS[i] }) }, // 点击头像:选择微信头像或从相册选择 onAvatarTap() { wx.showActionSheet({ itemList: ['使用微信头像', '从相册选择'], success: (res) => { if (res.tapIndex === 0) { this.setData({ showAvatarModal: true }) } else if (res.tapIndex === 1) { this.chooseAvatarFromAlbum() } }, }) }, closeAvatarModal() { this.setData({ showAvatarModal: false }) }, // 从相册/相机选择头像 chooseAvatarFromAlbum() { wx.chooseMedia({ count: 1, mediaType: ['image'], sourceType: ['album', 'camera'], success: async (res) => { const tempPath = res.tempFiles[0].tempFilePath wx.showLoading({ title: '上传中...', mask: true }) try { const uploadRes = await new Promise((resolve, reject) => { wx.uploadFile({ url: app.globalData.baseUrl + '/api/miniprogram/upload', filePath: tempPath, name: 'file', formData: { folder: 'avatars' }, success: (r) => { try { const data = JSON.parse(r.data) if (data.success) resolve(data) else reject(new Error(data.error || '上传失败')) } catch { reject(new Error('解析失败')) } }, fail: reject, }) }) const avatarUrl = app.globalData.baseUrl + uploadRes.data.url this.setData({ avatar: avatarUrl }) await app.request({ url: '/api/miniprogram/user/profile', method: 'POST', data: { userId: app.globalData.userInfo?.id, avatar: avatarUrl }, }) if (app.globalData.userInfo) { app.globalData.userInfo.avatar = avatarUrl wx.setStorageSync('userInfo', app.globalData.userInfo) } wx.hideLoading() wx.showToast({ title: '头像已更新', icon: 'success' }) } catch (e) { wx.hideLoading() wx.showToast({ title: e.message || '上传失败', icon: 'none' }) } }, }) }, // 微信原生 chooseAvatar 回调:使用当前微信头像 async onChooseAvatar(e) { const tempAvatarUrl = e.detail?.avatarUrl this.setData({ showAvatarModal: false }) if (!tempAvatarUrl) return wx.showLoading({ title: '上传中...', mask: true }) try { const uploadRes = await new Promise((resolve, reject) => { wx.uploadFile({ url: app.globalData.baseUrl + '/api/miniprogram/upload', filePath: tempAvatarUrl, name: 'file', formData: { folder: 'avatars' }, success: (r) => { try { const data = JSON.parse(r.data) if (data.success) resolve(data) else reject(new Error(data.error || '上传失败')) } catch { reject(new Error('解析失败')) } }, fail: reject, }) }) const avatarUrl = app.globalData.baseUrl + uploadRes.data.url this.setData({ avatar: avatarUrl }) await app.request({ url: '/api/miniprogram/user/profile', method: 'POST', data: { userId: app.globalData.userInfo?.id, avatar: avatarUrl }, }) if (app.globalData.userInfo) { app.globalData.userInfo.avatar = avatarUrl wx.setStorageSync('userInfo', app.globalData.userInfo) } wx.hideLoading() wx.showToast({ title: '头像已更新', icon: 'success' }) } catch (err) { wx.hideLoading() wx.showToast({ title: err.message || '上传失败,请重试', icon: 'none' }) } }, async saveProfile() { const userId = app.globalData.userInfo?.id if (!userId) { wx.showToast({ title: '请先登录', icon: 'none' }) return } this.setData({ saving: true }) try { const s = (v) => (v || '').toString().trim() const isVip = this.data.isVip const payload = { userId, avatar: s(this.data.avatar), nickname: s(this.data.nickname), mbti: s(this.data.mbti), region: s(this.data.region), industry: s(this.data.industry), businessScale: s(this.data.businessScale), position: s(this.data.position), phone: s(this.data.phone), wechatId: s(this.data.wechatId), } const showHelp = isVip || this.data.helpOffer || this.data.helpNeed if (isVip) { payload.skills = s(this.data.skills) payload.storyBestMonth = s(this.data.storyBestMonth) payload.storyAchievement = s(this.data.storyAchievement) payload.storyTurning = s(this.data.storyTurning) payload.projectIntro = s(this.data.projectIntro) } if (showHelp) { payload.helpOffer = s(this.data.helpOffer) payload.helpNeed = s(this.data.helpNeed) } if (payload.wechatId) wx.setStorageSync('user_wechat', payload.wechatId) if (payload.phone) wx.setStorageSync('user_phone', payload.phone) const hasUpdate = Object.keys(payload).some(k => k !== 'userId' && payload[k] !== '') if (!hasUpdate) { wx.showToast({ title: '无变更', icon: 'none' }) this.setData({ saving: false }) return } await app.request({ url: '/api/miniprogram/user/profile', method: 'POST', data: payload, }) wx.showToast({ title: '保存成功', icon: 'success' }) if (app.globalData.userInfo) { if (payload.nickname) app.globalData.userInfo.nickname = payload.nickname if (payload.avatar) app.globalData.userInfo.avatar = payload.avatar wx.setStorageSync('userInfo', app.globalData.userInfo) } setTimeout(() => getApp().goBackOrToHome(), 800) } catch (e) { wx.showToast({ title: e.message || '保存失败', icon: 'none' }) } this.setData({ saving: false }) }, })