183 lines
5.0 KiB
JavaScript
183 lines
5.0 KiB
JavaScript
|
|
import { defineStore } from 'pinia'
|
|||
|
|
import { ref, computed } from 'vue'
|
|||
|
|
import { login, register, logout, getCurrentUser } from '@/api/auth'
|
|||
|
|
|
|||
|
|
export const useUserStore = defineStore('user', () => {
|
|||
|
|
// 状态 - 从 localStorage 尝试恢复用户信息
|
|||
|
|
const user = ref(null)
|
|||
|
|
const token = ref(null)
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const initialized = ref(false)
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const cachedUser = localStorage.getItem('user')
|
|||
|
|
const cachedToken = localStorage.getItem('token')
|
|||
|
|
if (cachedUser && cachedToken) {
|
|||
|
|
user.value = JSON.parse(cachedUser)
|
|||
|
|
token.value = cachedToken
|
|||
|
|
}
|
|||
|
|
} catch (_) {
|
|||
|
|
// ignore localStorage parse errors
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算属性
|
|||
|
|
const isAuthenticated = computed(() => !!user.value)
|
|||
|
|
const isAdmin = computed(() => user.value?.role === 'ROLE_ADMIN' || user.value?.role === 'ROLE_SUPER_ADMIN')
|
|||
|
|
const isSuperAdmin = computed(() => user.value?.role === 'ROLE_SUPER_ADMIN')
|
|||
|
|
const username = computed(() => user.value?.username || '')
|
|||
|
|
|
|||
|
|
// 可用积分(总积分 - 冻结积分)
|
|||
|
|
const availablePoints = computed(() => {
|
|||
|
|
if (!user.value) return 0
|
|||
|
|
const total = user.value.points || 0
|
|||
|
|
const frozen = user.value.frozenPoints || 0
|
|||
|
|
return Math.max(0, total - frozen)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 登录
|
|||
|
|
const loginUser = async (credentials) => {
|
|||
|
|
try {
|
|||
|
|
loading.value = true
|
|||
|
|
const response = await login(credentials)
|
|||
|
|
|
|||
|
|
if (response.success) {
|
|||
|
|
// 使用JWT认证,保存token和用户信息
|
|||
|
|
user.value = response.data.user
|
|||
|
|
token.value = response.data.token
|
|||
|
|
|
|||
|
|
// 保存到localStorage,关闭浏览器后仍保持登录
|
|||
|
|
localStorage.setItem('token', response.data.token)
|
|||
|
|
localStorage.setItem('user', JSON.stringify(user.value))
|
|||
|
|
return { success: true }
|
|||
|
|
} else {
|
|||
|
|
return { success: false, message: response.message }
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Login error:', error)
|
|||
|
|
return { success: false, message: '登录失败,请检查网络连接' }
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 注册
|
|||
|
|
const registerUser = async (userData) => {
|
|||
|
|
try {
|
|||
|
|
loading.value = true
|
|||
|
|
const response = await register(userData)
|
|||
|
|
|
|||
|
|
if (response.success) {
|
|||
|
|
return { success: true, message: '注册成功,请登录' }
|
|||
|
|
} else {
|
|||
|
|
return { success: false, message: response.message }
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Register error:', error)
|
|||
|
|
return { success: false, message: '注册失败,请检查网络连接' }
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 登出
|
|||
|
|
const logoutUser = async () => {
|
|||
|
|
try {
|
|||
|
|
// JWT无状态,直接清除localStorage即可
|
|||
|
|
token.value = null
|
|||
|
|
user.value = null
|
|||
|
|
localStorage.removeItem('token')
|
|||
|
|
localStorage.removeItem('user')
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Logout error:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取当前用户信息
|
|||
|
|
const fetchCurrentUser = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await getCurrentUser()
|
|||
|
|
// 统一使用 response.data 格式
|
|||
|
|
const data = response.data || response
|
|||
|
|
|
|||
|
|
if (data.success) {
|
|||
|
|
user.value = data.data
|
|||
|
|
localStorage.setItem('user', JSON.stringify(user.value))
|
|||
|
|
} else {
|
|||
|
|
console.warn('获取用户信息失败:', data.message)
|
|||
|
|
// 不要立即清除用户数据,保持当前登录状态
|
|||
|
|
// 只在明确的401/认证失败时才由axios拦截器处理登出
|
|||
|
|
}
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Fetch user error:', error)
|
|||
|
|
// 请求失败时不强制清除,保持现有本地态
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除用户数据
|
|||
|
|
const clearUserData = () => {
|
|||
|
|
token.value = null
|
|||
|
|
user.value = null
|
|||
|
|
// 清除 localStorage 中的用户数据
|
|||
|
|
localStorage.removeItem('token')
|
|||
|
|
localStorage.removeItem('user')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化
|
|||
|
|
const init = async () => {
|
|||
|
|
if (initialized.value) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从 localStorage 恢复用户状态
|
|||
|
|
const savedToken = localStorage.getItem('token')
|
|||
|
|
const savedUser = localStorage.getItem('user')
|
|||
|
|
|
|||
|
|
console.log('Store init - savedToken:', savedToken ? savedToken.substring(0, 30) + '...' : 'null')
|
|||
|
|
|
|||
|
|
if (savedToken && savedUser) {
|
|||
|
|
try {
|
|||
|
|
token.value = savedToken
|
|||
|
|
user.value = JSON.parse(savedUser)
|
|||
|
|
|
|||
|
|
console.log('恢复用户状态:', user.value?.username)
|
|||
|
|
|
|||
|
|
// 刷新用户信息(确保角色等信息是最新的)
|
|||
|
|
await fetchCurrentUser()
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Failed to restore user state:', error)
|
|||
|
|
clearUserData()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
initialized.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 重置初始化状态(登录成功后调用)
|
|||
|
|
const resetInitialized = () => {
|
|||
|
|
initialized.value = false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
// 状态
|
|||
|
|
user,
|
|||
|
|
token,
|
|||
|
|
loading,
|
|||
|
|
// 计算属性
|
|||
|
|
isAuthenticated,
|
|||
|
|
isAdmin,
|
|||
|
|
isSuperAdmin,
|
|||
|
|
username,
|
|||
|
|
availablePoints,
|
|||
|
|
// 方法
|
|||
|
|
loginUser,
|
|||
|
|
registerUser,
|
|||
|
|
logoutUser,
|
|||
|
|
fetchCurrentUser,
|
|||
|
|
clearUserData,
|
|||
|
|
init,
|
|||
|
|
initialized,
|
|||
|
|
resetInitialized
|
|||
|
|
}
|
|||
|
|
})
|