344 lines
10 KiB
TypeScript
344 lines
10 KiB
TypeScript
import './utils/amap_system';
|
||
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';
|
||
import {
|
||
getRemoteMenu,
|
||
getRoutersInfo,
|
||
getUserInfo,
|
||
patchRouteWithRemoteMenus,
|
||
setRemoteMenu,
|
||
} from './services/session';
|
||
import { PageEnum } from './enums/pagesEnums';
|
||
import { stringify } from 'querystring';
|
||
import { message } from 'antd';
|
||
import { encrypt, decrypt, needEncrypt } from '@/utils/encrypt';
|
||
|
||
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);
|
||
if (window.location.pathname !== '/qingdao/user/login' && !redirect) {
|
||
history.replace({
|
||
pathname: '/user/login',
|
||
search: stringify({
|
||
redirect: pathname.replace('/qingdao', '') + 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';
|
||
}
|
||
return {
|
||
...response.user,
|
||
permissions: response.permissions,
|
||
roles: response.roles,
|
||
} as API.CurrentUser;
|
||
} catch (error) {
|
||
console.log(error);
|
||
history.push(PageEnum.LOGIN);
|
||
}
|
||
return undefined;
|
||
};
|
||
// 如果不是登录页面,执行
|
||
const { location } = history;
|
||
if (location.pathname !== PageEnum.LOGIN) {
|
||
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 />,
|
||
render: (_, avatarChildren) => {
|
||
return <AvatarDropdown menu="True">{avatarChildren}</AvatarDropdown>;
|
||
},
|
||
},
|
||
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;
|
||
// 如果没有登录,重定向到 login
|
||
if (!initialState?.currentUser && location.pathname !== PageEnum.LOGIN) {
|
||
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,
|
||
};
|
||
};
|
||
|
||
export async function onRouteChange({ clientRoutes, location }) {
|
||
const menus = getRemoteMenu();
|
||
// console.log('onRouteChange', clientRoutes, location, menus);
|
||
if (menus === null && location.pathname !== PageEnum.LOGIN) {
|
||
console.log('refresh');
|
||
// history.go(0);
|
||
}
|
||
}
|
||
|
||
// export function patchRoutes({ routes, routeComponents }) {
|
||
// console.log('patchRoutes', routes, routeComponents);
|
||
// }
|
||
|
||
export async function patchClientRoutes({ routes }) {
|
||
// console.log('patchClientRoutes', routes);
|
||
patchRouteWithRemoteMenus(routes);
|
||
}
|
||
|
||
export async function render(oldRender: () => void) {
|
||
console.log('render get routers', oldRender);
|
||
const token = getAccessToken();
|
||
if (!token || token?.length === 0) {
|
||
oldRender();
|
||
return;
|
||
}
|
||
await getRoutersInfo().then((res) => {
|
||
console.log('render get routers', 123);
|
||
|
||
setRemoteMenu(res);
|
||
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' ? '' : 'https://qd.zhaopinzao8dian.com/api',
|
||
// baseURL: 'http://39.98.44.136:8080',
|
||
// baseURL:
|
||
// process.env.NODE_ENV === 'development'
|
||
// ? 'http://10.213.6.207:19010'
|
||
// : 'http://10.213.6.207:19010/api',
|
||
requestInterceptors: [
|
||
(url: any, options: { headers: any; data?: any; params?: any; method?: string }) => {
|
||
const headers = options.headers ? options.headers : {};
|
||
console.log('request ====>:', url);
|
||
const authHeader = headers['Authorization'];
|
||
const isToken = headers['isToken'];
|
||
|
||
// 处理开发环境API路径
|
||
if (process.env.NODE_ENV !== 'development') {
|
||
if (url.startsWith('/api')) {
|
||
url = url.replace(/^\/api/, '');
|
||
}
|
||
}
|
||
|
||
// 处理认证token
|
||
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();
|
||
}
|
||
}
|
||
|
||
// 处理SM4加密 - 根据config的isEncrypt来判断
|
||
if (needEncrypt(options)) {
|
||
console.log('进行SM4加密处理');
|
||
|
||
let requestData = options.data;
|
||
let requestParams = options.params;
|
||
|
||
// 加密请求数据
|
||
if (requestData && Object.keys(requestData).length > 0) {
|
||
const jsonData = JSON.stringify(requestData);
|
||
const encryptedBody = encrypt(jsonData);
|
||
requestData = {
|
||
encrypted: true,
|
||
encryptedData: encryptedBody,
|
||
timestamp: Date.now(),
|
||
};
|
||
}
|
||
|
||
// 加密查询参数
|
||
if (requestParams && Object.keys(requestParams).length > 0) {
|
||
const jsonParams = JSON.stringify(requestParams);
|
||
const encryptedParams = encrypt(jsonParams);
|
||
requestParams = {
|
||
encrypted: true,
|
||
encryptedData: encryptedParams,
|
||
timestamp: Date.now(),
|
||
};
|
||
}
|
||
|
||
// 添加加密标识头
|
||
headers['X-Encrypted'] = 'true';
|
||
|
||
return {
|
||
url,
|
||
options: {
|
||
...options,
|
||
headers,
|
||
data: requestData,
|
||
params: requestParams,
|
||
},
|
||
};
|
||
}
|
||
|
||
return { url, options: { ...options, headers } };
|
||
},
|
||
],
|
||
responseInterceptors: [
|
||
(response) => {
|
||
// 不再需要异步处理读取返回体内容,可直接在data中读出,部分字段可在 config 中找到
|
||
const { data = {} as any, config } = response;
|
||
|
||
// 检查是否需要解密
|
||
const isEncrypted = data.encrypted;
|
||
|
||
if (isEncrypted && data.encryptedData) {
|
||
console.log('进行SM4解密处理');
|
||
try {
|
||
// 解密响应数据
|
||
const decryptedData = decrypt(data.encryptedData);
|
||
// console.log(decryptedData)
|
||
response.data =
|
||
typeof decryptedData === 'string' ? JSON.parse(decryptedData) : decryptedData;
|
||
} catch (error) {
|
||
console.error('响应解密失败:', error);
|
||
// 如果解密失败,保持原始数据
|
||
}
|
||
}
|
||
|
||
// 处理业务状态码
|
||
switch (data.code) {
|
||
case 401:
|
||
loginOut();
|
||
break;
|
||
}
|
||
if (data.code !== 200 && data.msg) {
|
||
message.info(data.msg);
|
||
}
|
||
|
||
return response;
|
||
},
|
||
],
|
||
};
|