upload project source code

This commit is contained in:
2026-04-30 18:49:43 +08:00
commit 9b394ba682
2277 changed files with 660945 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
/**
* 登录状态管理工具
*/
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));
};