212 lines
7.2 KiB
TypeScript
212 lines
7.2 KiB
TypeScript
import { request } from '../../utils/request'
|
|
import * as auth from '../../utils/auth'
|
|
import { logger } from '../../utils/logger'
|
|
|
|
// 日志模块名
|
|
const LOG = 'login'
|
|
import { STORAGE_KEYS, QuotaType } from '../../utils/config'
|
|
import { LoginResult, UserInfo, QuotaItem } from '../../types/models'
|
|
import { formatDate } from '../../utils/format'
|
|
|
|
interface GiftItem {
|
|
icon: string
|
|
name: string
|
|
value: string
|
|
}
|
|
|
|
Page({
|
|
data: {
|
|
loading: false,
|
|
redirect: '', // 登录后要回跳的页面(如从好友分享链接进入)
|
|
userInfo: {} as UserInfo,
|
|
giftVisible: false,
|
|
giftItems: [] as GiftItem[],
|
|
},
|
|
|
|
onLoad(query: Record<string, string>) {
|
|
logger.debug(LOG, 'onLoad, query=', JSON.stringify(query), 'isLoggedIn=', auth.isLoggedIn())
|
|
if (query.redirect) this.setData({ redirect: query.redirect })
|
|
if (auth.isLoggedIn()) {
|
|
this.goAfterLogin()
|
|
}
|
|
},
|
|
|
|
// 登录完成后的落地页:带 redirect 时回跳来源页(分享详情等),否则进衣橱
|
|
goAfterLogin(fallback = false) {
|
|
const redirect = this.data.redirect
|
|
logger.debug(LOG, 'goAfterLogin, redirect=', redirect, 'fallback=', fallback)
|
|
if (redirect && !fallback) {
|
|
wx.redirectTo({
|
|
url: redirect,
|
|
fail: (e) => {
|
|
logger.warn(LOG, 'redirect 回跳失败,回退衣橱, err=', e && e.errMsg)
|
|
this.goAfterLogin(true)
|
|
},
|
|
})
|
|
return
|
|
}
|
|
wx.switchTab({
|
|
url: '/pages/wardrobe/wardrobe',
|
|
fail: (e) => logger.warn(LOG, 'switchTab 衣橱失败, err=', e && e.errMsg),
|
|
})
|
|
},
|
|
|
|
onLogin() {
|
|
if (this.data.loading) {
|
|
logger.debug(LOG, '登录中,忽略重复点击')
|
|
return
|
|
}
|
|
logger.debug(LOG, '点击登录, redirect=', this.data.redirect)
|
|
this.setData({ loading: true })
|
|
|
|
// 超时兜底:wx.login / wx.request 异常挂起时复位按钮,避免永久 loading 点不动
|
|
this.clearLoginTimer()
|
|
;(this as any).loginTimer = setTimeout(() => {
|
|
;(this as any).loginTimer = null
|
|
if (!this.data.loading) return
|
|
logger.warn(LOG, '登录超时(30s),复位按钮')
|
|
this.setData({ loading: false })
|
|
wx.showToast({ title: '登录超时,请重试', icon: 'none' })
|
|
}, 30000)
|
|
|
|
wx.login({
|
|
success: (res) => {
|
|
if (!res.code) {
|
|
logger.error(LOG, 'wx.login 返回空 code')
|
|
this.finishLogin()
|
|
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
|
return
|
|
}
|
|
logger.debug(LOG, 'wx.login 成功, codeLen=', res.code.length)
|
|
request<LoginResult>({
|
|
url: '/user/wxlogin',
|
|
method: 'POST',
|
|
data: { code: res.code },
|
|
auth: false,
|
|
})
|
|
.then((data) => {
|
|
logger.info(
|
|
LOG,
|
|
'wxlogin 成功, isNew=',
|
|
data.user_info && data.user_info.is_new_user,
|
|
'expireTime=',
|
|
data.expire_time,
|
|
)
|
|
auth.setTokens({
|
|
accessToken: data.access_token,
|
|
refreshToken: data.refresh_token,
|
|
expireTime: data.expire_time,
|
|
})
|
|
wx.setStorageSync(STORAGE_KEYS.USER_INFO, data.user_info)
|
|
// 新登录用户重置未读红点,进入 tab 页后由 tabBar 拉取真实值
|
|
wx.setStorageSync(STORAGE_KEYS.UNREAD, 0)
|
|
this.setData({ userInfo: data.user_info })
|
|
if (data.user_info.is_new_user) {
|
|
this.showWelcomeGift(data.user_info)
|
|
} else if (!data.user_info.birth_day) {
|
|
this.showBirthGuide()
|
|
} else {
|
|
this.goAfterLogin()
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
logger.error(LOG, 'wxlogin 失败, code=', e && e.code, 'msg=', e && e.message)
|
|
})
|
|
.then(() => {
|
|
// 无论成功还是失败都复位:分支走向礼包/生日引导时若不复位,登录按钮会永久点不动
|
|
this.finishLogin()
|
|
})
|
|
},
|
|
fail: (e) => {
|
|
logger.error(LOG, 'wx.login 失败, errMsg=', e && e.errMsg)
|
|
this.finishLogin()
|
|
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
|
},
|
|
})
|
|
},
|
|
|
|
// 复位登录态:清超时定时器 + 复位按钮
|
|
finishLogin() {
|
|
this.clearLoginTimer()
|
|
this.setData({ loading: false })
|
|
},
|
|
|
|
clearLoginTimer() {
|
|
const timer = (this as any).loginTimer
|
|
if (timer) {
|
|
clearTimeout(timer)
|
|
;(this as any).loginTimer = null
|
|
}
|
|
},
|
|
|
|
onUnload() {
|
|
this.clearLoginTimer()
|
|
},
|
|
|
|
showWelcomeGift(userInfo: UserInfo) {
|
|
// 额度只用于礼包展示:失败静默降级,绝不能触发登出/清 token
|
|
request<{ quotas: QuotaItem[] }>({ url: '/quota/info', showError: false, silentAuth: true })
|
|
.then((res) => this.setData({ giftItems: this.buildGift(userInfo, res.quotas), giftVisible: true }))
|
|
.catch((e) => {
|
|
// 额度拉取失败不阻塞礼包展示,降级为空额度;绝不能触发登出/清 token
|
|
logger.warn(LOG, '礼包额度拉取失败,降级展示, code=', e && e.code)
|
|
this.setData({ giftItems: this.buildGift(userInfo, []), giftVisible: true })
|
|
})
|
|
},
|
|
|
|
buildGift(userInfo: UserInfo, quotas: QuotaItem[]): GiftItem[] {
|
|
const total = (t: number) => {
|
|
const q = quotas.find((it) => it.quota_type === t)
|
|
return q ? q.total : 0
|
|
}
|
|
const items: GiftItem[] = []
|
|
if (userInfo.vip_type === 4 && userInfo.vip_expire) {
|
|
items.push({ icon: 'icon-crown', name: '体验会员', value: `有效期至 ${formatDate(userInfo.vip_expire)}` })
|
|
}
|
|
items.push({ icon: 'icon-camera', name: '上传额度', value: `${total(QuotaType.UPLOAD)} 次 · 永久` })
|
|
items.push({ icon: 'icon-sparkles', name: 'AI 推荐额度', value: `${total(QuotaType.AI)} 次/天` })
|
|
items.push({ icon: 'icon-shirt', name: '搭配分享额度', value: `${total(QuotaType.OUTFIT_SHARE)} 次/天` })
|
|
items.push({ icon: 'icon-hanger', name: '衣柜分享额度', value: `${total(QuotaType.CLOSET_SHARE)} 次/天` })
|
|
return items
|
|
},
|
|
|
|
onGiftConfirm() {
|
|
this.setData({ giftVisible: false })
|
|
if (!this.data.userInfo.birth_day) {
|
|
this.showBirthGuide()
|
|
} else {
|
|
this.goAfterLogin()
|
|
}
|
|
},
|
|
|
|
showBirthGuide() {
|
|
wx.showModal({
|
|
title: '完善个人信息',
|
|
content: '设置生日后即可查看今日星座运势',
|
|
confirmText: '去完善',
|
|
cancelText: '稍后再说',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
// switchTab 不支持 query 传参,用 storage 标记让「我的」页 onShow 自动弹出编辑弹窗
|
|
wx.setStorageSync(STORAGE_KEYS.OPEN_PROFILE_EDIT, 1)
|
|
wx.switchTab({
|
|
url: '/pages/profile/profile',
|
|
fail: (e) => {
|
|
logger.warn(LOG, 'switchTab 个人资料失败, err=', e && e.errMsg)
|
|
// 跳转失败时清除标记,避免下次进入「我的」页意外弹窗
|
|
wx.removeStorageSync(STORAGE_KEYS.OPEN_PROFILE_EDIT)
|
|
},
|
|
})
|
|
} else {
|
|
this.goAfterLogin()
|
|
}
|
|
},
|
|
fail: (e) => logger.warn(LOG, '生日引导弹窗失败, err=', e && e.errMsg),
|
|
})
|
|
},
|
|
|
|
openAgreement() {
|
|
wx.showToast({ title: '协议内容敬请期待', icon: 'none' })
|
|
},
|
|
})
|