Files
WAI_Project_UNIX/cool/store/user.ts
2026-01-21 01:37:34 +08:00

192 lines
3.9 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 { computed, ref } from "vue";
import { forInObject, isNull, isObject, parse, storage } from "../utils";
import { router } from "../router";
import { request } from "../service";
import type { UserInfo } from "@/types";
export type Token = {
token: string; // 访问token
expire: number; // token过期时间
refreshToken: string; // 刷新token
refreshExpire: number; // 刷新token过期时间
};
export class User {
/**
* 用户信息,响应式对象
*/
info = ref<UserInfo | null>(null);
/**
* 当前token字符串或null
*/
token: string | null = null;
constructor() {
// 获取本地用户信息
const userInfo = storage.get("userInfo");
// 获取本地token
const token = storage.get("token") as string | null;
// 如果token为空字符串则置为null
this.token = token == "" ? null : token;
// 初始化用户信息
if (userInfo != null && isObject(userInfo)) {
this.set(userInfo);
}
}
/**
* 获取用户信息(从服务端拉取最新信息并更新本地)
* @returns Promise<void>
*/
async get() {
if (this.token != null) {
// 如果是模拟token则跳过远程获取防止由于后端校验失败导致的自动退出登录
if (this.token.startsWith("fake-")) {
return;
}
await request({
url: "/api/nongchuang/user/info"
})
.then((res) => {
if (res != null) {
this.set(res.userInfo || res); // Adapt to the map structure { userInfo: ..., stats: ... } or flat
}
})
.catch(() => {
this.logout();
});
}
}
/**
* 设置用户信息并存储到本地
* @param data 用户信息对象
*/
set(data: any) {
if (isNull(data)) {
return;
}
// 设置
this.info.value = parse<UserInfo>(data)!;
// 持久化到本地存储
storage.set("userInfo", data, 0);
}
/**
* 更新用户信息(本地与服务端同步)
* @param data 新的用户信息
*/
async update(data: any) {
if (isNull(data) || isNull(this.info.value)) {
return;
}
// 本地同步更新
forInObject(data, (value, key) => {
this.info.value![key] = value;
});
// 同步到服务端
await request({
url: "/api/nongchuang/user/info",
method: "PUT",
data
});
}
/**
* 移除用户信息
*/
remove() {
this.info.value = null;
storage.remove("userInfo");
}
/**
* 判断用户信息是否为空
* @returns boolean
*/
isNull() {
return this.info.value == null;
}
/**
* 清除本地所有用户信息和token
*/
clear() {
storage.remove("userInfo");
storage.remove("token");
storage.remove("refreshToken");
this.token = null;
this.remove();
}
/**
* 退出登录,清除所有信息并跳转到登录页
*/
logout() {
this.clear();
// 移除自动跳转,仅清除已过期的登录态,由页面自行决定是否引导登录
// router.login();
}
/**
* 设置token并存储到本地
* @param data Token对象
*/
setToken(data: Token) {
this.token = data.token;
// 访问token提前5秒过期防止边界问题
storage.set("token", data.token, data.expire - 5);
// 刷新token提前5秒过期
storage.set("refreshToken", data.refreshToken, data.refreshExpire - 5);
}
/**
* 刷新token调用服务端接口自动更新本地token
* @returns Promise<string> 新的token
*/
refreshToken(): Promise<string> {
return new Promise((resolve, reject) => {
request({
url: "/api/nongchuang/auth/refresh-token",
method: "POST",
params: {
refreshToken: storage.get("refreshToken")
}
})
.then((res) => {
if (res != null) {
const token = parse<Token>(res);
if (token != null) {
this.setToken(token);
resolve(token.token);
}
}
})
.catch((err) => {
reject(err);
});
});
}
}
/**
* 单例用户对象,项目全局唯一
*/
export const user = new User();
/**
* 用户信息,响应式对象
*/
export const userInfo = computed(() => user.info.value);