Files
Mycontent/miniprogram/pages/avatar-nickname/avatar-nickname.js
2026-04-02 19:20:19 +08:00

248 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 卡若创业派对 - 头像与昵称设置页
* 每次进入页面头像、昵称表单均为空,不回显服务端/本地资料;提交后再写入。
* 新用户from=new_user须上传头像 + 自填昵称后才能「完成」。
*/
const app = getApp()
const { trackClick } = require('../../utils/trackClick')
function isPlaceholderNickname(n) {
const s = (n || '').trim()
if (!s) return true
return s === '微信用户' || s.indexOf('微信用户') === 0
}
Page({
data: {
statusBarHeight: 44,
avatar: '',
nickname: '',
saving: false,
nicknameInputFocus: false,
/** 规则引擎传入avatar | nickname用于高亮对应区块 */
uiFocus: '',
fromNewUser: false,
/** 新用户:头像+真实昵称都齐才可提交 */
canSubmit: true,
/** 延后挂载昵称 input避免微信原生层缓存/自动填充造成「假回显」 */
nickInputReady: false,
},
onLoad(options) {
const fromNewUser = String(options.from || '').toLowerCase() === 'new_user'
const focus = String(options.focus || '').toLowerCase()
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44,
fromNewUser,
nickInputReady: false,
})
if (focus === 'avatar' || focus === 'nickname') {
this.setData({ uiFocus: focus })
}
this.loadFromUser()
// 下一帧再挂载 input + 再清一次,打断原生昵称缓存(测试工程师等)
setTimeout(() => {
this.setData({
nickname: '',
avatar: '',
nickInputReady: true,
})
this._updateCanSubmit()
if (focus === 'nickname') {
setTimeout(() => {
if (typeof wx.requirePrivacyAuthorize === 'function') {
wx.requirePrivacyAuthorize({
success: () => this.setData({ nicknameInputFocus: true }),
fail: () => {},
})
} else {
this.setData({ nicknameInputFocus: true })
}
}, 400)
}
}, 50)
if (fromNewUser) {
trackClick('avatar_nickname', 'page_view', '新注册引导页')
}
},
loadFromUser() {
const user = app.globalData.userInfo
if (!app.globalData.isLoggedIn || !user?.id) {
wx.showToast({ title: '请先登录', icon: 'none' })
setTimeout(() => getApp().goBackOrToHome(), 1500)
return
}
// 任意用户进入本页均不回显已有头像/昵称,由用户在本页重新选择、填写后再保存
this.setData({
nickname: '',
avatar: '',
})
this._updateCanSubmit()
},
_updateCanSubmit() {
const { avatar, nickname, fromNewUser } = this.data
const nick = (nickname || '').trim()
if (!fromNewUser) {
this.setData({ canSubmit: true })
return
}
const ok = !!(avatar && nick && !isPlaceholderNickname(nick))
this.setData({ canSubmit: ok })
},
goBack() {
if (this.data.fromNewUser && this._isProfileIncomplete()) {
wx.showModal({
title: '提示',
content: '请先设置头像并填写昵称后再离开',
showCancel: false,
confirmText: '我知道了',
})
return
}
getApp().goBackOrToHome()
},
_isProfileIncomplete() {
const { avatar, nickname, fromNewUser } = this.data
if (!fromNewUser) return false
const nick = (nickname || '').trim()
const av = (avatar || '').trim()
return !av || !nick || isPlaceholderNickname(nick)
},
onNicknameInput(e) {
this.setData({ nickname: e.detail.value })
this._updateCanSubmit()
},
onNicknameChange(e) {
this.setData({ nickname: e.detail.value })
this._updateCanSubmit()
},
onNicknameBlur() {
this.setData({ nicknameInputFocus: false })
},
onNicknameAreaTouch() {
if (typeof wx.requirePrivacyAuthorize !== 'function') return
wx.requirePrivacyAuthorize({
success: () => {
this.setData({ nicknameInputFocus: true })
},
fail: () => {},
})
},
async onChooseAvatar(e) {
const tempAvatarUrl = e.detail?.avatarUrl
if (!tempAvatarUrl) return
trackClick('avatar_nickname', 'btn_click', '选择头像')
await this.uploadAndSaveAvatar(tempAvatarUrl)
},
async uploadAndSaveAvatar(tempPath) {
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,
})
})
let avatarUrl = uploadRes.data?.url || uploadRes.url
if (avatarUrl && !avatarUrl.startsWith('http')) {
avatarUrl = app.globalData.baseUrl + avatarUrl
}
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' })
this._updateCanSubmit()
if (this.data.fromNewUser) {
trackClick('avatar_nickname', 'form_step_done', '头像更新完成')
}
} catch (e) {
wx.hideLoading()
wx.showToast({ title: e.message || '上传失败', icon: 'none' })
}
},
async saveProfile() {
const userId = app.globalData.userInfo?.id
if (!userId) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
if (this.data.fromNewUser && !this.data.canSubmit) {
wx.showToast({ title: '请先设置头像并填写昵称', icon: 'none' })
return
}
const nickname = (this.data.nickname || '').trim()
const avatar = (this.data.avatar || '').trim()
if (!nickname) {
wx.showToast({ title: '请输入昵称', icon: 'none' })
return
}
if (this.data.fromNewUser) {
if (!avatar) {
wx.showToast({ title: '请先选择头像', icon: 'none' })
return
}
if (isPlaceholderNickname(nickname)) {
wx.showToast({ title: '请修改昵称,不能使用系统默认占位词', icon: 'none' })
return
}
}
trackClick('avatar_nickname', 'btn_click', '完成保存')
this.setData({ saving: true })
try {
await app.request({
url: '/api/miniprogram/user/profile',
method: 'POST',
data: { userId, avatar: avatar || undefined, nickname },
})
if (app.globalData.userInfo) {
if (nickname) app.globalData.userInfo.nickname = nickname
if (avatar) app.globalData.userInfo.avatar = avatar
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
if (this.data.fromNewUser) {
try {
wx.removeStorageSync('profile_guide_required')
} catch (_) {}
wx.setStorageSync('new_user_guide_done_at', Date.now())
trackClick('avatar_nickname', 'form_submit', '新注册引导完成', {
hasAvatar: !!avatar,
nicknameLen: nickname.length,
})
}
wx.showToast({ title: '保存成功', icon: 'success' })
setTimeout(() => getApp().goBackOrToHome(), 800)
} catch (e) {
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
}
this.setData({ saving: false })
},
})