kydc/me/me.js
2026-05-12 20:27:47 +08:00

279 lines
9.8 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.

const app = getApp()
function normalizeAvatarUrl(avatarUrl, baseUrl) {
if (!avatarUrl) return ''
return avatarUrl.startsWith('http') ? avatarUrl : `${baseUrl}${avatarUrl}`
}
function requestFailHint(errMsg) {
const m = String(errMsg || '').toLowerCase()
if (m.includes('domain') || m.includes('合法域名')) return '域名未在公众平台配置,或未开启 HTTPS'
if (m.includes('ssl') || m.includes('certificate')) return 'HTTPS 证书异常,请检查服务器证书'
if (m.includes('timeout') || m.includes('timed out')) return '连接超时,请稍后重试'
return '网络异常,请检查网络后重试'
}
const DEFAULT_AVATAR = 'data:image/svg+xml;utf8,' + encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320" viewBox="0 0 320 320">
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#c19cff"/>
<stop offset="100%" stop-color="#00e3fd"/>
</linearGradient>
</defs>
<rect width="320" height="320" rx="160" fill="#1d2025"/>
<circle cx="160" cy="160" r="154" fill="url(#bg)" opacity="0.18"/>
<circle cx="160" cy="126" r="54" fill="#f6f6fc" opacity="0.92"/>
<path d="M72 268c18-44 52-70 88-70s70 26 88 70" fill="#f6f6fc" opacity="0.92"/>
</svg>
`)
Page({
data: {
name: '',
nickname: '',
days: 0,
badges: 0,
openid: '',
avatarUrl: '',
defaultAvatarUrl: DEFAULT_AVATAR,
loading: false,
error: '',
showNicknameModal: false,
nicknameDraft: '',
},
onShow() {
this.loadProfile()
},
async loadProfile() {
const openid = app.globalData.openid || wx.getStorageSync('openid') || ''
if (!openid) {
this.setData({
loading: false,
openid: '',
name: '未登录',
nickname: '未登录',
avatarUrl: '',
nicknameDraft: '',
badges: 0,
days: 0,
error: '',
})
return
}
this.setData({ loading: true, error: '' })
app.globalData.openid = openid
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
let profileErrMsg = ''
wx.request({
url: `${baseUrl}/api/v1/auth/profile?openid=${encodeURIComponent(openid)}`,
method: 'GET',
success: (res) => {
const body = res.data || {}
const ok = res.statusCode >= 200 && res.statusCode < 300 && body.code === 0 && body.data
if (!ok) {
profileErrMsg = '个人信息加载失败,请稍后重试'
const storedAvatarUrl = wx.getStorageSync(`avatarUrl:${openid}`) || ''
const normalizedAvatar = normalizeAvatarUrl(storedAvatarUrl, baseUrl)
this.setData({
openid,
name: '考研战士',
nickname: '考研战士',
avatarUrl: normalizedAvatar,
nicknameDraft: '考研战士',
error: profileErrMsg,
})
return
}
const profile = body.data
const avatarUrl = wx.getStorageSync(`avatarUrl:${openid}`) || profile.avatar_url || ''
const nickname = profile.nickname || '考研战士'
const normalizedAvatar = normalizeAvatarUrl(avatarUrl, baseUrl)
profileErrMsg = ''
this.setData({
openid,
name: nickname,
nickname,
avatarUrl: normalizedAvatar,
nicknameDraft: nickname,
days: Number(profile.days || 0),
error: '',
})
},
fail: () => {
profileErrMsg = '个人信息加载失败,请稍后重试'
const storedAvatarUrl = wx.getStorageSync(`avatarUrl:${openid}`) || ''
const normalizedAvatar = normalizeAvatarUrl(storedAvatarUrl, baseUrl)
this.setData({
openid,
name: '考研战士',
nickname: '考研战士',
avatarUrl: normalizedAvatar,
nicknameDraft: '考研战士',
error: profileErrMsg,
})
},
complete: () => {
this.loadAchievements(profileErrMsg)
this.setData({ loading: false })
},
})
},
openNicknameModal() {
this.setData({ showNicknameModal: true, nicknameDraft: this.data.nickname || this.data.name || '考研战士' })
},
onAvatarError() {
this.setData({ avatarUrl: this.data.defaultAvatarUrl })
},
closeNicknameModal() {
this.setData({ showNicknameModal: false, nicknameDraft: this.data.nickname || this.data.name || '考研战士' })
},
onNicknameInput(e) {
this.setData({ nicknameDraft: e.detail.value })
},
saveNickname() {
if (!app.globalData.openid) return
const nickname = (this.data.nicknameDraft || '').trim()
if (!nickname) {
wx.showToast({ title: '请输入昵称', icon: 'none' })
return
}
wx.request({
url: `${app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl}/api/v1/auth/profile`,
method: 'PUT',
header: { 'Content-Type': 'application/json' },
data: { openid: app.globalData.openid, nickname },
success: (res) => {
const profile = res.data?.data || {}
const nextNickname = profile.nickname || nickname
this.setData({ nickname: nextNickname, name: nextNickname, nicknameDraft: nextNickname, showNicknameModal: false })
wx.showToast({ title: '保存成功', icon: 'success' })
},
fail: () => wx.showToast({ title: '保存失败', icon: 'none' }),
})
},
onChooseAvatar(e) {
const tempPath = e.detail?.avatarUrl || ''
if (!app.globalData.openid) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
if (!tempPath) {
wx.showToast({ title: '未选择头像', icon: 'none' })
return
}
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
wx.uploadFile({
url: `${baseUrl}/api/v1/auth/avatar?openid=${encodeURIComponent(app.globalData.openid)}`,
filePath: tempPath,
name: 'file',
success: (uploadRes) => {
try {
const payload = JSON.parse(uploadRes.data || '{}')
const ok =
uploadRes.statusCode >= 200 &&
uploadRes.statusCode < 300 &&
payload.code === 0 &&
payload.data
if (!ok) {
wx.showToast({ title: payload.message || '头像上传失败', icon: 'none' })
return
}
const rel = payload.data.avatar_url || ''
const full = normalizeAvatarUrl(rel, baseUrl)
wx.setStorageSync(`avatarUrl:${app.globalData.openid}`, full)
this.setData({ avatarUrl: full })
wx.showToast({ title: '头像已更新', icon: 'success' })
} catch {
wx.showToast({ title: '头像上传结果异常', icon: 'none' })
}
},
fail: (err) => wx.showToast({ title: requestFailHint(err.errMsg), icon: 'none' }),
})
},
loadAchievements(profileErrMsg) {
if (!app.globalData.openid) {
this.setData({ error: '登录状态已失效,请重新进入页面' })
return
}
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
const achFail = '成就数据加载失败,请稍后重试'
wx.request({
url: `${baseUrl}/api/v1/achievements`,
header: { 'X-OpenID': app.globalData.openid },
success: (res) => {
const body = res.data || {}
const ok = res.statusCode >= 200 && res.statusCode < 300 && body.code === 0 && Array.isArray(body.data)
if (!ok) {
this.setData({ error: profileErrMsg ? `${profileErrMsg}${achFail}` : achFail })
return
}
const list = body.data
const badges = list.filter((i) => i.is_unlocked).length
this.setData({ badges, error: profileErrMsg || '' })
},
fail: (err) => {
const netHint = requestFailHint(err.errMsg)
const line = profileErrMsg ? `${profileErrMsg}${achFail}` : achFail
this.setData({ error: `${line}${netHint}` })
},
})
},
manualLogin() {
wx.login({
success: (res) => {
const code = res.code || ''
console.log('[login] wx.login success', res)
console.log('[login] code', code)
if (!code) {
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
return
}
wx.request({
url: `${app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl}/api/v1/auth/wx-login`,
method: 'POST',
header: { 'Content-Type': 'application/json' },
data: { code },
success: (loginRes) => {
console.log('[login] wx-login success', loginRes)
const payload = loginRes.data?.data || {}
if (loginRes.statusCode >= 200 && loginRes.statusCode < 300 && loginRes.data?.code === 0 && payload.openid) {
app.globalData.openid = payload.openid
wx.setStorageSync('openid', payload.openid)
wx.showToast({ title: '登录成功', icon: 'success' })
this.loadProfile()
return
}
console.log('[login] wx-login invalid response', loginRes.data)
wx.showToast({ title: '登录接口异常,请稍后重试', icon: 'none' })
},
fail: (err) => {
console.log('[login] wx-login fail', err)
wx.showToast({ title: requestFailHint(err.errMsg), icon: 'none' })
},
})
},
fail: (err) => {
console.log('[login] wx.login fail', err)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
},
})
},
goTo(e) {
const { url } = e.currentTarget.dataset
if (!app.globalData.openid) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
wx.navigateTo({ url })
},
goHome() {
wx.reLaunch({ url: '/pages/index/index' })
},
goToReview() {
wx.navigateTo({ url: '/pages/review-config/review-config' })
},
goToMe() {},
noop() {},
})