Files
aiot-uniapp/src/store/token.ts
feige996 61fb851fa3 fix: 修复登录和刷新token时直接使用响应数据的问题
移除对响应数据中.data的直接引用,改为使用完整响应对象
2025-09-16 09:37:45 +08:00

289 lines
7.5 KiB
TypeScript
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 type { IAuthLoginRes } from '@/api/types/login'
import { defineStore } from 'pinia'
import { computed, ref } from 'vue' // 修复:导入 computed
import {
login as _login,
logout as _logout,
refreshToken as _refreshToken,
wxLogin as _wxLogin,
getWxCode,
} from '@/api/login'
import { isDoubleTokenRes, isSingleTokenRes } from '@/api/types/login'
import { isDoubleTokenMode } from '@/utils'
import { useUserStore } from './user'
// 初始化状态
const tokenInfoState = isDoubleTokenMode
? {
accessToken: '',
accessExpiresIn: 0,
refreshToken: '',
refreshExpiresIn: 0,
}
: {
token: '',
expiresIn: 0,
}
export const useTokenStore = defineStore(
'token',
() => {
// 定义用户信息
const tokenInfo = ref<IAuthLoginRes>({ ...tokenInfoState })
// 设置用户信息
const setTokenInfo = (val: IAuthLoginRes) => {
tokenInfo.value = val
// 计算并存储过期时间
const now = Date.now()
if (isSingleTokenRes(val)) {
// 单token模式
const expireTime = now + val.expiresIn * 1000
uni.setStorageSync('accessTokenExpireTime', expireTime)
}
else if (isDoubleTokenRes(val)) {
// 双token模式
const accessExpireTime = now + val.accessExpiresIn * 1000
const refreshExpireTime = now + val.refreshExpiresIn * 1000
uni.setStorageSync('accessTokenExpireTime', accessExpireTime)
uni.setStorageSync('refreshTokenExpireTime', refreshExpireTime)
}
}
/**
* 判断token是否过期
*/
const isTokenExpired = computed(() => {
if (!tokenInfo.value) {
return true
}
const now = Date.now()
const expireTime = uni.getStorageSync('accessTokenExpireTime')
if (!expireTime)
return true
return now >= expireTime
})
/**
* 判断refreshToken是否过期
*/
const isRefreshTokenExpired = computed(() => {
if (!isDoubleTokenMode)
return true
const now = Date.now()
const refreshExpireTime = uni.getStorageSync('refreshTokenExpireTime')
if (!refreshExpireTime)
return true
return now >= refreshExpireTime
})
/**
* 登录成功后处理逻辑
* @param tokenInfo 登录返回的token信息
*/
async function _postLogin(tokenInfo: IAuthLoginRes) {
setTokenInfo(tokenInfo)
const userStore = useUserStore()
await userStore.fetchUserInfo()
}
/**
* 用户登录
* @param credentials 登录参数
* @returns 登录结果
*/
const login = async (credentials: {
username: string
password: string
code: string
uuid: string
}) => {
try {
const res = await _login(credentials)
console.log('普通登录-res: ', res)
await _postLogin(res)
uni.showToast({
title: '登录成功',
icon: 'success',
})
return res
}
catch (error) {
console.error('登录失败:', error)
uni.showToast({
title: '登录失败,请重试',
icon: 'error',
})
throw error
}
}
/**
* 微信登录
* @returns 登录结果
*/
const wxLogin = async () => {
try {
// 获取微信小程序登录的code
const code = await getWxCode()
console.log('微信登录-code: ', code)
const res = await _wxLogin(code)
console.log('微信登录-res: ', res)
await _postLogin(res)
uni.showToast({
title: '登录成功',
icon: 'success',
})
return res
}
catch (error) {
console.error('微信登录失败:', error)
uni.showToast({
title: '微信登录失败,请重试',
icon: 'error',
})
throw error
}
}
/**
* 退出登录 并 删除用户信息
*/
const logout = async () => {
try {
// TODO 实现自己的退出登录逻辑
await _logout()
}
catch (error) {
console.error('退出登录失败:', error)
}
finally {
// 无论成功失败都需要清除本地token信息
// 清除存储的过期时间
uni.removeStorageSync('accessTokenExpireTime')
uni.removeStorageSync('refreshTokenExpireTime')
console.log('退出登录-清除用户信息')
tokenInfo.value = { ...tokenInfoState }
uni.removeStorageSync('token')
const userStore = useUserStore()
userStore.clearUserInfo()
}
}
/**
* 刷新token
* @returns 刷新结果
*/
const refreshToken = async () => {
if (!isDoubleTokenMode) {
console.error('单token模式不支持刷新token')
throw new Error('单token模式不支持刷新token')
}
try {
// 安全检查确保refreshToken存在
if (!isDoubleTokenRes(tokenInfo.value) || !tokenInfo.value.refreshToken) {
throw new Error('无效的refreshToken')
}
const refreshToken = tokenInfo.value.refreshToken
const res = await _refreshToken(refreshToken)
console.log('刷新token-res: ', res)
setTokenInfo(res)
return res
}
catch (error) {
console.error('刷新token失败:', error)
throw error
}
}
/**
* 获取有效的token
* 注意在computed中不直接调用异步函数只做状态判断
* 实际的刷新操作应由调用方处理
*/
const getValidToken = computed(() => {
// token已过期返回空
if (isTokenExpired.value) {
return ''
}
if (!isDoubleTokenMode) {
return isSingleTokenRes(tokenInfo.value) ? tokenInfo.value.token : ''
}
else {
return isDoubleTokenRes(tokenInfo.value) ? tokenInfo.value.accessToken : ''
}
})
/**
* 检查是否有登录信息不考虑token是否过期
*/
const hasLoginInfo = computed(() => {
if (!tokenInfo.value) {
return false
}
if (isDoubleTokenMode) {
return isDoubleTokenRes(tokenInfo.value) && !!tokenInfo.value.accessToken
}
else {
return isSingleTokenRes(tokenInfo.value) && !!tokenInfo.value.token
}
})
/**
* 检查是否已登录且token有效
*/
const hasValidLogin = computed(() => {
console.log('hasValidLogin', hasLoginInfo.value, !isTokenExpired.value)
return hasLoginInfo.value && !isTokenExpired.value
})
/**
* 尝试获取有效的token如果过期且可刷新则刷新token
* @returns 有效的token或空字符串
*/
const tryGetValidToken = async (): Promise<string> => {
if (!getValidToken.value && isDoubleTokenMode && !isRefreshTokenExpired.value) {
try {
await refreshToken()
return getValidToken.value
}
catch (error) {
console.error('尝试刷新token失败:', error)
return ''
}
}
return getValidToken.value
}
return {
// 核心API方法
login,
wxLogin,
logout,
// 认证状态判断(最常用的)
hasLogin: hasValidLogin,
// 内部系统使用的方法
refreshToken,
tryGetValidToken,
validToken: getValidToken,
// 调试或特殊场景可能需要直接访问的信息
tokenInfo,
setTokenInfo,
}
},
{
// 添加持久化配置确保刷新页面后token信息不丢失
persist: true,
},
)