Files
shz-admin/src/app.tsx

329 lines
11 KiB
TypeScript
Raw Normal View History

2025-11-10 16:28:01 +08:00
import { AvatarDropdown, AvatarName, Footer, SelectLang } from '@/components';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RunTimeLayoutConfig } from '@umijs/max';
import { history } from '@umijs/max';
import defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import { clearSessionToken, getAccessToken, getRefreshToken, getTokenExpireTime } from './access';
2026-05-26 17:06:53 +08:00
import {
exchangeThirdPartyCredential,
isThirdPartyTransitionPage,
} from './utils/thirdPartyLogin';
2026-06-04 18:47:59 +08:00
import { saveGetInfoCache } from './utils/jobPortalAuth';
2025-11-10 16:28:01 +08:00
import {
getRemoteMenu,
getRoutersInfo,
getUserInfo,
patchRouteWithRemoteMenus,
setRemoteMenu,
} from './services/session';
import { PageEnum } from './enums/pagesEnums';
import { stringify } from 'querystring';
import { message } from 'antd';
const isDev = process.env.NODE_ENV === 'development';
const loginOut = async () => {
clearSessionToken();
setRemoteMenu(null);
const { search, pathname } = window.location;
const urlParams = new URL(window.location.href).searchParams;
/** 此方法会跳转到 redirect 参数所在的位置 */
const redirect = urlParams.get('redirect');
// Note: There may be security issues, please note
console.log('redirect', window.location.pathname, redirect);
2025-11-13 15:07:44 +08:00
if (window.location.pathname !== '/shihezi/user/login' && !redirect) {
2025-11-10 16:28:01 +08:00
history.replace({
pathname: '/user/login',
search: stringify({
2025-11-13 15:07:44 +08:00
redirect: pathname.replace('/shihezi', '') + search,
2025-11-10 16:28:01 +08:00
}),
});
}
};
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: API.CurrentUser;
loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const response = await getUserInfo({
skipErrorHandler: true,
});
if (response.user.avatar === '') {
response.user.avatar =
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png';
}
2026-06-04 18:47:59 +08:00
saveGetInfoCache(response as unknown as Record<string, unknown>);
2025-11-10 16:28:01 +08:00
return {
...response.user,
permissions: response.permissions,
roles: response.roles,
2026-05-29 15:23:27 +08:00
userType: response.roles?.[0],
2025-11-10 16:28:01 +08:00
} as API.CurrentUser;
} catch (error) {
console.log(error);
history.push(PageEnum.LOGIN);
}
return undefined;
};
// 如果不是登录页面,执行
const { location } = history;
2026-05-26 17:06:53 +08:00
// 排除登录页面、过渡页面和求职者页面(考虑基础路径 /shihezi/
const isTransitionPage = location.pathname === '/' ||
location.pathname === '/login-tow' ||
location.pathname === '/shihezi/' ||
location.pathname === '/shihezi/login-tow';
// 排除求职者相关页面
const isJobPortalPage = location.pathname.startsWith('/job-portal') ||
location.pathname.startsWith('/shihezi/job-portal');
if (location.pathname !== PageEnum.LOGIN && !isTransitionPage && !isJobPortalPage) {
2025-11-10 16:28:01 +08:00
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
return {
fetchUserInfo,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
return {
// actionsRender: () => [<Question key="doc" />, <SelectLang key="SelectLang" />],
actionsRender: () => [<SelectLang key="SelectLang" />],
avatarProps: {
src: initialState?.currentUser?.avatar,
title: <AvatarName />,
2026-05-26 17:06:53 +08:00
render: (_, avatarChildren) => {
return <AvatarDropdown menu>{avatarChildren}</AvatarDropdown>;
},
2025-11-10 16:28:01 +08:00
},
waterMarkProps: {
// content: initialState?.currentUser?.nickName,
},
// actionRef: layoutActionRef,
menu: {
locale: false,
// // 每当 initialState?.currentUser?.userid 发生修改时重新执行 request
params: {
userId: initialState?.currentUser?.userId,
},
request: async () => {
if (!initialState?.currentUser?.userId) {
return [];
}
return getRemoteMenu();
},
},
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
2026-05-26 17:06:53 +08:00
// 如果没有登录,重定向到 login但排除过渡页面和求职者页面考虑基础路径 /shihezi/
const isTransitionPage = location.pathname === '/' ||
location.pathname === '/login-tow' ||
location.pathname === '/shihezi/' ||
location.pathname === '/shihezi/login-tow';
// 排除求职者相关页面
const isJobPortalPage = location.pathname.startsWith('/job-portal') ||
location.pathname.startsWith('/shihezi/job-portal');
if (!initialState?.currentUser && location.pathname !== PageEnum.LOGIN && !isTransitionPage && !isJobPortalPage) {
2025-11-10 16:28:01 +08:00
history.push(PageEnum.LOGIN);
}
},
layoutBgImgList: [
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
bottom: 0,
left: 0,
width: '331px',
},
],
pure: false,
// links: isDev
// ? [
// <Link key="openapi" to="/umi/plugin/openapi" target="_blank">
// <LinkOutlined />
// <span>OpenAPI 文档</span>
// </Link>,
// ]
// : [],
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children) => {
// if (initialState?.loading) return <PageLoading />;
return (
<>
{children}
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
</>
);
},
...initialState?.settings,
};
};
2026-05-26 17:06:53 +08:00
export async function onRouteChange({ clientRoutes, location }: { clientRoutes: any; location: any }) {
2025-11-10 16:28:01 +08:00
const menus = getRemoteMenu();
// console.log('onRouteChange', clientRoutes, location, menus);
2026-05-26 17:06:53 +08:00
// 如果路由信息未加载,尝试重新获取路由信息
const token = getAccessToken();
if (menus === null && location.pathname !== PageEnum.LOGIN && token) {
console.log('检测到路由信息未加载,正在重新获取路由信息...');
// 重新获取路由信息,而不是刷新页面
try {
const routers = await getRoutersInfo();
setRemoteMenu(routers);
console.log('路由信息重新获取成功');
} catch (error) {
console.error('重新获取路由信息失败:', error);
}
2025-11-10 16:28:01 +08:00
}
}
// export function patchRoutes({ routes, routeComponents }) {
// console.log('patchRoutes', routes, routeComponents);
// }
2026-05-26 17:06:53 +08:00
export async function patchClientRoutes({ routes }: { routes: any }) {
2025-11-10 16:28:01 +08:00
// console.log('patchClientRoutes', routes);
patchRouteWithRemoteMenus(routes);
}
export async function render(oldRender: () => void) {
2026-05-26 17:06:53 +08:00
const { pathname, search } = window.location;
const params = new URLSearchParams(search);
const code = params.get('code');
const tokenParam = params.get('token');
// 过渡页带 code/token 时,先完成 SSO 换票再拉路由,避免旧 token 导致 getRouters 401
if (isThirdPartyTransitionPage(pathname) && (code || tokenParam)) {
clearSessionToken();
const ok = await exchangeThirdPartyCredential({ code, token: tokenParam });
if (!ok) {
oldRender();
return;
}
}
2025-11-10 16:28:01 +08:00
const token = getAccessToken();
2026-05-26 17:06:53 +08:00
if (!token || token.length === 0) {
2025-11-10 16:28:01 +08:00
oldRender();
return;
}
2026-05-26 17:06:53 +08:00
try {
const routers = await getRoutersInfo();
setRemoteMenu(routers);
} catch (error) {
console.error('获取路由失败:', error);
}
oldRender();
2025-11-10 16:28:01 +08:00
}
/**
* @name request
* axios ahooks useRequest
* @doc https://umijs.org/docs/max/request#配置
*/
const checkRegion = 5 * 60 * 1000;
export const request = {
...errorConfig,
2025-11-21 14:48:00 +08:00
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://ks.zhaopinzao8dian.com/api/ks',
2026-01-11 11:17:09 +08:00
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://36.105.163.21:30081/api/ks',
2026-04-27 09:13:30 +08:00
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://xjshzly.longbiosphere.com:30081/api/ks',
baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://39.98.44.136:6024/api/shihezi/',
2025-11-10 16:28:01 +08:00
// baseURL: 'http://39.98.44.136:8080',
requestInterceptors: [
(url: any, options: { headers: any }) => {
const headers = options.headers ? options.headers : [];
console.log('request ====>:', url);
const authHeader = headers['Authorization'];
const isToken = headers['isToken'];
if (!authHeader && isToken !== false) {
const expireTime = getTokenExpireTime();
if (expireTime) {
const left = Number(expireTime) - new Date().getTime();
const refreshToken = getRefreshToken();
if (left < checkRegion && refreshToken) {
if (left < 0) {
clearSessionToken();
}
} else {
const accessToken = getAccessToken();
if (accessToken) {
headers['Authorization'] = `Bearer ${accessToken}`;
}
}
} else {
clearSessionToken();
}
}
if (process.env.NODE_ENV !== 'development') {
if (url.startsWith('/api')) {
url = url.replace(/^\/api/, '');
}
}
return { url, options };
},
],
responseInterceptors: [
(response) => {
// 不再需要异步处理读取返回体内容可直接在data中读出部分字段可在 config 中找到
const { data = {} as any, config } = response;
switch (data.code) {
case 401:
loginOut();
break;
}
if (data.code !== 200 && data.msg) {
message.info(data.msg);
}
// console.log('data: ', data)
// console.log('config: ', config)
return response;
},
],
};
2025-11-21 14:48:00 +08:00