237 lines
7.6 KiB
TypeScript
237 lines
7.6 KiB
TypeScript
// 导入单独抽离的类型
|
|
import type { UserInfo, LoginForm, LoginResponse, ResetPasswordForm, ResetPasswordResponse, ChangePasswordForm, ChangePasswordResponse } from '@/type/user';
|
|
import { request } from '@/api/index'
|
|
import { useLocalStorage, useSessionStorage } from '@/utils/storage'
|
|
import router from '@/router';
|
|
/**
|
|
* 用户状态管理 Store
|
|
* 核心功能:登录、登出、状态持久化、初始化用户信息
|
|
*/
|
|
export const useUserStore = defineStore('user', () => {
|
|
// ========== 响应式状态 ==========
|
|
/** 用户基本信息 */
|
|
const userInfo = ref<UserInfo>({
|
|
id: '',
|
|
username: '',
|
|
nickname: '',
|
|
roles: [],
|
|
permissions: []
|
|
});
|
|
/** 用户登录令牌 */
|
|
const token = ref<string>('');
|
|
/** 是否加载中(登录/登出请求时) */
|
|
const loading = ref<boolean>(false);
|
|
// ========== 方法 ==========
|
|
/**
|
|
* 用户登录
|
|
* @param form 登录表单数据
|
|
* @returns Promise<void>
|
|
*/
|
|
const login = async (form: LoginForm): Promise<void> => {
|
|
try {
|
|
loading.value = true;
|
|
// 使用API服务进行登录请求
|
|
const response = await request.post<LoginResponse>('/login', form);
|
|
|
|
// 从响应中获取数据
|
|
const res = response.data;
|
|
ElNotification({
|
|
type: 'success',
|
|
message: '登录成功',
|
|
duration: 2000
|
|
})
|
|
// 更新响应式状态
|
|
userInfo.value = res.data.user;
|
|
token.value = res.data.user.token ?? '';
|
|
// 持久化到本地存储(防止页面刷新丢失)
|
|
useLocalStorage.set('user', res.data.user);
|
|
useLocalStorage.set('token', res.data.user.token);
|
|
} catch (error) {
|
|
// 统一错误处理
|
|
const errMsg = error instanceof Error ? error.message : '登录异常';
|
|
ElNotification({
|
|
type: 'error',
|
|
message: errMsg,
|
|
duration: 2000
|
|
})
|
|
throw new Error(errMsg); // 抛出错误让组件层捕获
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 用户登出
|
|
*/
|
|
const logout = async (): Promise<void> => {
|
|
try {
|
|
loading.value = true;
|
|
// 使用API服务进行登出请求
|
|
await request.post<LoginResponse>('/logout');
|
|
} catch (error) {
|
|
// 统一错误处理
|
|
const errMsg = error instanceof Error ? error.message : '登出异常';
|
|
ElNotification({
|
|
type: 'error',
|
|
message: errMsg,
|
|
duration: 2000
|
|
})
|
|
throw new Error(errMsg); // 抛出错误让组件层捕获
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
// 清空响应式状态
|
|
userInfo.value = {
|
|
id: '',
|
|
username: '',
|
|
nickname: '',
|
|
roles: [],
|
|
permissions: []
|
|
};
|
|
token.value = '';
|
|
|
|
// 清除本地存储
|
|
useLocalStorage.remove('user');
|
|
useLocalStorage.remove('token');
|
|
useSessionStorage.remove('backPath');
|
|
useLocalStorage.remove('menus');
|
|
// 可选:登出后跳转到登录页(如果需要)
|
|
// 使用 Vue Router 进行跳转
|
|
// 显示登出成功通知
|
|
ElNotification({
|
|
type: 'success',
|
|
message: '退出登录',
|
|
duration: 2000
|
|
});
|
|
// 跳转到登录页
|
|
router.push('/login');
|
|
};
|
|
|
|
/**
|
|
* 初始化用户信息(页面刷新后恢复)
|
|
*/
|
|
const initUser = (): void => {
|
|
try {
|
|
const savedUser = useLocalStorage.get('user');
|
|
const savedToken = useLocalStorage.get('token');
|
|
const savedMenus = useLocalStorage.get('menus');
|
|
if (savedUser.value && savedToken.value) {
|
|
// 类型断言 + 验证,确保数据格式正确
|
|
const parsedUser = savedUser.value as UserInfo;
|
|
// 基础校验(防止本地存储数据被篡改)
|
|
if (parsedUser.id && parsedUser.username) {
|
|
userInfo.value = parsedUser;
|
|
token.value = savedToken.value;
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('初始化用户信息失败:', error);
|
|
// 校验失败则清空本地存储
|
|
useLocalStorage.remove('user');
|
|
useLocalStorage.remove('token');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 检查用户是否拥有指定权限
|
|
* @param permission 权限标识
|
|
* @returns boolean
|
|
*/
|
|
const hasPermission = (permission: string): boolean => {
|
|
return userInfo.value.permissions.includes(permission);
|
|
};
|
|
|
|
/**
|
|
* 检查用户是否拥有指定角色
|
|
* @param role 角色标识
|
|
* @returns boolean
|
|
*/
|
|
const hasRole = (role: string): boolean => {
|
|
return userInfo.value.roles.includes(role);
|
|
};
|
|
/**
|
|
* 重置密码
|
|
* @param form 重置密码表单数据
|
|
* @returns Promise<void>
|
|
*/
|
|
const resetPassword = async (form: ResetPasswordForm): Promise<void> => {
|
|
try {
|
|
loading.value = true;
|
|
// 使用API服务进行重置密码请求
|
|
const response = await request.post<ResetPasswordResponse>('user/reset-password', form);
|
|
|
|
// 从响应中获取数据
|
|
const res = response.data;
|
|
ElNotification({
|
|
type: 'success',
|
|
message: '密码重置成功',
|
|
duration: 2000
|
|
});
|
|
|
|
// 重置成功后跳转到登录页
|
|
router.push('/login');
|
|
} catch (error) {
|
|
// 统一错误处理
|
|
const errMsg = error instanceof Error ? error.message : '密码重置失败';
|
|
ElNotification({
|
|
type: 'error',
|
|
message: errMsg,
|
|
duration: 2000
|
|
});
|
|
throw new Error(errMsg); // 抛出错误让组件层捕获
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 修改密码
|
|
* @param form 修改密码表单数据
|
|
* @returns Promise<void>
|
|
*/
|
|
const changePassword = async (form: ChangePasswordForm): Promise<void> => {
|
|
try {
|
|
loading.value = true;
|
|
// 使用API服务进行修改密码请求
|
|
const response = await request.post<ChangePasswordResponse>('user/change-password', form);
|
|
|
|
// 从响应中获取数据
|
|
const res = response.data;
|
|
ElNotification({
|
|
type: 'success',
|
|
message: '密码修改成功',
|
|
duration: 2000
|
|
});
|
|
|
|
// 修改成功后跳转到登录页
|
|
useLocalStorage.remove('token');
|
|
router.push('/login');
|
|
} catch (error) {
|
|
// 统一错误处理
|
|
const errMsg = error instanceof Error ? error.message : '密码修改失败';
|
|
ElNotification({
|
|
type: 'error',
|
|
message: errMsg,
|
|
duration: 2000
|
|
});
|
|
throw new Error(errMsg); // 抛出错误让组件层捕获
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
};
|
|
// ========== 暴露状态和方法 ==========
|
|
return {
|
|
// 状态和数据
|
|
userInfo,
|
|
token,
|
|
loading,
|
|
// 方法
|
|
login,
|
|
logout,
|
|
initUser,
|
|
hasPermission,
|
|
hasRole,
|
|
resetPassword,
|
|
changePassword
|
|
};
|
|
}); |