Files
tashow-manager/src/app.tsx
2026-02-27 10:34:49 +08:00

270 lines
7.7 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 type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
import { history } from '@umijs/max';
import {
AvatarDropdown,
AvatarName,
Footer,
Question,
SelectLang,
} from '@/components';
import { getInfo } from '@/services/login';
import type { UserInfoVO } from '@/services/login/types';
import defaultSettings from '../config/defaultSettings';
import { errorConfig } from './requestErrorConfig';
import '@ant-design/v5-patch-for-react-19';
import { useDictStore } from '@/hooks/stores/dict';
import { getAccessToken, getTenantId } from '@/utils/auth';
import { CACHE_KEY, useCache } from './hooks/web/useCache';
import type { MenuVO } from './services/system/menu';
import { loopMenuItem } from './utils/menuUtils';
const isDev = process.env.NODE_ENV === 'development';
const isDevOrTest = isDev || process.env.CI;
const loginPath = '/user/login';
// 标记是否已添加动态路由
/**
* @see https://umijs.org/docs/api/runtime-config#getinitialstate
* */
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: UserInfoVO;
loading?: boolean;
menus?: MenuVO[];
fetchUserInfo?: () => Promise<UserInfoVO | undefined>;
}> {
const { wsCache } = useCache();
const dictStore = useDictStore();
const fetchUserInfo = async () => {
// history.push(loginPath);
try {
const token = getAccessToken();
if (!token) {
throw new Error('No token found');
}
const data = await getInfo();
console.log(data, 'data');
wsCache.set(CACHE_KEY.USER, data);
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
if (!dictStore.getIsSetDict) {
await dictStore.setDictMap();
}
// 转换菜单格式
return data;
} catch (_error) {
history.push(loginPath);
}
return undefined;
};
// 如果不是登录页面,执行
const { location } = history;
if (
![loginPath, '/user/register', '/user/register-result'].includes(
location.pathname,
)
) {
const currentUser = wsCache.get(CACHE_KEY.USER);
if (getAccessToken() && !currentUser) {
await fetchUserInfo();
}
const menus = wsCache.get(CACHE_KEY.ROLE_ROUTERS);
return {
fetchUserInfo,
currentUser,
menus,
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" />,
],
menu: {
locale: false,
},
avatarProps: {
src: initialState?.currentUser?.user.avatar,
title: <AvatarName />,
render: (_, avatarChildren) => (
<AvatarDropdown>{avatarChildren}</AvatarDropdown>
),
},
waterMarkProps: {
content: initialState?.currentUser?.user.nickname,
},
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
bgLayoutImgList: [
{
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',
},
],
// 面包屑配置
breadcrumb: {
enable: true,
useRoutes: true,
},
menuHeaderRender: undefined,
// // 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: (children) => {
// if (initialState?.loading) return <PageLoading />;
return (
<>
{children}
{isDevOrTest && (
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
)}
</>
);
},
...initialState?.settings,
};
};
/**
* @name request 配置,可以配置错误处理
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
* @doc https://umijs.org/docs/max/request#配置
*/
export const request: RequestConfig = {
baseURL: isDev ? '' : 'https://proapi.azurewebsites.net',
...errorConfig,
// 添加请求拦截器
requestInterceptors: [
(url, options) => {
// 为所有请求添加 API 前缀
if (url && !url.startsWith(process.env.API_PREFIX || '/admin-api')) {
url = (process.env.API_PREFIX || '/admin-api') + url;
}
// 获取存储在本地的 token 和 tenantId
const token = getAccessToken();
const tenantId = getTenantId(); // 默认租户ID为1
// 设置统一的请求头
const headers: Record<string, string> = {
...options.headers,
Accept: '*',
'tenant-id': tenantId,
};
// 如果有token则添加Authorization头
if (token) {
headers.Authorization = `Bearer ${getAccessToken()}`;
}
return { url, options: { ...options, headers } };
},
],
// 添加参数序列化配置
paramsSerializer: (params) => {
const searchParams = new URLSearchParams();
const appendParams = (key: string, value: any) => {
if (Array.isArray(value)) {
// 特殊处理 createTime 数组,转换为 createTime[0] 和 createTime[1] 格式
if (key === 'createTime') {
value.forEach((val, index) => {
searchParams.append(`${key}[${index}]`, val);
});
} else {
// 其他数组参数保持默认行为
value.forEach((val) => {
searchParams.append(`${key}[]`, val);
});
}
} else if (value !== null && value !== undefined) {
searchParams.append(key, String(value));
}
};
Object.keys(params).forEach((key) => {
appendParams(key, params[key]);
});
return searchParams.toString();
},
};
// Umi 4 支持异步的 patchClientRoutes
export async function patchClientRoutes({ routes }: any) {
const { wsCache } = useCache();
console.log('patchClientRoutes', patchClientRoutes);
// 先尝试从缓存获取
let menus = wsCache.get(CACHE_KEY.ROLE_ROUTERS);
// 如果缓存没有,且有 token则获取
if (!menus && getAccessToken()) {
try {
const data = await getInfo();
wsCache.set(CACHE_KEY.USER, data);
wsCache.set(CACHE_KEY.ROLE_ROUTERS, data.menus);
menus = data.menus;
} catch (error) {
console.error('获取菜单失败:', error);
return;
}
}
if (!menus || menus.length === 0) {
return;
}
const routerIndex = routes.findIndex((item: any) => item.path === '/');
const parentId = routes[routerIndex].id;
if (menus) {
const r = loopMenuItem(menus, parentId);
console.log(r, routes);
routes[routerIndex].routes.push(...r);
}
}