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; }; } /** * 请求顺序 * @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;