Files
shz-admin/src/app.tsx
francis-fh 86e70a38d0
Some checks failed
Node CI / build (14.x, macOS-latest) (push) Has been cancelled
Node CI / build (14.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (14.x, windows-latest) (push) Has been cancelled
Node CI / build (16.x, macOS-latest) (push) Has been cancelled
Node CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node CI / build (16.x, windows-latest) (push) Has been cancelled
coverage CI / build (push) Has been cancelled
Node pnpm CI / build (16.x, macOS-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, ubuntu-latest) (push) Has been cancelled
Node pnpm CI / build (16.x, windows-latest) (push) Has been cancelled
消息角标开发
2026-07-20 20:50:35 +08:00

337 lines
11 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 { AvatarDropdown, AvatarName, Footer, SelectLang } from '@/components';
import EnterpriseHeader from '@/components/EnterpriseHeader';
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';
import {
exchangeThirdPartyCredential,
isThirdPartyTransitionPage,
} from './utils/thirdPartyLogin';
import { shouldSkipManagementBootstrap } from './utils/routeContext';
import { saveGetInfoCache } from './utils/jobPortalAuth';
import {
getRemoteMenu,
getRoutersInfo,
getUserInfo,
patchRouteWithRemoteMenus,
setRemoteMenu,
} from './services/session';
import {
fetchAndUpdateBadge,
patchMenuBadge,
clearBadgeCount,
} from './services/Management/interviewBadge';
import { PageEnum } from './enums/pagesEnums';
import { stringify } from 'querystring';
import { message } from 'antd';
import React from 'react';
import { getProductionApiBaseUrl } from './utils/publicUrl';
const isDev = process.env.NODE_ENV === 'development';
const loginOut = async () => {
clearSessionToken();
setRemoteMenu(null);
clearBadgeCount();
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);
if (window.location.pathname !== '/shihezi/user/login' && !redirect) {
history.replace({
pathname: '/user/login',
search: stringify({
redirect: pathname.replace('/shihezi', '') + search,
}),
});
}
};
/**
* @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';
}
saveGetInfoCache(response as unknown as Record<string, unknown>);
return {
...response.user,
permissions: response.permissions,
roles: response.roles,
userType: response.roles?.[0],
} as API.CurrentUser;
} catch (error) {
console.log(error);
history.push(PageEnum.LOGIN);
}
return undefined;
};
const { location } = history;
if (!shouldSkipManagementBootstrap(location.pathname)) {
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 {
// 嵌入第三方平台:隐藏 ProLayout 顶栏(系统名、用户头像等)
headerRender: false,
menuHeaderRender: false,
// actionsRender: () => [<Question key="doc" />, <SelectLang key="SelectLang" />],
actionsRender: () => [<SelectLang key="SelectLang" />],
avatarProps: {
src: initialState?.currentUser?.avatar,
title: <AvatarName />,
render: (_, avatarChildren) => {
return <AvatarDropdown menu>{avatarChildren}</AvatarDropdown>;
},
},
waterMarkProps: {
// content: initialState?.currentUser?.nickName,
},
// actionRef: layoutActionRef,
postMenuData: (menuData: any[]) => patchMenuBadge(menuData),
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;
// 如果没有登录,重定向到 login但排除过渡页面和求职者页面考虑基础路径 /shihezi/
if (
!initialState?.currentUser &&
!shouldSkipManagementBootstrap(location.pathname)
) {
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>,
// ]
// : [],
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children) => {
// if (initialState?.loading) return <PageLoading />;
// roles?.[0] 为 'view' 时是企业账号,显示返回首页顶栏
const isEnterprise = initialState?.currentUser?.userType === 'view';
return (
<>
{isEnterprise && <EnterpriseHeader />}
{children}
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
</>
);
},
...initialState?.settings,
};
};
export async function onRouteChange({ clientRoutes, location }: { clientRoutes: any; location: any }) {
const menus = getRemoteMenu();
const token = getAccessToken();
if (
menus === null &&
token &&
!shouldSkipManagementBootstrap(location.pathname)
) {
console.log('检测到路由信息未加载,正在重新获取路由信息...');
// 重新获取路由信息,而不是刷新页面
try {
const routers = await getRoutersInfo();
setRemoteMenu(routers);
console.log('路由信息重新获取成功');
} catch (error) {
console.error('重新获取路由信息失败:', error);
}
}
}
// export function patchRoutes({ routes, routeComponents }) {
// console.log('patchRoutes', routes, routeComponents);
// }
export async function patchClientRoutes({ routes }: { routes: any }) {
// console.log('patchClientRoutes', routes);
patchRouteWithRemoteMenus(routes);
}
export async function render(oldRender: () => void) {
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;
}
}
if (shouldSkipManagementBootstrap(pathname)) {
oldRender();
return;
}
const token = getAccessToken();
if (!token || token.length === 0) {
oldRender();
return;
}
try {
const routers = await getRoutersInfo();
setRemoteMenu(routers);
await fetchAndUpdateBadge();
} catch (error) {
console.error('获取路由失败:', error);
}
oldRender();
}
/**
* @name request 配置,可以配置错误处理
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
* @doc https://umijs.org/docs/max/request#配置
*/
const checkRegion = 5 * 60 * 1000;
export const request = {
...errorConfig,
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://ks.zhaopinzao8dian.com/api/ks',
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'http://36.105.163.21:30081/api/ks',
// baseURL: process.env.NODE_ENV === 'development' ? '' : 'https://xjshzly.longbiosphere.com:30081/api/ks',
baseURL: process.env.NODE_ENV === 'development' ? '' : getProductionApiBaseUrl(),
// 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;
},
],
};