/** * 登录状态管理工具 */ import router from '@/router'; const TOKEN_KEY = 'token'; const USER_INFO_KEY = 'userInfo'; const TOKEN_EXPIRE_TIME_KEY = 'tokenExpireTime'; const normalizeAvatarUrl = (avatar: any): any => { if (typeof avatar !== 'string') return avatar; const url = avatar.trim(); if (!url) return url; if (!url.startsWith('http://')) return url; const host = url.slice('http://'.length).split('/')[0] || ''; const isLocalhost = host.startsWith('localhost') || host.startsWith('127.0.0.1'); const isPrivateIp = host.startsWith('10.') || host.startsWith('192.168.') || /^172\.(1[6-9]|2\d|3[0-1])\./.test(host); if (isLocalhost || isPrivateIp) return url; return 'https://' + url.slice('http://'.length); }; const normalizeUserInfo = (userInfo: any): any => { if (!userInfo || typeof userInfo !== 'object') return userInfo; const next = { ...userInfo }; if ('avatar' in next) { next.avatar = normalizeAvatarUrl((next as any).avatar); } return next; }; /** * 获取 token */ export const getToken = (): string | null => { try { return localStorage.getItem(TOKEN_KEY) || null; } catch (e) { console.error('获取 token 失败:', e); return null; } }; /** * 设置 token */ export const setToken = (token: string, expireTime?: number): void => { try { localStorage.setItem(TOKEN_KEY, token); if (expireTime) { localStorage.setItem(TOKEN_EXPIRE_TIME_KEY, String(expireTime)); } } catch (e) { console.error('设置 token 失败:', e); } }; /** * 清除 token */ export const clearToken = (): void => { try { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(TOKEN_EXPIRE_TIME_KEY); localStorage.removeItem(USER_INFO_KEY); } catch (e) { console.error('清除 token 失败:', e); } }; /** * 检查 token 是否存在 */ export const hasToken = (): boolean => { return !!getToken(); }; /** * 检查 token 是否过期 * @param expireTime token 过期时间戳(毫秒),如果不传则从存储中读取 */ export const isTokenExpired = (expireTime?: number): boolean => { try { const expireStr = expireTime ? String(expireTime) : localStorage.getItem(TOKEN_EXPIRE_TIME_KEY); if (!expireStr) { // 如果没有过期时间,认为 token 有效(由后端验证) return false; } const expire = Number(expireStr); return Date.now() >= expire; } catch (e) { console.error('检查 token 过期失败:', e); return false; } }; /** * 检查是否已登录(token 存在且未过期) */ export const isLoggedIn = (): boolean => { return hasToken() && !isTokenExpired(); }; /** * 获取用户信息 */ export const getUserInfo = (): any | null => { try { const v = localStorage.getItem(USER_INFO_KEY); if (!v) return null; return normalizeUserInfo(JSON.parse(v)); } catch (e) { console.error('获取用户信息失败:', e); return null; } }; /** * 设置用户信息 */ export const setUserInfo = (userInfo: any): void => { try { localStorage.setItem(USER_INFO_KEY, JSON.stringify(normalizeUserInfo(userInfo))); } catch (e) { console.error('设置用户信息失败:', e); } }; /** * 跳转到登录页 * @param force 为 true 时即使当前已在 /login 也 replace 一次(用于退出登录后刷新登录态) */ export const navigateToLogin = (options?: { redirect?: string; force?: boolean }): void => { try { const currentRoute = router.currentRoute.value.path; if (options?.force || currentRoute !== '/login') { const redirect = options?.redirect ?? (currentRoute !== '/login' ? currentRoute : '/'); router.replace({ path: '/login', query: { redirect: encodeURIComponent(redirect) } }); } } catch (e) { console.error('跳转登录页失败:', e); } }; /** * 退出登录:仅前端清空 token / 用户信息并进入登录页(不请求后端) */ export const logout = (): void => { clearToken(); navigateToLogin({ force: true, redirect: '/' }); }; /** * 需要登录的页面路径列表(白名单外的页面都需要登录) */ const LOGIN_WHITELIST = ['/login']; /** * 检查页面是否需要登录 */ export const isPageRequireLogin = (path: string): boolean => { return !LOGIN_WHITELIST.some((whitelistPath) => path.includes(whitelistPath)); };