112 lines
3.6 KiB
JavaScript
112 lines
3.6 KiB
JavaScript
import { defineStore } from 'pinia'
|
||
import { ref, computed } from 'vue'
|
||
import authService from '@/api/authService'
|
||
import { checkUserVip } from '@/utils/appUtils'
|
||
|
||
export const useUserStore = defineStore('user', () => {
|
||
const username = ref('')
|
||
const isVip = ref(false)
|
||
const avatarUrl = ref('')
|
||
const membershipExpireAt = ref(null)
|
||
const userInfoRaw = ref(null) // 响应式保存最新完整的用户原始数据
|
||
const tokenChecked = ref(false) // 是否已完成 Token 校验
|
||
const tokenValid = ref(false) // Token 是否真实有效
|
||
|
||
// 严谨的全局登录状态判定:必须 Token 有效、用户名存在且本地有 token 凭证
|
||
const isLoggedIn = computed(() => {
|
||
return tokenValid.value && !!username.value && !!localStorage.getItem('token')
|
||
})
|
||
|
||
// 设置/保存用户信息
|
||
function setUser(data, fallbackPhone = '') {
|
||
if (!data) return
|
||
const userObj = data.user || data
|
||
userInfoRaw.value = userObj
|
||
|
||
// 写入本地缓存,供快照加载与 checkUserVip 自动使用
|
||
try {
|
||
localStorage.setItem('user_info', JSON.stringify(userObj))
|
||
} catch (e) { }
|
||
|
||
// 游客账号特征判断:temp 为 true,或角色 role 为 '8',或名称包含 '游客'
|
||
const isGuest = userObj.temp === true || userObj.role === '8' || userObj.name === '游客' || userObj.nickname === '游客'
|
||
|
||
if (isGuest) {
|
||
username.value = ''
|
||
isVip.value = false
|
||
avatarUrl.value = ''
|
||
tokenChecked.value = true
|
||
tokenValid.value = false
|
||
return
|
||
}
|
||
|
||
username.value = userObj.name || '已登录用户'
|
||
isVip.value = checkUserVip(userObj)
|
||
avatarUrl.value = userObj.avater || userObj.avatar || ''
|
||
membershipExpireAt.value = userObj.vip_expire || null
|
||
tokenChecked.value = true
|
||
tokenValid.value = true
|
||
}
|
||
|
||
let fetchPromise = null
|
||
|
||
async function fetchProfile() {
|
||
const token = localStorage.getItem('token')
|
||
if (!token) {
|
||
reset()
|
||
return null
|
||
}
|
||
|
||
// 1. 本地快照秒开:瞬间解析 user_info 填充响应式数据,0毫秒延迟
|
||
const localUserInfo = localStorage.getItem('user_info')
|
||
if (localUserInfo) {
|
||
try {
|
||
const parsed = JSON.parse(localUserInfo)
|
||
setUser(parsed)
|
||
} catch (e) { }
|
||
}
|
||
|
||
// 2. 防并发防阻塞:如果后台已有进行中的网络校验,直接复用 Promise
|
||
if (fetchPromise) {
|
||
return fetchPromise
|
||
}
|
||
|
||
// 3. 实时后台接口校验:精准兼容多种 Api 返回数据结构并更新 store 与缓存
|
||
fetchPromise = (async () => {
|
||
try {
|
||
const res = await authService.getUserInfo()
|
||
// 兼容 res.data 结构以及直接返回用户对象的结构
|
||
const userData = (res && res.data) ? res.data : (res && (res.vip !== undefined || res.user_id || res.name) ? res : null)
|
||
|
||
if (userData) {
|
||
setUser(userData)
|
||
console.log("最新后台用户信息已同步:", userData)
|
||
return userData
|
||
} else if (res && res.code && res.code !== 0) {
|
||
reset()
|
||
}
|
||
} catch (e) {
|
||
console.error("同步后台用户信息异常:", e)
|
||
} finally {
|
||
fetchPromise = null
|
||
}
|
||
})()
|
||
|
||
return fetchPromise
|
||
}
|
||
|
||
function reset() {
|
||
username.value = ''
|
||
isVip.value = false
|
||
avatarUrl.value = ''
|
||
membershipExpireAt.value = null
|
||
userInfoRaw.value = null
|
||
tokenChecked.value = true
|
||
tokenValid.value = false
|
||
localStorage.removeItem('token')
|
||
localStorage.removeItem('user_info')
|
||
}
|
||
|
||
return { username, isVip, avatarUrl, membershipExpireAt, userInfoRaw, tokenChecked, tokenValid, isLoggedIn, setUser, fetchProfile, reset, checkVip: checkUserVip }
|
||
})
|