- Introduced a utility function to ensure absolute media URLs for user profile images. - Updated the user cache synchronization method to handle profile data more effectively, including avatar, nickname, phone, and MBTI. - Refactored the avatar upload process to utilize the new URL handling function, improving reliability. - Modified the super article editor to focus on image uploads instead of file uploads, enhancing user experience with clearer labeling and prompts. This update aims to streamline user profile management and improve the image upload experience in the application.
594 lines
22 KiB
JavaScript
594 lines
22 KiB
JavaScript
/**
|
||
* 卡若创业派对 - 资料编辑完整版(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 { toAvatarPath, pickLocalAvatarImagePath } = require('../../utils/util.js')
|
||
const { uploadByApi } = require('../../utils/miniprogramUpload.js')
|
||
|
||
const MBTI_OPTIONS = ['INTJ', 'INFP', 'INTP', 'ENTP', 'ENFP', 'ENTJ', 'ENFJ', 'INFJ', 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ', 'ISTP', 'ISFP', 'ESTP', 'ESFP']
|
||
|
||
/** 补齐小程序可用的绝对 URL(本地/OSS 相对路径拼 baseUrl) */
|
||
function ensureAbsMediaUrl(u, base) {
|
||
const s = String(u || '').trim()
|
||
if (!s) return ''
|
||
if (/^https?:\/\//i.test(s)) return s
|
||
const b = String(base || '').replace(/\/$/, '')
|
||
return b ? b + (s.startsWith('/') ? s : '/' + s) : s
|
||
}
|
||
|
||
/** 首次分步完善完成后写入;与手机号+昵称齐全时自动写入,老用户免向导 */
|
||
const PROFILE_WIZARD_DONE_KEY = 'profile_wizard_v1_done'
|
||
|
||
Page({
|
||
data: {
|
||
statusBarHeight: 44,
|
||
isVip: false,
|
||
avatar: '',
|
||
nickname: '',
|
||
shareCardPath: '', // 分享名片封面图(预生成)
|
||
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,
|
||
showPrivacyModal: false,
|
||
nicknameInputFocus: false,
|
||
/** 首次完善:分 3 步;full=1 或未达标资料时为单页 */
|
||
wizardMode: false,
|
||
wizardStep: 1,
|
||
totalWizardSteps: 3,
|
||
/** 头像区展示:含 MBTI 默认图 */
|
||
avatarPreviewUrl: '',
|
||
auditMode: false,
|
||
},
|
||
|
||
onLoad(options) {
|
||
this._wizardRouteOpts = {
|
||
full: options?.full === '1',
|
||
wizardOff: options?.wizard === '0',
|
||
}
|
||
this.setData({
|
||
statusBarHeight: app.globalData.statusBarHeight || 44,
|
||
fromVip: options?.from === 'vip',
|
||
auditMode: app.globalData.auditMode || false,
|
||
})
|
||
wx.showShareMenu({ withShareTimeline: true })
|
||
// 从朋友圈/分享打开且带 id:跳转到名片详情(member-detail)
|
||
if (options?.id) {
|
||
const ref = options.ref ? `&ref=${options.ref}` : ''
|
||
wx.redirectTo({ url: `/pages/member-detail/member-detail?id=${options.id}${ref}` })
|
||
return
|
||
}
|
||
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 }),
|
||
])
|
||
try {
|
||
if (app.loadMbtiAvatarsMap) await app.loadMbtiAvatarsMap()
|
||
} catch (_) {}
|
||
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,
|
||
})
|
||
this._applyWizardModeFromProfile(d)
|
||
this._syncAvatarPreview()
|
||
setTimeout(() => this.generateShareCard(), 200)
|
||
} else {
|
||
this.setData({ loading: false })
|
||
}
|
||
} catch (e) {
|
||
this.setData({ loading: false })
|
||
}
|
||
},
|
||
|
||
goBack() { getApp().goBackOrToHome() },
|
||
|
||
/** 分步向导时:齿轮 → 一页完整编辑(与「派对会员」资料同路径) */
|
||
onOpenFullEdit() {
|
||
if (!this.data.wizardMode) return
|
||
wx.showModal({
|
||
title: '切换到完整编辑',
|
||
content: '将在一页中展示全部资料项,便于一次性填写。当前分步进度不会丢失,可随时返回。',
|
||
confirmText: '进入完整编辑',
|
||
cancelText: '取消',
|
||
success: (r) => {
|
||
if (!r.confirm) return
|
||
wx.redirectTo({ url: '/pages/profile-edit/profile-edit?full=1&wizard=0' })
|
||
},
|
||
})
|
||
},
|
||
|
||
/**
|
||
* 是否走三步向导:资料未「手机号+昵称」齐全且未标记完成,且非 full=1、非 VIP 开通页强制单页。
|
||
* 老用户已齐全则自动写 DONE,避免重复向导。
|
||
*/
|
||
_applyWizardModeFromProfile(d) {
|
||
const ro = this._wizardRouteOpts || {}
|
||
const forceFull = ro.full === true
|
||
const forceNoWizard = ro.wizardOff === true
|
||
const phoneNum = String(d.phone || '').replace(/\D/g, '')
|
||
const phoneOk = /^1[3-9]\d{9}$/.test(phoneNum)
|
||
const nickOk = !!(d.nickname && String(d.nickname).trim())
|
||
if (phoneOk && nickOk && !wx.getStorageSync(PROFILE_WIZARD_DONE_KEY)) {
|
||
wx.setStorageSync(PROFILE_WIZARD_DONE_KEY, '1')
|
||
}
|
||
const wizardMode = !forceFull && !forceNoWizard && !wx.getStorageSync(PROFILE_WIZARD_DONE_KEY)
|
||
this.setData({ wizardMode, wizardStep: 1 })
|
||
},
|
||
|
||
onWizardPrev() {
|
||
if (this.data.wizardStep > 1) {
|
||
this.setData({ wizardStep: this.data.wizardStep - 1 })
|
||
}
|
||
},
|
||
|
||
onWizardNext() {
|
||
if (this.data.saving) return
|
||
const { wizardMode, wizardStep } = this.data
|
||
if (!wizardMode) return
|
||
if (wizardStep === 1) {
|
||
if (!(this.data.nickname || '').trim()) {
|
||
wx.showToast({ title: '请填写昵称', icon: 'none' })
|
||
return
|
||
}
|
||
this.setData({ wizardStep: 2 })
|
||
return
|
||
}
|
||
if (wizardStep === 2) {
|
||
this.setData({ wizardStep: 3 })
|
||
return
|
||
}
|
||
this._doSaveProfile({ wizardComplete: true })
|
||
},
|
||
|
||
onNicknameAreaTouch() {
|
||
if (typeof wx.requirePrivacyAuthorize !== 'function') return
|
||
wx.requirePrivacyAuthorize({
|
||
success: () => { this.setData({ nicknameInputFocus: true }) },
|
||
fail: () => {},
|
||
})
|
||
},
|
||
onNicknameBlur() { this.setData({ nicknameInputFocus: false }) },
|
||
preventMove() {},
|
||
handleAgreePrivacy() {
|
||
if (app._privacyResolve) {
|
||
app._privacyResolve({ buttonId: 'agree-btn', event: 'agree' })
|
||
app._privacyResolve = null
|
||
}
|
||
this.setData({ showPrivacyModal: false, nicknameInputFocus: true })
|
||
},
|
||
handleDisagreePrivacy() {
|
||
if (app._privacyResolve) {
|
||
app._privacyResolve({ event: 'disagree' })
|
||
app._privacyResolve = null
|
||
}
|
||
this.setData({ showPrivacyModal: false })
|
||
},
|
||
|
||
// 生成分享名片封面图(参考:头像左+昵称右,分隔线,四栏信息 5:4)
|
||
async generateShareCard() {
|
||
const { avatar, nickname, region, mbti, industry, position } = this.data
|
||
const userId = app.globalData.userInfo?.id
|
||
if (!userId) return
|
||
try {
|
||
const ctx = wx.createCanvasContext('shareCardCanvas', this)
|
||
const w = 500
|
||
const h = 400
|
||
const pad = 32
|
||
// 背景(深灰卡片感)
|
||
const grd = ctx.createLinearGradient(0, 0, w, h)
|
||
grd.addColorStop(0, '#1E293B')
|
||
grd.addColorStop(1, '#0F172A')
|
||
ctx.setFillStyle(grd)
|
||
ctx.fillRect(0, 0, w, h)
|
||
// 顶部区域:左头像 + 右昵称
|
||
const avatarSize = 100
|
||
const avatarX = pad + 10
|
||
const avatarY = 50
|
||
const avatarRadius = avatarSize / 2
|
||
const rightStart = avatarX + avatarSize + 28
|
||
const drawAvatar = () => new Promise((resolve) => {
|
||
if (avatar && avatar.startsWith('http')) {
|
||
wx.downloadFile({
|
||
url: avatar,
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
ctx.save()
|
||
ctx.beginPath()
|
||
ctx.arc(avatarX + avatarRadius, avatarY + avatarRadius, avatarRadius, 0, Math.PI * 2)
|
||
ctx.clip()
|
||
ctx.drawImage(res.tempFilePath, avatarX, avatarY, avatarSize, avatarSize)
|
||
ctx.restore()
|
||
} else {
|
||
this.drawAvatarPlaceholder(ctx, avatarX, avatarY, avatarSize, nickname)
|
||
}
|
||
resolve()
|
||
},
|
||
fail: () => {
|
||
this.drawAvatarPlaceholder(ctx, avatarX, avatarY, avatarSize, nickname)
|
||
resolve()
|
||
},
|
||
})
|
||
} else {
|
||
this.drawAvatarPlaceholder(ctx, avatarX, avatarY, avatarSize, nickname)
|
||
resolve()
|
||
}
|
||
})
|
||
await drawAvatar()
|
||
ctx.setStrokeStyle('rgba(94,234,212,0.5)')
|
||
ctx.setLineWidth(2)
|
||
ctx.beginPath()
|
||
ctx.arc(avatarX + avatarRadius, avatarY + avatarRadius, avatarRadius, 0, Math.PI * 2)
|
||
ctx.stroke()
|
||
// 右侧:昵称 + 个人名片
|
||
const displayName = (nickname || '').trim() || '创业者'
|
||
ctx.setFillStyle('#ffffff')
|
||
ctx.setFontSize(26)
|
||
ctx.setTextAlign('left')
|
||
ctx.fillText(displayName, rightStart, avatarY + 36)
|
||
ctx.setFillStyle('#94A3B8')
|
||
ctx.setFontSize(13)
|
||
ctx.fillText('个人名片', rightStart, avatarY + 62)
|
||
// 分隔线
|
||
const divY = 168
|
||
ctx.setStrokeStyle('rgba(255,255,255,0.08)')
|
||
ctx.setLineWidth(1)
|
||
ctx.beginPath()
|
||
ctx.moveTo(pad, divY)
|
||
ctx.lineTo(w - pad, divY)
|
||
ctx.stroke()
|
||
// 底部四栏:地区 | MBTI,行业 | 职位
|
||
const labelGray = '#64748B'
|
||
const valueWhite = '#F1F5F9'
|
||
const rowH = 52
|
||
const colW = (w - pad * 2) / 2
|
||
const truncate = (text, maxW) => {
|
||
if (!text) return ''
|
||
ctx.setFontSize(15)
|
||
let m = ctx.measureText(text)
|
||
if (m.width <= maxW) return text
|
||
for (let i = text.length - 1; i > 0; i--) {
|
||
const t = text.slice(0, i) + '…'
|
||
if (ctx.measureText(t).width <= maxW) return t
|
||
}
|
||
return text[0] + '…'
|
||
}
|
||
const maxValW = colW - 16
|
||
const items = [
|
||
{ label: '地区', value: truncate((region || '').trim() || '未填写', maxValW), x: pad },
|
||
{ label: 'MBTI', value: (mbti || '').trim() || '未填写', x: pad + colW },
|
||
{ label: '行业', value: truncate((industry || '').trim() || '未填写', maxValW), x: pad },
|
||
{ label: '职位', value: truncate((position || '').trim() || '未填写', maxValW), x: pad + colW },
|
||
]
|
||
items.forEach((item, i) => {
|
||
const row = Math.floor(i / 2)
|
||
const baseY = divY + 36 + row * rowH
|
||
ctx.setFillStyle(labelGray)
|
||
ctx.setFontSize(12)
|
||
ctx.fillText(item.label, item.x, baseY - 8)
|
||
ctx.setFillStyle(valueWhite)
|
||
ctx.setFontSize(15)
|
||
ctx.fillText(item.value, item.x, baseY + 14)
|
||
})
|
||
ctx.draw(true, () => {
|
||
wx.canvasToTempFilePath({
|
||
canvasId: 'shareCardCanvas',
|
||
destWidth: 500,
|
||
destHeight: 400,
|
||
success: (res) => {
|
||
this.setData({ shareCardPath: res.tempFilePath })
|
||
},
|
||
}, this)
|
||
})
|
||
} catch (e) {
|
||
console.warn('[ShareCard] 生成失败:', e)
|
||
}
|
||
},
|
||
|
||
drawAvatarPlaceholder(ctx, x, y, size, nickname) {
|
||
ctx.setFillStyle('rgba(94,234,212,0.2)')
|
||
ctx.beginPath()
|
||
ctx.arc(x + size / 2, y + size / 2, size / 2, 0, Math.PI * 2)
|
||
ctx.fill()
|
||
ctx.setFillStyle('#5EEAD4')
|
||
ctx.setFontSize(size * 0.42)
|
||
ctx.setTextAlign('center')
|
||
ctx.fillText((nickname || '?')[0], x + size / 2, y + size / 2 + size * 0.14)
|
||
},
|
||
|
||
onShareAppMessage() {
|
||
const ref = app.getMyReferralCode()
|
||
const userId = app.globalData.userInfo?.id
|
||
const nickname = (this.data.nickname || '').trim() || '我'
|
||
const path = userId
|
||
? (ref ? `/pages/member-detail/member-detail?id=${userId}&ref=${ref}` : `/pages/member-detail/member-detail?id=${userId}`)
|
||
: (ref ? `/pages/profile-edit/profile-edit?ref=${ref}` : '/pages/profile-edit/profile-edit')
|
||
const result = {
|
||
title: `${nickname}为您分享名片`,
|
||
path,
|
||
}
|
||
if (this.data.shareCardPath) result.imageUrl = this.data.shareCardPath
|
||
return result
|
||
},
|
||
|
||
onShareTimeline() {
|
||
const ref = app.getMyReferralCode()
|
||
const userId = app.globalData.userInfo?.id
|
||
const nickname = (this.data.nickname || '').trim() || '我'
|
||
const query = userId
|
||
? (ref ? `id=${userId}&ref=${ref}` : `id=${userId}`)
|
||
: (ref ? `ref=${ref}` : '')
|
||
const result = {
|
||
title: `${nickname}为您分享名片`,
|
||
query: query || '',
|
||
}
|
||
if (this.data.shareCardPath) result.imageUrl = this.data.shareCardPath
|
||
return result
|
||
},
|
||
|
||
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] }, () => this._syncAvatarPreview())
|
||
},
|
||
|
||
_syncAvatarPreview() {
|
||
try {
|
||
const { resolveAvatarWithMbti } = require('../../utils/mbtiAvatar.js')
|
||
const url = resolveAvatarWithMbti(
|
||
this.data.avatar,
|
||
this.data.mbti,
|
||
app.globalData.mbtiAvatarsMap || {},
|
||
app.globalData.baseUrl || ''
|
||
)
|
||
this.setData({ avatarPreviewUrl: url || '' })
|
||
} catch (_) {
|
||
this.setData({ avatarPreviewUrl: (this.data.avatar || '').trim() })
|
||
}
|
||
},
|
||
|
||
/** 将接口返回的 profile data 写入 globalData.userInfo、storage;可选同步本页展示字段(头像上传时用) */
|
||
_syncUserCacheFromProfileData(d, patchPage) {
|
||
if (!d || typeof d !== 'object') return
|
||
const base = app.globalData.baseUrl || ''
|
||
const ensureAbs = (u) => ensureAbsMediaUrl(u, base)
|
||
|
||
if (patchPage) {
|
||
const patch = {}
|
||
if (d.avatar != null && String(d.avatar).trim() !== '') patch.avatar = ensureAbs(d.avatar)
|
||
if (d.nickname != null) patch.nickname = String(d.nickname)
|
||
if (d.phone != null) patch.phone = String(d.phone)
|
||
if (d.wechatId != null) patch.wechatId = String(d.wechatId)
|
||
if (d.mbti != null) {
|
||
patch.mbti = String(d.mbti)
|
||
const idx = MBTI_OPTIONS.indexOf(patch.mbti)
|
||
if (idx >= 0) patch.mbtiIndex = idx
|
||
}
|
||
if (Object.keys(patch).length) {
|
||
this.setData(patch, () => this._syncAvatarPreview())
|
||
} else {
|
||
this._syncAvatarPreview()
|
||
}
|
||
}
|
||
|
||
const u = app.globalData.userInfo
|
||
if (!u) return
|
||
if (d.nickname != null && String(d.nickname).trim() !== '') u.nickname = String(d.nickname).trim()
|
||
if (d.avatar != null && String(d.avatar).trim() !== '') u.avatar = ensureAbs(d.avatar)
|
||
if (d.phone != null && String(d.phone).trim() !== '') {
|
||
u.phone = String(d.phone).trim()
|
||
wx.setStorageSync('user_phone', u.phone)
|
||
}
|
||
if (d.wechatId != null) {
|
||
const w = String(d.wechatId).trim()
|
||
u.wechatId = w
|
||
u.wechat = w
|
||
u.wechat_id = w
|
||
if (w) wx.setStorageSync('user_wechat', w)
|
||
}
|
||
if (d.mbti != null && String(d.mbti).trim() !== '') u.mbti = String(d.mbti).trim()
|
||
wx.setStorageSync('userInfo', u)
|
||
},
|
||
|
||
/** 点击头像:相册/相机选图(不使用 open-type=chooseAvatar,避免 Windows 开发者工具 http://tmp 渲染报错) */
|
||
onTapPickAvatar() {
|
||
const run = () => {
|
||
pickLocalAvatarImagePath()
|
||
.then((filePath) => this._uploadChosenAvatar(filePath))
|
||
.catch((err) => {
|
||
if (err && err.cancelled) return
|
||
wx.showToast({ title: (err && err.message) || '选择图片失败', icon: 'none' })
|
||
})
|
||
}
|
||
if (typeof wx.requirePrivacyAuthorize === 'function') {
|
||
wx.requirePrivacyAuthorize({
|
||
success: run,
|
||
fail: () => wx.showToast({ title: '需同意隐私指引后再换头像', icon: 'none' }),
|
||
})
|
||
} else {
|
||
run()
|
||
}
|
||
},
|
||
|
||
async _uploadChosenAvatar(filePath) {
|
||
if (!filePath) return
|
||
wx.showLoading({ title: '上传中...', mask: true })
|
||
|
||
try {
|
||
const uploadRes = await uploadByApi({ filePath, folder: 'avatars' })
|
||
|
||
let avatarUrl = uploadRes.data?.url || uploadRes.url
|
||
if (avatarUrl && !/^https?:\/\//i.test(avatarUrl)) {
|
||
avatarUrl = ensureAbsMediaUrl(avatarUrl, app.globalData.baseUrl)
|
||
}
|
||
this.setData({ avatar: avatarUrl }, () => this._syncAvatarPreview())
|
||
const avatarToSave = toAvatarPath(avatarUrl)
|
||
const profileRes = await app.request({
|
||
url: '/api/miniprogram/user/profile',
|
||
method: 'POST',
|
||
data: { userId: app.globalData.userInfo?.id, avatar: avatarToSave },
|
||
})
|
||
if (!profileRes?.success) {
|
||
throw new Error(profileRes?.error || profileRes?.message || '保存头像失败')
|
||
}
|
||
this._syncUserCacheFromProfileData(profileRes.data, true)
|
||
wx.hideLoading()
|
||
wx.showToast({ title: '头像已更新', icon: 'success' })
|
||
setTimeout(() => this.generateShareCard(), 200)
|
||
} catch (err) {
|
||
wx.hideLoading()
|
||
wx.showToast({ title: err.message || '上传失败,请重试', icon: 'none' })
|
||
}
|
||
},
|
||
|
||
saveProfile() {
|
||
this._doSaveProfile({ wizardComplete: false })
|
||
},
|
||
|
||
async _doSaveProfile({ wizardComplete }) {
|
||
const userId = app.globalData.userInfo?.id
|
||
if (!userId) {
|
||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||
return
|
||
}
|
||
const s = (v) => (v || '').toString().trim()
|
||
const isVip = this.data.isVip
|
||
const phoneRaw = s(this.data.phone)
|
||
if (!phoneRaw) {
|
||
wx.showToast({ title: '请输入手机号', icon: 'none' })
|
||
return
|
||
}
|
||
const phoneNum = phoneRaw.replace(/\D/g, '')
|
||
if (!/^1[3-9]\d{9}$/.test(phoneNum)) {
|
||
wx.showToast({ title: '请输入正确的11位手机号', icon: 'none' })
|
||
return
|
||
}
|
||
const phoneToSave = phoneNum
|
||
this.setData({ saving: true })
|
||
try {
|
||
const payload = {
|
||
userId,
|
||
avatar: toAvatarPath(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: phoneToSave,
|
||
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
|
||
}
|
||
const res = await app.request({
|
||
url: '/api/miniprogram/user/profile',
|
||
method: 'POST',
|
||
data: payload,
|
||
})
|
||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||
if (res?.data) this._syncUserCacheFromProfileData(res.data, false)
|
||
if (wizardComplete) {
|
||
wx.setStorageSync(PROFILE_WIZARD_DONE_KEY, '1')
|
||
this.setData({ wizardMode: false, saving: false })
|
||
setTimeout(() => {
|
||
wx.redirectTo({ url: `/pages/member-detail/member-detail?id=${userId}` })
|
||
}, 400)
|
||
return
|
||
}
|
||
setTimeout(() => getApp().goBackOrToHome(), 800)
|
||
} catch (e) {
|
||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||
}
|
||
this.setData({ saving: false })
|
||
},
|
||
})
|