更新登录功能,切换为注册账号登录
This commit is contained in:
parent
6ec1860410
commit
6ec4b114da
@ -137,7 +137,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/achievements`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const list = this.decorateList(res.data?.data || [])
|
||||
const sections = this.buildSections(list)
|
||||
@ -156,7 +156,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/achievements/refresh`,
|
||||
method: 'POST',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const payload = res.data?.data || {}
|
||||
const list = this.decorateList(payload.achievements || [])
|
||||
|
||||
@ -36,7 +36,7 @@ Page({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/upload-file`,
|
||||
filePath: file.path,
|
||||
name: 'file',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const payload = JSON.parse(uploadRes.data)
|
||||
|
||||
@ -13,7 +13,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/checkin/stats`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => this.setData({ stats: res.data?.data || null }),
|
||||
fail: () => this.setData({ error: '打卡数据加载失败' }),
|
||||
complete: () => this.setData({ loading: false }),
|
||||
|
||||
@ -11,7 +11,7 @@ Page({
|
||||
this.loadEntry()
|
||||
},
|
||||
loadEntry() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '请先登录' })
|
||||
return
|
||||
}
|
||||
@ -22,7 +22,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${this.entryId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
const entry = res.data?.data || null
|
||||
this.setData({ entry, error: entry ? '' : '未找到词条' })
|
||||
@ -45,7 +45,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${this.data.entry.id}`,
|
||||
method: 'DELETE',
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: () => {
|
||||
wx.showToast({ title: '已删除', icon: 'success' })
|
||||
const pages = getCurrentPages()
|
||||
|
||||
@ -17,14 +17,14 @@ Page({
|
||||
if (entryId) this.loadEntry(entryId)
|
||||
},
|
||||
loadEntry(entryId) {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '请先登录' })
|
||||
return
|
||||
}
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${entryId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
const entry = res.data?.data
|
||||
if (!entry) {
|
||||
@ -71,7 +71,7 @@ Page({
|
||||
wx.navigateBack()
|
||||
},
|
||||
save() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@ -83,7 +83,7 @@ Page({
|
||||
wx.request({
|
||||
url,
|
||||
method,
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': app.globalData.authToken },
|
||||
data: this.data.isEditMode
|
||||
? {
|
||||
entry_type: this.data.entryType,
|
||||
|
||||
@ -12,13 +12,13 @@ Page({
|
||||
async loadStats() {
|
||||
this.setData({ loading: true, error: '' })
|
||||
await app.ensureLogin()
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '登录失败,请稍后重试', loading: false })
|
||||
return
|
||||
}
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/home/stats`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
const payload = res.data?.data
|
||||
if (payload) this.setData({ stats: payload })
|
||||
|
||||
@ -36,7 +36,7 @@ Page({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/upload-file`,
|
||||
filePath: file.path,
|
||||
name: 'file',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const payload = JSON.parse(uploadRes.data)
|
||||
|
||||
@ -11,7 +11,7 @@ Page({
|
||||
this.loadEntry()
|
||||
},
|
||||
loadEntry() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '????' })
|
||||
return
|
||||
}
|
||||
@ -22,7 +22,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${this.entryId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
const entry = res.data?.data || null
|
||||
this.setData({ entry, error: entry ? '' : '?????' })
|
||||
@ -45,7 +45,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${this.data.entry.id}`,
|
||||
method: 'DELETE',
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: () => {
|
||||
wx.showToast({ title: '???', icon: 'success' })
|
||||
const pages = getCurrentPages()
|
||||
|
||||
@ -17,14 +17,14 @@ Page({
|
||||
if (entryId) this.loadEntry(entryId)
|
||||
},
|
||||
loadEntry(entryId) {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '请先登录' })
|
||||
return
|
||||
}
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/entries/${entryId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
const entry = res.data?.data
|
||||
if (!entry) {
|
||||
@ -71,7 +71,7 @@ Page({
|
||||
wx.navigateBack()
|
||||
},
|
||||
save() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@ -83,7 +83,7 @@ Page({
|
||||
wx.request({
|
||||
url,
|
||||
method,
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': app.globalData.authToken },
|
||||
data: this.data.isEditMode
|
||||
? {
|
||||
entry_type: this.data.entryType,
|
||||
|
||||
20
me/file_content_needed.py
Normal file
20
me/file_content_needed.py
Normal file
@ -0,0 +1,20 @@
|
||||
from sqlalchemy import BigInteger, Column, DateTime, String, func
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.db.session import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
||||
openid = Column(String(64), nullable=False, unique=True, index=True)
|
||||
username = Column(String(64), nullable=True, unique=True, index=True)
|
||||
password = Column(String(128), nullable=True)
|
||||
login_type = Column(String(20), nullable=False, default="wechat")
|
||||
nickname = Column(String(100), nullable=True)
|
||||
avatar_url = Column(String(255), nullable=True)
|
||||
created_at = Column(DateTime, nullable=False, server_default=func.now())
|
||||
updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
entries = relationship("Entry", back_populates="user")
|
||||
293
me/me.js
293
me/me.js
@ -7,10 +7,10 @@ function normalizeAvatarUrl(avatarUrl, baseUrl) {
|
||||
|
||||
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('domain')) return '域名未配置或未开启 HTTPS'
|
||||
if (m.includes('ssl') || m.includes('certificate')) return 'HTTPS 证书异常,请检查服务端配置'
|
||||
if (m.includes('timeout') || m.includes('timed out')) return '连接超时,请稍后重试'
|
||||
return '网络异常,请检查网络后重试'
|
||||
return '网络异常,请稍后重试'
|
||||
}
|
||||
|
||||
const DEFAULT_AVATAR = 'data:image/svg+xml;utf8,' + encodeURIComponent(`
|
||||
@ -32,91 +32,148 @@ Page({
|
||||
data: {
|
||||
name: '',
|
||||
nickname: '',
|
||||
username: '',
|
||||
days: 0,
|
||||
badges: 0,
|
||||
openid: '',
|
||||
avatarUrl: '',
|
||||
defaultAvatarUrl: DEFAULT_AVATAR,
|
||||
loading: false,
|
||||
error: '',
|
||||
showNicknameModal: false,
|
||||
nicknameDraft: '',
|
||||
authMode: 'login',
|
||||
authForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
nickname: '',
|
||||
},
|
||||
},
|
||||
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: '',
|
||||
})
|
||||
isLoggedIn() {
|
||||
return !!app.globalData.authToken
|
||||
},
|
||||
onAuthModeChange(e) {
|
||||
this.setData({ authMode: e.currentTarget.dataset.mode || 'login', error: '' })
|
||||
},
|
||||
onAuthInput(e) {
|
||||
const { field } = e.currentTarget.dataset
|
||||
const value = e.detail.value || ''
|
||||
this.setData({ [`authForm.${field}`]: value })
|
||||
},
|
||||
submitAuth() {
|
||||
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
|
||||
const mode = this.data.authMode
|
||||
const username = (this.data.authForm.username || '').trim()
|
||||
const password = (this.data.authForm.password || '').trim()
|
||||
const nickname = (this.data.authForm.nickname || '').trim()
|
||||
|
||||
if (!username || !password) {
|
||||
wx.showToast({ title: '请输入账号和密码', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (mode === 'register' && !nickname) {
|
||||
wx.showToast({ title: '请输入昵称', icon: 'none' })
|
||||
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',
|
||||
url: `${baseUrl}/api/v1/auth/${mode === 'register' ? 'register' : 'login'}`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json' },
|
||||
data: mode === 'register' ? { username, password, nickname } : { username, password },
|
||||
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,
|
||||
})
|
||||
const payload = res.data?.data || null
|
||||
if (res.statusCode < 200 || res.statusCode >= 300 || !payload?.auth_token) {
|
||||
const message = res.data?.detail || res.data?.message || `${mode === 'register' ? '注册' : '登录'}失败`
|
||||
this.setData({ error: message })
|
||||
wx.showToast({ title: message, icon: 'none' })
|
||||
return
|
||||
}
|
||||
const profile = body.data
|
||||
const avatarUrl = wx.getStorageSync(`avatarUrl:${openid}`) || profile.avatar_url || ''
|
||||
const nickname = profile.nickname || '考研战士'
|
||||
const normalizedAvatar = normalizeAvatarUrl(avatarUrl, baseUrl)
|
||||
profileErrMsg = ''
|
||||
|
||||
app.setLoginSession(payload)
|
||||
this.setData({
|
||||
openid,
|
||||
name: nickname,
|
||||
nickname,
|
||||
avatarUrl: normalizedAvatar,
|
||||
nicknameDraft: nickname,
|
||||
days: Number(profile.days || 0),
|
||||
authForm: { username: '', password: '', nickname: '' },
|
||||
username: payload.username || '',
|
||||
})
|
||||
wx.showToast({ title: mode === 'register' ? '注册成功' : '登录成功', icon: 'success' })
|
||||
this.loadProfile()
|
||||
},
|
||||
fail: (err) => {
|
||||
const message = requestFailHint(err.errMsg)
|
||||
this.setData({ error: message })
|
||||
wx.showToast({ title: message, icon: 'none' })
|
||||
},
|
||||
complete: () => this.setData({ loading: false }),
|
||||
})
|
||||
},
|
||||
loadProfile() {
|
||||
const token = app.ensureLogin()
|
||||
Promise.resolve(token).then((authToken) => {
|
||||
if (!authToken) {
|
||||
this.setData({
|
||||
loading: false,
|
||||
name: '未登录',
|
||||
nickname: '未登录',
|
||||
username: '',
|
||||
avatarUrl: '',
|
||||
nicknameDraft: '',
|
||||
badges: 0,
|
||||
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 })
|
||||
},
|
||||
return
|
||||
}
|
||||
|
||||
this.setData({ loading: true, error: '' })
|
||||
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
|
||||
wx.request({
|
||||
url: `${baseUrl}/api/v1/auth/profile`,
|
||||
method: 'GET',
|
||||
header: app.getAuthHeader(),
|
||||
success: (res) => {
|
||||
const body = res.data || {}
|
||||
const profile = body.data
|
||||
const ok = res.statusCode >= 200 && res.statusCode < 300 && body.code === 0 && profile
|
||||
if (!ok) {
|
||||
const message = body.detail || body.message || '个人信息加载失败'
|
||||
this.setData({ error: message })
|
||||
return
|
||||
}
|
||||
|
||||
const identity = profile.username || app.globalData.currentUser?.username || ''
|
||||
const avatarUrl = wx.getStorageSync(`avatarUrl:${identity}`) || profile.avatar_url || ''
|
||||
const normalizedAvatar = normalizeAvatarUrl(avatarUrl, baseUrl)
|
||||
const nickname = profile.nickname || identity || '考研战士'
|
||||
|
||||
app.globalData.currentUser = {
|
||||
...(app.globalData.currentUser || {}),
|
||||
id: profile.id,
|
||||
username: profile.username || '',
|
||||
nickname,
|
||||
loginType: profile.login_type || 'account',
|
||||
}
|
||||
wx.setStorageSync('currentUser', app.globalData.currentUser)
|
||||
|
||||
this.setData({
|
||||
name: nickname,
|
||||
nickname,
|
||||
username: profile.username || '',
|
||||
avatarUrl: normalizedAvatar,
|
||||
nicknameDraft: nickname,
|
||||
days: Number(profile.days || 0),
|
||||
error: '',
|
||||
})
|
||||
this.loadAchievements()
|
||||
},
|
||||
fail: (err) => {
|
||||
this.setData({ error: requestFailHint(err.errMsg) })
|
||||
},
|
||||
complete: () => this.setData({ loading: false }),
|
||||
})
|
||||
})
|
||||
},
|
||||
openNicknameModal() {
|
||||
@ -132,7 +189,7 @@ Page({
|
||||
this.setData({ nicknameDraft: e.detail.value })
|
||||
},
|
||||
saveNickname() {
|
||||
if (!app.globalData.openid) return
|
||||
if (!this.isLoggedIn()) return
|
||||
const nickname = (this.data.nicknameDraft || '').trim()
|
||||
if (!nickname) {
|
||||
wx.showToast({ title: '请输入昵称', icon: 'none' })
|
||||
@ -141,11 +198,14 @@ Page({
|
||||
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 },
|
||||
header: app.getAuthHeader({ 'Content-Type': 'application/json' }),
|
||||
data: { nickname },
|
||||
success: (res) => {
|
||||
const profile = res.data?.data || {}
|
||||
const nextNickname = profile.nickname || nickname
|
||||
const currentUser = { ...(app.globalData.currentUser || {}), nickname: nextNickname }
|
||||
app.globalData.currentUser = currentUser
|
||||
wx.setStorageSync('currentUser', currentUser)
|
||||
this.setData({ nickname: nextNickname, name: nextNickname, nicknameDraft: nextNickname, showNicknameModal: false })
|
||||
wx.showToast({ title: '保存成功', icon: 'success' })
|
||||
},
|
||||
@ -154,7 +214,7 @@ Page({
|
||||
},
|
||||
onChooseAvatar(e) {
|
||||
const tempPath = e.detail?.avatarUrl || ''
|
||||
if (!app.globalData.openid) {
|
||||
if (!this.isLoggedIn()) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@ -164,24 +224,22 @@ Page({
|
||||
}
|
||||
const baseUrl = app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl
|
||||
wx.uploadFile({
|
||||
url: `${baseUrl}/api/v1/auth/avatar?openid=${encodeURIComponent(app.globalData.openid)}`,
|
||||
url: `${baseUrl}/api/v1/auth/avatar`,
|
||||
filePath: tempPath,
|
||||
name: 'file',
|
||||
header: app.getAuthHeader(),
|
||||
success: (uploadRes) => {
|
||||
try {
|
||||
const payload = JSON.parse(uploadRes.data || '{}')
|
||||
const ok =
|
||||
uploadRes.statusCode >= 200 &&
|
||||
uploadRes.statusCode < 300 &&
|
||||
payload.code === 0 &&
|
||||
payload.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)
|
||||
const key = this.data.username || app.globalData.currentUser?.username || ''
|
||||
if (key) wx.setStorageSync(`avatarUrl:${key}`, full)
|
||||
this.setData({ avatarUrl: full })
|
||||
wx.showToast({ title: '头像已更新', icon: 'success' })
|
||||
} catch {
|
||||
@ -191,77 +249,46 @@ Page({
|
||||
fail: (err) => wx.showToast({ title: requestFailHint(err.errMsg), icon: 'none' }),
|
||||
})
|
||||
},
|
||||
loadAchievements(profileErrMsg) {
|
||||
if (!app.globalData.openid) {
|
||||
this.setData({ error: '登录状态已失效,请重新进入页面' })
|
||||
return
|
||||
}
|
||||
loadAchievements() {
|
||||
if (!this.isLoggedIn()) 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 },
|
||||
header: app.getAuthHeader(),
|
||||
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})` })
|
||||
if (!ok) return
|
||||
const badges = body.data.filter((i) => i.is_unlocked).length
|
||||
this.setData({ badges })
|
||||
},
|
||||
})
|
||||
},
|
||||
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' })
|
||||
},
|
||||
logout() {
|
||||
if (!this.isLoggedIn()) return
|
||||
wx.request({
|
||||
url: `${app.getApiBaseUrl ? app.getApiBaseUrl() : app.globalData.baseUrl}/api/v1/auth/logout`,
|
||||
method: 'POST',
|
||||
header: app.getAuthHeader(),
|
||||
complete: () => {
|
||||
app.clearLoginSession()
|
||||
this.setData({
|
||||
name: '未登录',
|
||||
nickname: '未登录',
|
||||
username: '',
|
||||
avatarUrl: '',
|
||||
nicknameDraft: '',
|
||||
badges: 0,
|
||||
days: 0,
|
||||
error: '',
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log('[login] wx.login fail', err)
|
||||
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||||
wx.showToast({ title: '已退出登录', icon: 'success' })
|
||||
},
|
||||
})
|
||||
},
|
||||
goTo(e) {
|
||||
const { url } = e.currentTarget.dataset
|
||||
if (!app.globalData.openid) {
|
||||
if (!this.isLoggedIn()) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
25
me/me.wxml
25
me/me.wxml
@ -6,10 +6,19 @@
|
||||
<text class="empty-subtitle">正在获取个人信息</text>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{!openid}}" class="empty-state">
|
||||
<text class="empty-title">还未登录</text>
|
||||
<text class="empty-subtitle">请点击下方按钮手动登录后继续使用</text>
|
||||
<button class="secondary-btn error-btn" bindtap="manualLogin">手动登录</button>
|
||||
<view wx:elif="{{!username}}" class="empty-state">
|
||||
<text class="empty-title">账号登录</text>
|
||||
<text class="empty-subtitle">使用账号密码登录,或先注册一个新账号</text>
|
||||
<view class="auth-switch">
|
||||
<view class="auth-chip {{authMode === 'login' ? 'active' : ''}}" data-mode="login" bindtap="onAuthModeChange">登录</view>
|
||||
<view class="auth-chip {{authMode === 'register' ? 'active' : ''}}" data-mode="register" bindtap="onAuthModeChange">注册</view>
|
||||
</view>
|
||||
<view class="auth-card">
|
||||
<input class="auth-input" placeholder="请输入账号" value="{{authForm.username}}" data-field="username" bindinput="onAuthInput" />
|
||||
<input class="auth-input" password="true" placeholder="请输入密码" value="{{authForm.password}}" data-field="password" bindinput="onAuthInput" />
|
||||
<input wx:if="{{authMode === 'register'}}" class="auth-input" placeholder="请输入昵称" value="{{authForm.nickname}}" data-field="nickname" bindinput="onAuthInput" />
|
||||
<button class="auth-submit" bindtap="submitAuth">{{authMode === 'register' ? '注册并登录' : '登录'}}</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:elif="{{error}}" class="error-banner">
|
||||
@ -35,12 +44,12 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{openid && badges === 0}}" class="empty-state small">
|
||||
<view wx:if="{{username && badges === 0}}" class="empty-state small">
|
||||
<text class="empty-title">还没有解锁成就</text>
|
||||
<text class="empty-subtitle">继续复习和打卡,成就会逐渐点亮</text>
|
||||
</view>
|
||||
|
||||
<view wx:if="{{openid}}" class="grid-2">
|
||||
<view wx:if="{{username}}" class="grid-2">
|
||||
<view class="glass-card accent-tertiary" bindtap="goTo" data-url="/package-stats/pages/achievements/achievements">
|
||||
<view class="card-glow"></view>
|
||||
<view class="card-icon icon-tertiary">🏅</view>
|
||||
@ -85,6 +94,8 @@
|
||||
<text class="settings-text">设置中心</text>
|
||||
<view class="chevron">›</view>
|
||||
</view>
|
||||
|
||||
<button wx:if="{{username}}" class="logout-btn" bindtap="logout">退出登录</button>
|
||||
</scroll-view>
|
||||
|
||||
<view wx:if="{{showNicknameModal}}" class="modal-mask" bindtap="closeNicknameModal">
|
||||
@ -106,4 +117,4 @@
|
||||
<view class="nav-item" bindtap="goToReview"><text class="nav-icon">↻</text><text class="nav-label">复习</text></view>
|
||||
<view class="nav-item active"><text class="nav-icon">☺</text><text class="nav-label">我的</text></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
68
me/me.wxss
68
me/me.wxss
@ -79,6 +79,62 @@ page {
|
||||
min-width: 200rpx
|
||||
}
|
||||
|
||||
.auth-switch {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin: 32rpx 0 24rpx
|
||||
}
|
||||
|
||||
.auth-chip {
|
||||
min-width: 160rpx;
|
||||
padding: 18rpx 0;
|
||||
border-radius: 9999rpx;
|
||||
background: rgba(255, 255, 255, .05);
|
||||
border: 1rpx solid rgba(255, 255, 255, .08);
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #aaabb0
|
||||
}
|
||||
|
||||
.auth-chip.active {
|
||||
color: #0c0e12;
|
||||
background: linear-gradient(135deg, #00e3fd, #c19cff)
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
width: 100%;
|
||||
padding: 28rpx;
|
||||
border-radius: 28rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, .08);
|
||||
background: rgba(23, 26, 31, .72);
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
.auth-input {
|
||||
width: 100%;
|
||||
height: 84rpx;
|
||||
margin-bottom: 18rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 18rpx;
|
||||
border: 1rpx solid rgba(255, 255, 255, .08);
|
||||
background: #111318;
|
||||
color: #f6f6fc;
|
||||
font-size: 26rpx;
|
||||
box-sizing: border-box
|
||||
}
|
||||
|
||||
.auth-submit {
|
||||
margin-top: 12rpx;
|
||||
border: none;
|
||||
border-radius: 20rpx;
|
||||
height: 84rpx;
|
||||
line-height: 84rpx;
|
||||
background: linear-gradient(135deg, #c19cff, #9146ff);
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
.hero-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -381,6 +437,16 @@ page {
|
||||
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, .1)
|
||||
}
|
||||
|
||||
.logout-btn {
|
||||
margin-top: 28rpx;
|
||||
border: 1rpx solid rgba(255, 110, 132, .35);
|
||||
border-radius: 24rpx;
|
||||
background: rgba(255, 110, 132, .08);
|
||||
color: #ff6e84;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700
|
||||
}
|
||||
|
||||
.settings-icon {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
@ -542,4 +608,4 @@ page {
|
||||
|
||||
.nav-label {
|
||||
font-size: 20rpx
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/progress/review`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const reviewList = res.data?.data || []
|
||||
const grouped = groupByType(reviewList)
|
||||
@ -41,7 +41,7 @@ Page({
|
||||
})
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/progress/mastered`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const masteredList = res.data?.data || []
|
||||
const grouped = groupByType(masteredList)
|
||||
|
||||
@ -142,7 +142,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/achievements`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const list = this.decorateList(res.data?.data || [])
|
||||
const sections = this.buildSections(list)
|
||||
@ -161,7 +161,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/achievements/refresh`,
|
||||
method: 'POST',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const payload = res.data?.data || {}
|
||||
const list = this.decorateList(payload.achievements || [])
|
||||
|
||||
@ -13,7 +13,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/checkin/stats`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => this.setData({ stats: res.data?.data || null }),
|
||||
fail: () => this.setData({ error: '打卡数据加载失败' }),
|
||||
complete: () => this.setData({ loading: false }),
|
||||
|
||||
@ -31,7 +31,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/progress/review`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const reviewList = res.data?.data || []
|
||||
const grouped = groupByType(reviewList)
|
||||
@ -41,7 +41,7 @@ Page({
|
||||
})
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/progress/mastered`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const masteredList = res.data?.data || []
|
||||
const grouped = groupByType(masteredList)
|
||||
|
||||
@ -69,15 +69,15 @@ Page({
|
||||
const question_mode = questionModeMap[this.data.activeMode]
|
||||
const scope_config = {}
|
||||
this.setData({ loading: true, error: '' })
|
||||
app.ensureLogin().then((openid) => {
|
||||
if (!openid) {
|
||||
app.ensureLogin().then((authToken) => {
|
||||
if (!authToken) {
|
||||
this.setData({ loading: false, error: '请先登录后再开始复习' })
|
||||
return
|
||||
}
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/generate`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': openid },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': authToken },
|
||||
data: {
|
||||
scope_type,
|
||||
scope_config,
|
||||
|
||||
@ -30,7 +30,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review-result?session_id=${sessionId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const data = res.data?.data || {}
|
||||
const total = Number(data.total || 0)
|
||||
|
||||
@ -46,13 +46,13 @@ Page({
|
||||
this.loadSettingsAndQuestions()
|
||||
},
|
||||
loadSettingsAndQuestions() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '请先登录' })
|
||||
return
|
||||
}
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/settings`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
app.globalData.userSettings = res.data?.data || {}
|
||||
this.setData({ settings: getReviewSettings() })
|
||||
@ -128,7 +128,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/questions?session_id=${session.id}`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
const message = res.data?.detail || res.data?.message || '题目加载失败'
|
||||
@ -339,7 +339,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/submit`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
data: payload,
|
||||
success: (res) => {
|
||||
const result = res.data?.data || {}
|
||||
@ -422,7 +422,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/finish?session_id=${app.globalData.reviewSessionMeta.id}`,
|
||||
method: 'POST',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: () => wx.navigateTo({ url: '/pages/review-result/review-result' }),
|
||||
fail: () => {
|
||||
wx.showToast({ title: '提交结果失败,已本地完成', icon: 'none' })
|
||||
|
||||
@ -69,15 +69,15 @@ Page({
|
||||
const question_mode = questionModeMap[this.data.activeMode]
|
||||
const scope_config = {}
|
||||
this.setData({ loading: true, error: '' })
|
||||
app.ensureLogin().then((openid) => {
|
||||
if (!openid) {
|
||||
app.ensureLogin().then((authToken) => {
|
||||
if (!authToken) {
|
||||
this.setData({ loading: false, error: '??????????' })
|
||||
return
|
||||
}
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/generate`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': openid },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': authToken },
|
||||
data: {
|
||||
scope_type,
|
||||
scope_config,
|
||||
|
||||
@ -30,7 +30,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review-result?session_id=${sessionId}`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
const data = res.data?.data || {}
|
||||
const total = Number(data.total || 0)
|
||||
|
||||
@ -46,13 +46,13 @@ Page({
|
||||
this.loadSettingsAndQuestions()
|
||||
},
|
||||
loadSettingsAndQuestions() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '????' })
|
||||
return
|
||||
}
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/settings`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
app.globalData.userSettings = res.data?.data || {}
|
||||
this.setData({ settings: getReviewSettings() })
|
||||
@ -128,7 +128,7 @@ Page({
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/questions?session_id=${session.id}`,
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: (res) => {
|
||||
if (res.statusCode < 200 || res.statusCode >= 300) {
|
||||
const message = res.data?.detail || res.data?.message || '??????'
|
||||
@ -339,7 +339,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/submit`,
|
||||
method: 'POST',
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
data: payload,
|
||||
success: (res) => {
|
||||
const result = res.data?.data || {}
|
||||
@ -422,7 +422,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/review/finish?session_id=${app.globalData.reviewSessionMeta.id}`,
|
||||
method: 'POST',
|
||||
header: { 'X-OpenID': app.globalData.openid || '' },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken || '' },
|
||||
success: () => wx.navigateTo({ url: '/pages/review/review-result/review-result' }),
|
||||
fail: () => {
|
||||
wx.showToast({ title: '????????????', icon: 'none' })
|
||||
|
||||
@ -38,14 +38,14 @@ Page({
|
||||
})
|
||||
},
|
||||
loadSettings() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
this.setData({ error: '请先登录' })
|
||||
return
|
||||
}
|
||||
this.setData({ loading: true, error: '' })
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/settings`,
|
||||
header: { 'X-OpenID': app.globalData.openid },
|
||||
header: { 'X-Auth-Token': app.globalData.authToken },
|
||||
success: (res) => {
|
||||
this.syncDerivedState(res.data?.data || null)
|
||||
},
|
||||
@ -93,7 +93,7 @@ Page({
|
||||
this.setData({ settings })
|
||||
},
|
||||
save() {
|
||||
if (!app.globalData.openid) {
|
||||
if (!app.globalData.authToken) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
@ -101,7 +101,7 @@ Page({
|
||||
wx.request({
|
||||
url: `${app.globalData.baseUrl}/api/v1/settings`,
|
||||
method: 'PUT',
|
||||
header: { 'Content-Type': 'application/json', 'X-OpenID': app.globalData.openid },
|
||||
header: { 'Content-Type': 'application/json', 'X-Auth-Token': app.globalData.authToken },
|
||||
data: settings,
|
||||
success: () => wx.showToast({ title: '已保存', icon: 'success' }),
|
||||
fail: () => wx.showToast({ title: '保存失败', icon: 'none' }),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user