forked from tools/tape-springboot-sysadmin
172 lines
5.2 KiB
JavaScript
172 lines
5.2 KiB
JavaScript
import { ref } from "vue";
|
||
import { defineStore } from "pinia";
|
||
import { apiRequest } from "@/iview/utils/request";
|
||
import { toAny } from "@/iview/utils/toany";
|
||
import { on } from "@/iview/utils/emitter";
|
||
import config from "@/config/index";
|
||
|
||
const generateKey = (key) => {
|
||
return `${config.APP_CACHE_PROFIX}_${key}`;
|
||
};
|
||
|
||
const getStorageValue = (key) => {
|
||
return Promise.resolve().then(() => {
|
||
key = generateKey(key);
|
||
const value = localStorage.getItem(key);
|
||
return toAny(value, {}).value;
|
||
});
|
||
};
|
||
|
||
const setStorageValue = (key, value) => {
|
||
return Promise.resolve().then(() => {
|
||
key = generateKey(key);
|
||
localStorage.setItem(
|
||
key,
|
||
toAny({ key, value }, "")
|
||
);
|
||
});
|
||
};
|
||
|
||
const removeStorageValue = (key) => {
|
||
return Promise.resolve().then(() => {
|
||
key = generateKey(key);
|
||
localStorage.removeItem(key);
|
||
});
|
||
};
|
||
|
||
const API_LOGIN = "api.system.admin.info.auth.login";
|
||
const API_PROFILE = "api.system.admin.info.auth.profile";
|
||
const API_REFRESH_TOKEN = `refreshToken`;
|
||
const KEY_SIDEBAR_COLLAPSE = "state_collapseSidebar";
|
||
const KEY_LAST_USERNAME = "state_lastUsername";
|
||
const KEY_REFRESH_TOKEN = "state_refreshToken";
|
||
const KEY_USER_TOKEN = "state_userToken";
|
||
const KEY_USER_INFO = "state_userInfo";
|
||
|
||
/** 防止 103 / 刷新失败 连续触发时重复跳转 */
|
||
let sessionExpiryHandling = false;
|
||
|
||
export const useUserStore = defineStore("user", {
|
||
state: () => ({
|
||
initialized: ref(false),
|
||
collapseSidebar: ref(false),
|
||
lastUsername: ref(null),
|
||
refreshToken: ref(null),
|
||
userToken: ref(null),
|
||
userInfo: ref(null),
|
||
}),
|
||
|
||
getters: {
|
||
isLoggedIn: (state) => !!state.userToken,
|
||
isInitialized: (state) => state.initialized,
|
||
isCollapseSidebar: (state) => state.collapseSidebar,
|
||
isSuper: (state) => state.userInfo?.optSuper === 1,
|
||
username: (state) => state.userInfo?.username,
|
||
nickname: (state) => state.userInfo?.nickname,
|
||
},
|
||
|
||
actions: {
|
||
async init() {
|
||
const collapseSidebar = await getStorageValue(KEY_SIDEBAR_COLLAPSE);
|
||
const lastUsername = await getStorageValue(KEY_LAST_USERNAME);
|
||
const refreshToken = await getStorageValue(KEY_REFRESH_TOKEN);
|
||
const userToken = await getStorageValue(KEY_USER_TOKEN);
|
||
const userInfo = await getStorageValue(KEY_USER_INFO);
|
||
this.initialized = true;
|
||
this.collapseSidebar = collapseSidebar === "true";
|
||
this.lastUsername = lastUsername;
|
||
this.refreshToken = refreshToken;
|
||
this.userToken = userToken;
|
||
this.userInfo = userInfo;
|
||
},
|
||
// 登录
|
||
async login(username, password) {
|
||
const data = await apiRequest(API_LOGIN, {
|
||
username,
|
||
password,
|
||
});
|
||
this.userToken = data.token;
|
||
this.refreshToken = data.refreshToken;
|
||
this.lastUsername = username;
|
||
await setStorageValue(KEY_LAST_USERNAME, username);
|
||
await setStorageValue(KEY_REFRESH_TOKEN, data.refreshToken);
|
||
await setStorageValue(KEY_USER_TOKEN, data.token);
|
||
await this.getUserProfile();
|
||
return { success: true };
|
||
},
|
||
|
||
|
||
// 刷新Token
|
||
async refreshUserToken() {
|
||
if (!this.refreshToken) {
|
||
throw new Error("No refresh token available");
|
||
}
|
||
try {
|
||
const data = await apiRequest(API_REFRESH_TOKEN, {
|
||
refreshToken: this.refreshToken,
|
||
});
|
||
this.userToken = data.token;
|
||
this.refreshToken = data.refreshToken;
|
||
await setStorageValue(KEY_USER_TOKEN, data.token);
|
||
await setStorageValue(KEY_REFRESH_TOKEN, data.refreshToken);
|
||
return {
|
||
success: true,
|
||
token: data.token,
|
||
refreshToken: data.refreshToken
|
||
};
|
||
} catch (error) {
|
||
this.refreshToken = null;
|
||
await removeStorageValue(KEY_REFRESH_TOKEN);
|
||
throw error;
|
||
}
|
||
},
|
||
|
||
// 获取用户信息
|
||
async getUserProfile() {
|
||
const userInfo = await apiRequest(API_PROFILE, {});
|
||
this.userInfo = userInfo;
|
||
await setStorageValue(KEY_USER_INFO, userInfo);
|
||
return { success: true, data: userInfo };
|
||
},
|
||
|
||
// 退出登录
|
||
async logout() {
|
||
this.userToken = "";
|
||
this.refreshToken = "";
|
||
this.userInfo = {};
|
||
await removeStorageValue(KEY_USER_INFO);
|
||
await removeStorageValue(KEY_USER_TOKEN);
|
||
await removeStorageValue(KEY_REFRESH_TOKEN);
|
||
},
|
||
|
||
async saveCollapseSidebar(collapse) {
|
||
this.collapseSidebar = !!collapse;
|
||
await setStorageValue(KEY_SIDEBAR_COLLAPSE, this.collapseSidebar ? "true" : "false");
|
||
},
|
||
|
||
/** 登录失效:清状态并跳到登录(用动态 import router,避免与 request 循环依赖且保证能拿到路由器实例) */
|
||
async handleSessionExpired() {
|
||
if (sessionExpiryHandling) return;
|
||
sessionExpiryHandling = true;
|
||
try {
|
||
await this.logout();
|
||
const { default: router } = await import("@/config/router");
|
||
const current = router.currentRoute.value;
|
||
if (current.path === "/login") return;
|
||
await router.replace({
|
||
path: "/login",
|
||
query: { redirect: current.fullPath },
|
||
});
|
||
} finally {
|
||
sessionExpiryHandling = false;
|
||
}
|
||
},
|
||
|
||
initTokenExpirationListener() {
|
||
on("auth:token-expired", () => {
|
||
void this.handleSessionExpired();
|
||
});
|
||
},
|
||
},
|
||
});
|