353 lines
10 KiB
TypeScript
353 lines
10 KiB
TypeScript
import { history } from 'umi';
|
||
import {
|
||
userLogin, retrieveUserInfo, retrieveUserInfoAuthorityMenu, userLogout,
|
||
retrieveUserAuthorityMenu,
|
||
} from '@/services/user';
|
||
import type { Effect, Reducer, ConnectProps } from 'umi';
|
||
import handleRedirect from '@/utils/handleRedirect';
|
||
import handleGetRootSubmenuKeys from '@/utils/handleGetRootSubmenuKeys';
|
||
import handleGetEachDatumFromNestedDataByKey from '@/utils/handleGetEachDatumFromNestedDataByKey';
|
||
import handleGetIndexValidMenuItemByPath from '@/utils/handleGetIndexValidMenuItemByPath';
|
||
import { notification } from 'antd';
|
||
|
||
/**
|
||
* 全局用户数据
|
||
* @description isLogin 是否登录过
|
||
* @description data 用户信息
|
||
* @description menu 菜单数据
|
||
* @description authority 权限数组
|
||
* @description loginBtnLoading 按钮loading状态
|
||
* @description rootSubmenuKeys 子菜单的父级菜单key
|
||
* @description layoutWrapperLoading 布局外层容器loading状态
|
||
* @description indexAllMenuItemById 通过id索引索引菜单项的映射
|
||
* @description indexAllMenuItemByPath 通过path索引所有菜单项的映射
|
||
* @description indexValidMenuItemByPath 通过path索引有效菜单项的映射
|
||
*/
|
||
export type UserModelState = {
|
||
isLogin: boolean;
|
||
data: API.UserInfo;
|
||
menu: API.MenuData;
|
||
authority: string[];
|
||
loginBtnLoading: boolean;
|
||
rootSubmenuKeys: React.Key[];
|
||
layoutWrapperLoading: boolean;
|
||
indexAllMenuItemById: IndexAllMenuItemByKey<'id'>;
|
||
indexValidMenuItemByPath: IndexValidMenuItemByPath;
|
||
indexAllMenuItemByPath: IndexAllMenuItemByKey<'path'>;
|
||
}
|
||
|
||
export type UserConnectedProps = {
|
||
user: UserModelState;
|
||
} & ConnectProps;
|
||
|
||
type UserModelType = {
|
||
namespace: 'user';
|
||
state: UserModelState;
|
||
effects: {
|
||
login: Effect;
|
||
logout: Effect;
|
||
resetLoginStatus: Effect;
|
||
getUserInfoAuthorityMenu: Effect;
|
||
};
|
||
reducers: {
|
||
save: Reducer<UserModelState>;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 请求顺序
|
||
* @description 并发 | 继发
|
||
*/
|
||
type ReqOrder = 'concurrent' | 'relay';
|
||
|
||
const UserModel: UserModelType = {
|
||
namespace: 'user',
|
||
state: {
|
||
data: {},
|
||
authority: [],
|
||
isLogin: false,
|
||
rootSubmenuKeys: [],
|
||
loginBtnLoading: false,
|
||
layoutWrapperLoading: true,
|
||
menu: [
|
||
|
||
],
|
||
indexAllMenuItemById: {
|
||
|
||
},
|
||
indexAllMenuItemByPath: {
|
||
|
||
},
|
||
indexValidMenuItemByPath: {
|
||
|
||
},
|
||
},
|
||
effects: {
|
||
//登录
|
||
*login({ payload }, { call, put, select }) {
|
||
// 检查当前是否已经在登录状态
|
||
const { isLogin } = yield select((state) => state.user);
|
||
if (isLogin) {
|
||
return { code: 1, message: '您已经登录' };
|
||
}
|
||
|
||
// 检查是否有payload,防止自动触发
|
||
if (!payload) {
|
||
return { code: 1, message: '登录参数无效' };
|
||
}
|
||
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
loginBtnLoading: true,
|
||
},
|
||
});
|
||
|
||
try {
|
||
const res: API.LoginResponse = yield call(userLogin, payload);
|
||
console.log('res', res);
|
||
|
||
if (res.code === 200) {
|
||
localStorage.setItem('Authorization', res.data.accessToken);
|
||
|
||
yield put({
|
||
type: 'getUserInfoAuthorityMenu',
|
||
payload: {
|
||
type: 'concurrent',
|
||
},
|
||
});
|
||
return res;
|
||
} else {
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
loginBtnLoading: false,
|
||
},
|
||
});
|
||
return { code: res.code, message: res.message };
|
||
}
|
||
} catch (error) {
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
loginBtnLoading: false,
|
||
},
|
||
});
|
||
return { code: 1, message: error?.message || '登录失败,请稍后重试' };
|
||
}
|
||
},
|
||
//获取用户信息和权限以及菜单
|
||
*getUserInfoAuthorityMenu({ payload }, { call, put }) {
|
||
const { type }: { type: ReqOrder } = payload;
|
||
|
||
// 如果当前在登录页面,且不是通过登录操作触发,则不执行后续操作
|
||
if (window.location.pathname === '/user/login' && type !== 'concurrent') {
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
layoutWrapperLoading: false,
|
||
isLogin: false // 确保在登录页面时重置登录状态
|
||
},
|
||
});
|
||
return false;
|
||
}
|
||
|
||
let userInfoRes: API.UserInfoResponse = {
|
||
data: {},
|
||
code: 0,
|
||
message: '',
|
||
};
|
||
|
||
let userAuthorityRes: API.UserAuthorityResponse = {
|
||
data: {
|
||
authority: [],
|
||
},
|
||
code: 0,
|
||
message: '',
|
||
};
|
||
|
||
let menuRes: API.MenuDataResponse = {
|
||
data: [],
|
||
code: 0,
|
||
message: '',
|
||
};
|
||
console.log('type', type);
|
||
|
||
//用户在登录页登录完成之后执行
|
||
if (type === 'concurrent') {
|
||
const res: API.UserInfoAuthMenuResponse = yield call(retrieveUserInfoAuthorityMenu);
|
||
console.log('res', res);
|
||
userInfoRes = res[0] as API.UserInfoResponse;
|
||
// userAuthorityRes = res[1] as API.UserAuthorityResponse;
|
||
menuRes = res[1] as API.MenuDataResponse;
|
||
} else {
|
||
//其他情形首先查询用户的登录状态, 未登录则不继续操作
|
||
try {
|
||
userInfoRes = yield call(retrieveUserInfo);
|
||
} catch (error) {
|
||
//接口报错了, 比如返回了401
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
layoutWrapperLoading: false,
|
||
},
|
||
});
|
||
|
||
// 只有在非登录页面时才执行重定向
|
||
if (window.location.pathname !== '/user/login') {
|
||
yield put({
|
||
type: 'resetLoginStatus',
|
||
});
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
const res: API.UserAuthMenuResponse = yield call(retrieveUserAuthorityMenu);
|
||
// userAuthorityRes = res[0] as API.UserAuthorityResponse;
|
||
menuRes = res[0] as API.MenuDataResponse;
|
||
}
|
||
|
||
console.log('userInfoRes', userInfoRes);
|
||
|
||
|
||
menuRes.data = [
|
||
{
|
||
path: '/standard/index',
|
||
redirect: '',
|
||
name: '生成标准页面',
|
||
component: "@/pages/standard/index",
|
||
},
|
||
{
|
||
path: '/examine',
|
||
redirect: '',
|
||
name: '走动式管理',
|
||
children: [
|
||
{
|
||
path: '/examine/index',
|
||
name: '考评分类管理',
|
||
component: "@/pages/examine/index",
|
||
},
|
||
{
|
||
path: '/examine/question',
|
||
name: '考核问题管理',
|
||
component: "@/pages/examine/question",
|
||
},
|
||
{
|
||
path: '/examine/modal',
|
||
name: '考核模版管理',
|
||
component: "@/pages/examine/modal",
|
||
},
|
||
{
|
||
path: '/examine/record',
|
||
name: '考核记录管理',
|
||
component: "@/pages/examine/record",
|
||
}
|
||
]
|
||
},
|
||
{
|
||
path: "/setting",
|
||
redirect: '',
|
||
name: '系统设置',
|
||
children: [
|
||
{
|
||
path: '/setting/menu',
|
||
name: '菜单管理',
|
||
component: "@/pages/setting/menu/index",
|
||
},
|
||
]
|
||
}
|
||
]
|
||
let indexAllMenuItemByPath: any = []
|
||
let indexValidMenuItemByPath: any = []
|
||
if (menuRes.data && menuRes.data.length > 0) {
|
||
indexAllMenuItemByPath = handleGetEachDatumFromNestedDataByKey(menuRes.data, 'path');
|
||
indexValidMenuItemByPath = handleGetIndexValidMenuItemByPath(menuRes.data);
|
||
}
|
||
|
||
//在登录完获取菜单数据之后做是否需要重定向的操作
|
||
yield call(
|
||
handleRedirect,
|
||
window.location.pathname === '/cloudNew/user/login',
|
||
indexAllMenuItemByPath,
|
||
indexValidMenuItemByPath,
|
||
);
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
isLogin: true,
|
||
menu: menuRes.data,
|
||
data: userInfoRes.data,
|
||
loginBtnLoading: false,
|
||
indexAllMenuItemByPath,
|
||
indexValidMenuItemByPath,
|
||
layoutWrapperLoading: false,
|
||
// authority: userAuthorityRes.data.authority,
|
||
authority: [
|
||
'/standard/index',
|
||
'/examine/index',
|
||
'/examine/modal',
|
||
'/examine/question',
|
||
'/examine/record',
|
||
'/setting/menu'
|
||
],
|
||
rootSubmenuKeys: handleGetRootSubmenuKeys(menuRes.data),
|
||
indexAllMenuItemById: handleGetEachDatumFromNestedDataByKey(menuRes.data, 'id'),
|
||
},
|
||
});
|
||
//为保证所有语句都return, 因此这里加一句这个
|
||
return true;
|
||
},
|
||
//登出
|
||
* logout({ payload }, { call, put }) {
|
||
const res: API.LogoutResponse = yield call(userLogout, payload);
|
||
console.log('res', res)
|
||
if (res.code === 200) {
|
||
yield put({
|
||
type: 'resetLoginStatus',
|
||
});
|
||
}
|
||
},
|
||
//重置登录状态
|
||
* resetLoginStatus(_, { put }) {
|
||
localStorage.removeItem('Authorization');
|
||
|
||
yield put({
|
||
type: 'save',
|
||
payload: {
|
||
isLogin: false,
|
||
loginBtnLoading: false,
|
||
},
|
||
});
|
||
|
||
//当前页面不是登录页时,才进行重定向
|
||
if (window.location.pathname !== '/user/login') {
|
||
// 只获取路径部分,不包含查询参数,避免redirect参数累积
|
||
let redirectValue = window.location.pathname;
|
||
|
||
// 检查是否有base路径重复问题
|
||
if (redirectValue.startsWith('/') && redirectValue.indexOf('/', 1) !== -1) {
|
||
const firstSlashAfterRoot = redirectValue.indexOf('/', 1);
|
||
const possiblePrefix = redirectValue.substring(0, firstSlashAfterRoot);
|
||
// 检查是否有重复的路径前缀
|
||
if (redirectValue.indexOf(possiblePrefix, firstSlashAfterRoot) === firstSlashAfterRoot) {
|
||
redirectValue = redirectValue.substring(firstSlashAfterRoot);
|
||
}
|
||
}
|
||
|
||
history.push(`/user/login?redirect=${encodeURIComponent(redirectValue)}`);
|
||
}
|
||
},
|
||
},
|
||
reducers: {
|
||
save(state, action) {
|
||
return {
|
||
...state,
|
||
...action.payload,
|
||
};
|
||
},
|
||
},
|
||
};
|
||
|
||
export default UserModel;
|