flaunt-web/src/stores/user.js

112 lines
3.6 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.

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 }
})