@spacego/fe-components 0.0.1-alpha.2 → 0.0.1-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/svg/chrome-bg-after.svg.js +3 -2
- package/lib/assets/svg/chrome-bg-before.svg.js +2 -1
- package/lib/assets/svg/icon-arrows-right.svg.js +3 -2
- package/lib/assets/svg/icon-bell.svg.js +3 -2
- package/lib/assets/svg/icon-custom.svg.js +3 -2
- package/lib/assets/svg/icon-sun-moon.svg.js +3 -2
- package/lib/assets/svg/icon-system.svg.js +3 -2
- package/lib/assets/svg/login-view.svg.js +871 -2
- package/lib/fe-layouts/auth-layout/index.js +8 -8
- package/lib/fe-layouts/basics-layout/components/basics-layout/tabs.js +5 -5
- package/package.json +9 -1
- package/src/assets/styles/animate.css +0 -62
- package/src/assets/styles/index.css +0 -49
- package/src/assets/svg/chrome-bg-after.svg +0 -1
- package/src/assets/svg/chrome-bg-before.svg +0 -1
- package/src/assets/svg/icon-arrows-right.svg +0 -1
- package/src/assets/svg/icon-bell.svg +0 -1
- package/src/assets/svg/icon-custom.svg +0 -1
- package/src/assets/svg/icon-sun-moon.svg +0 -1
- package/src/assets/svg/icon-system.svg +0 -1
- package/src/assets/svg/loading.svg +0 -13
- package/src/assets/svg/login-view.svg +0 -193
- package/src/config/constants.ts +0 -19
- package/src/config/index.ts +0 -2
- package/src/config/theme.ts +0 -20
- package/src/fe-layouts/auth-layout/index.scss +0 -34
- package/src/fe-layouts/auth-layout/index.tsx +0 -121
- package/src/fe-layouts/basics-layout/components/basics-layout/header.tsx +0 -148
- package/src/fe-layouts/basics-layout/components/basics-layout/setting-custom-color.tsx +0 -52
- package/src/fe-layouts/basics-layout/components/basics-layout/setting-drawer.tsx +0 -165
- package/src/fe-layouts/basics-layout/components/basics-layout/sidebar.tsx +0 -88
- package/src/fe-layouts/basics-layout/components/basics-layout/tabs.tsx +0 -94
- package/src/fe-layouts/basics-layout/components/utils/index.ts +0 -142
- package/src/fe-layouts/basics-layout/index.scss +0 -110
- package/src/fe-layouts/basics-layout/index.tsx +0 -207
- package/src/fe-layouts/blank-layout/index.tsx +0 -12
- package/src/fe-layouts/context/context.ts +0 -11
- package/src/fe-layouts/context/global-context.d.ts +0 -241
- package/src/fe-layouts/context/global-context.provider.tsx +0 -81
- package/src/fe-layouts/context/index.ts +0 -10
- package/src/fe-layouts/index.ts +0 -13
- package/src/fe-layouts/layout.tsx +0 -74
- package/src/hooks/index.ts +0 -1
- package/src/hooks/use-auth.hook.ts +0 -54
- package/src/index.ts +0 -24
- package/src/router/index.ts +0 -110
- package/src/router/permission.tsx +0 -134
- package/src/router/routes.tsx +0 -94
- package/src/router/utils.ts +0 -283
- package/src/store/index.ts +0 -9
- package/src/store/modules/layout-config.store.ts +0 -343
- package/src/store/modules/theme.store.ts +0 -99
- package/src/typings/index.d.ts +0 -59
- package/src/typings/shims-axios.d.ts +0 -38
- package/src/utils/icon.tsx +0 -32
- package/src/utils/index.ts +0 -9
- package/src/utils/theme.ts +0 -219
- package/tsconfig.json +0 -28
- package/vite.config.ts +0 -85
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: dushuai
|
|
3
|
-
* @Date: 2024-08-19 21:19:46
|
|
4
|
-
* @LastEditors: dushuai
|
|
5
|
-
* @LastEditTime: 2026-02-01 11:30:00
|
|
6
|
-
* @description: permission 权限控制的最佳使用
|
|
7
|
-
*
|
|
8
|
-
* 用法:/login、/logout、/(id: 'root') 为一级路由,所有功能路由均存在于/下 或者新建一级路由
|
|
9
|
-
*
|
|
10
|
-
* 受保护:需要登录后才能访问的路由 使用loader: ProtectedLoader
|
|
11
|
-
* 登录:外部调用登录接口成功后 调用用 useSubmit() 方法
|
|
12
|
-
* useSubmit()({ token, redirectTo: params.get('from') || '/' }, { method: 'post', replace: true });
|
|
13
|
-
* 登出:有外部接口时 在接口成功后 调用 useFetcher() 方法
|
|
14
|
-
* useFetcher().submit(null, { action: '/logout', method: 'post' });
|
|
15
|
-
*
|
|
16
|
-
* 所有逻辑均可根据实际情况进行修改
|
|
17
|
-
*/
|
|
18
|
-
import { message } from 'antd';
|
|
19
|
-
import { type LoaderFunctionArgs, redirect } from 'react-router-dom';
|
|
20
|
-
|
|
21
|
-
export type AuthStatus = {
|
|
22
|
-
token: string | void | null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface TokenHelpers {
|
|
26
|
-
/** 获取token */
|
|
27
|
-
getToken: () => string | void | null;
|
|
28
|
-
/** 设置token */
|
|
29
|
-
setToken: (token: string) => void;
|
|
30
|
-
/** 移除token */
|
|
31
|
-
removeToken: () => void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* 创建权限相关的 loader 和 action
|
|
36
|
-
* @param tokenHelpers token 操作方法
|
|
37
|
-
*/
|
|
38
|
-
export function createPermissionHelpers(tokenHelpers: TokenHelpers) {
|
|
39
|
-
const { getToken, setToken, removeToken } = tokenHelpers;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* root根目录的Loader 根据需要设置使用
|
|
43
|
-
*/
|
|
44
|
-
function RootLoader(): AuthStatus {
|
|
45
|
-
const token = getToken();
|
|
46
|
-
|
|
47
|
-
return { token };
|
|
48
|
-
// Get our logged in user, if they exist, from the root route loader data
|
|
49
|
-
// const { token } = useRouteLoaderData('root') as AuthStatus;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* 受保护页面的Loader
|
|
54
|
-
*/
|
|
55
|
-
function ProtectedLoader({ request }: LoaderFunctionArgs) {
|
|
56
|
-
// If the user is not logged in and tries to access `/protected`, we redirect
|
|
57
|
-
// them to `/login` with a `from` parameter that allows login to redirect back
|
|
58
|
-
// to this page upon successful authentication
|
|
59
|
-
const token = getToken();
|
|
60
|
-
|
|
61
|
-
if(!token) {
|
|
62
|
-
const params = new URLSearchParams();
|
|
63
|
-
params.set('from', new URL(request.url).pathname);
|
|
64
|
-
return redirect('/auth/login?' + params.toString());
|
|
65
|
-
}
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* login页面的Loader 主要用来频闭登陆后再次进入/login
|
|
71
|
-
* 所有有该需求的页面都可以使用
|
|
72
|
-
*/
|
|
73
|
-
async function LoginLoader() {
|
|
74
|
-
const token = getToken();
|
|
75
|
-
if(token) {
|
|
76
|
-
return redirect('/');
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 登录的action 登陆后自动跳转到之前想要访问的页面
|
|
83
|
-
*/
|
|
84
|
-
async function LoginAction({ request }: LoaderFunctionArgs) {
|
|
85
|
-
const formData = await request.formData();
|
|
86
|
-
const token = formData.get('token') as string | null;
|
|
87
|
-
|
|
88
|
-
// Validate our form inputs and return validation errors via useActionData()
|
|
89
|
-
if(!token) {
|
|
90
|
-
return message.warning('You must provide a token to log in');
|
|
91
|
-
// return {
|
|
92
|
-
// error: 'You must provide a token to log in'
|
|
93
|
-
/**
|
|
94
|
-
* action内return error时 只能通过 useActionData() as { error: string } | undefined 来获取,根据需要使用
|
|
95
|
-
*/
|
|
96
|
-
// };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Sign in and redirect to the proper destination if successful.
|
|
100
|
-
try {
|
|
101
|
-
setToken(token);
|
|
102
|
-
message.success('登录成功');
|
|
103
|
-
} catch(error) {
|
|
104
|
-
// Unused as of now but this is how you would handle invalid
|
|
105
|
-
// username/password combinations - just like validating the inputs
|
|
106
|
-
// above
|
|
107
|
-
return message.warning('Invalid login attempt');
|
|
108
|
-
// return {
|
|
109
|
-
// error: 'Invalid login attempt'
|
|
110
|
-
// };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const redirectTo = formData.get('redirectTo') as string | null;
|
|
114
|
-
return redirect(redirectTo || '/');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* 退出登录的action
|
|
119
|
-
*/
|
|
120
|
-
function LogoutAction() {
|
|
121
|
-
// We signout in a "resource route" that we can hit from a fetcher.Form
|
|
122
|
-
removeToken();
|
|
123
|
-
message.success('退出成功');
|
|
124
|
-
return redirect('/auth/login');
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
RootLoader,
|
|
129
|
-
ProtectedLoader,
|
|
130
|
-
LoginLoader,
|
|
131
|
-
LoginAction,
|
|
132
|
-
LogoutAction
|
|
133
|
-
};
|
|
134
|
-
}
|
package/src/router/routes.tsx
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: dushuai
|
|
3
|
-
* @Date: 2024-03-29 16:17:20
|
|
4
|
-
* @LastEditors: dushuai
|
|
5
|
-
* @LastEditTime: 2026-02-01 12:15:00
|
|
6
|
-
* @description: 路由表
|
|
7
|
-
*/
|
|
8
|
-
import type { ComponentType } from 'react';
|
|
9
|
-
import type { RouteObject } from 'react-router-dom';
|
|
10
|
-
|
|
11
|
-
import type { createPermissionHelpers } from './permission';
|
|
12
|
-
import { createLazyComponent, type TModule } from './utils';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 创建默认路由表
|
|
16
|
-
* @param modules 页面模块集合
|
|
17
|
-
* @param permissionHelpers 权限相关的 loader 和 action
|
|
18
|
-
* @param BaseLayout 基础布局组件
|
|
19
|
-
*/
|
|
20
|
-
export function createDefaultRoutes(
|
|
21
|
-
modules: TModule,
|
|
22
|
-
permissionHelpers: ReturnType<typeof createPermissionHelpers>,
|
|
23
|
-
BaseLayout: ComponentType<any>
|
|
24
|
-
): RouteObject[] {
|
|
25
|
-
const { RootLoader, LoginLoader, LoginAction, LogoutAction } = permissionHelpers;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 路由表
|
|
29
|
-
*
|
|
30
|
-
* Component vs element 区别:
|
|
31
|
-
*
|
|
32
|
-
* 1. Component (推荐用于简单场景)
|
|
33
|
-
* - 接收组件类型,React Router 自动实例化
|
|
34
|
-
* - 自动传递路由数据(通过 useLoaderData() 获取)
|
|
35
|
-
* - 不能传递自定义 props
|
|
36
|
-
* - 代码更简洁
|
|
37
|
-
*
|
|
38
|
-
* 2. element (用于需要传递 props 的场景)
|
|
39
|
-
* - 接收 React 元素(JSX)
|
|
40
|
-
* - 可以传递自定义 props
|
|
41
|
-
* - 可以包装 Suspense、ErrorBoundary 等
|
|
42
|
-
* - 仍然可以通过 useLoaderData() 获取 loader 数据
|
|
43
|
-
*/
|
|
44
|
-
const routes: RouteObject[] = [
|
|
45
|
-
{
|
|
46
|
-
id: 'root',
|
|
47
|
-
path: '/',
|
|
48
|
-
// 使用 element: 可以传递自定义 props
|
|
49
|
-
element: <BaseLayout layoutType="basic" />,
|
|
50
|
-
loader: RootLoader,
|
|
51
|
-
children: [] // ...dynamicRoutes
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
id: 'blank',
|
|
55
|
-
path: '/blank',
|
|
56
|
-
element: <BaseLayout layoutType="blank" />,
|
|
57
|
-
loader: RootLoader,
|
|
58
|
-
children: []
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
id: 'auth',
|
|
62
|
-
element: <BaseLayout layoutType="auth" />,
|
|
63
|
-
loader: RootLoader,
|
|
64
|
-
children: [
|
|
65
|
-
{
|
|
66
|
-
path: '/auth/login',
|
|
67
|
-
loader: LoginLoader,
|
|
68
|
-
action: LoginAction,
|
|
69
|
-
// 使用 Component: 简洁,但不能传递自定义 props
|
|
70
|
-
Component: createLazyComponent('/basics/login', modules)
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
path: '/auth/register',
|
|
74
|
-
loader: LoginLoader,
|
|
75
|
-
action: LoginAction,
|
|
76
|
-
// 使用 Component: 简洁,但不能传递自定义 props
|
|
77
|
-
Component: createLazyComponent('/basics/register', modules)
|
|
78
|
-
}
|
|
79
|
-
]
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
// logout路由只用来退出登录,不展示页面
|
|
83
|
-
path: '/logout',
|
|
84
|
-
action: LogoutAction,
|
|
85
|
-
Component: createLazyComponent('/basics/error', modules)
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
path: '*',
|
|
89
|
-
Component: createLazyComponent('/basics/error', modules)
|
|
90
|
-
}
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
return routes;
|
|
94
|
-
}
|
package/src/router/utils.ts
DELETED
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { type ComponentType, lazy } from 'react';
|
|
2
|
-
import type { RouteObject } from 'react-router-dom';
|
|
3
|
-
|
|
4
|
-
import type { IRoute, MenuType } from '@/typings';
|
|
5
|
-
|
|
6
|
-
export type TModule = {
|
|
7
|
-
[keys in string]: () => Promise<{ default: ComponentType<any>; }>
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 获取页面路径
|
|
12
|
-
* @param name
|
|
13
|
-
* @returns
|
|
14
|
-
*/
|
|
15
|
-
export function getPath(name: string) {
|
|
16
|
-
// 移除开头的第一个斜杠
|
|
17
|
-
const cleanName = name.replace(/^\/+/, '');
|
|
18
|
-
return `/src/pages/${cleanName}/index.tsx`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 获取动态页面
|
|
23
|
-
* @param name
|
|
24
|
-
* @param modules
|
|
25
|
-
* @returns
|
|
26
|
-
*/
|
|
27
|
-
export function createLazyComponent(name: string, modules: TModule) {
|
|
28
|
-
// return lazy(components[`${name}/index.tsx`])
|
|
29
|
-
const moduleFn = modules[getPath(name)];
|
|
30
|
-
if(!moduleFn) {
|
|
31
|
-
console.warn(`Module not found for path: ${name}`);
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
return lazy(moduleFn);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* 规范化路径格式
|
|
39
|
-
* @param path 路径
|
|
40
|
-
* @returns 规范化后的路径
|
|
41
|
-
*/
|
|
42
|
-
function normalizePath(path?: string): string | undefined {
|
|
43
|
-
if(!path) return void 0;
|
|
44
|
-
// 确保路径以 / 开头
|
|
45
|
-
return path.startsWith('/') ? path : `/${path}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** 路由节点类型 */
|
|
49
|
-
type RouteNode = RouteObject & { parentId: string; children?: RouteNode[] };
|
|
50
|
-
|
|
51
|
-
/** 菜单项类型(用于 antd Menu 组件) */
|
|
52
|
-
export interface MenuItem {
|
|
53
|
-
key: string;
|
|
54
|
-
label: string;
|
|
55
|
-
icon?: string;
|
|
56
|
-
/** 对应的路由节点信息(不包含 children) */
|
|
57
|
-
routeNode?: Omit<RouteNode, 'children'>;
|
|
58
|
-
children?: MenuItem[];
|
|
59
|
-
menuType?: MenuType;
|
|
60
|
-
/** 是否独立菜单 */
|
|
61
|
-
isIndependentMenu?: boolean;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** 处理结果类型 */
|
|
65
|
-
export interface ProcessedRoutes {
|
|
66
|
-
/** react-router 路由配置 */
|
|
67
|
-
routes: RouteNode[];
|
|
68
|
-
/** antd Menu 菜单配置 */
|
|
69
|
-
menus: MenuItem[];
|
|
70
|
-
/** 按钮权限列表 */
|
|
71
|
-
permissions: string[];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* 按 sort 字段排序
|
|
76
|
-
*/
|
|
77
|
-
function sortByOrder(a: IRoute, b: IRoute): number {
|
|
78
|
-
return (a.sort ?? 0) - (b.sort ?? 0);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* 将扁平的 IRoute 数组处理成三份数据
|
|
83
|
-
* @param rawRoutes 扁平路由数组
|
|
84
|
-
* @param modules 页面模块集合
|
|
85
|
-
* @param protectedLoader 受保护路由的加载器
|
|
86
|
-
* @returns { routes, menus, permissions }
|
|
87
|
-
*/
|
|
88
|
-
export function processRoutes(
|
|
89
|
-
rawRoutes: IRoute[],
|
|
90
|
-
modules: TModule,
|
|
91
|
-
protectedLoader?: any
|
|
92
|
-
): ProcessedRoutes {
|
|
93
|
-
// 按 sort 排序
|
|
94
|
-
const sortedRoutes = [...rawRoutes].sort(sortByOrder);
|
|
95
|
-
|
|
96
|
-
// 按类型分组:1目录、2菜单、3按钮
|
|
97
|
-
const routesByType = sortedRoutes.reduce((acc, route) => {
|
|
98
|
-
const type = route.menuType || 2;
|
|
99
|
-
if(!acc[type]) acc[type] = [];
|
|
100
|
-
acc[type].push(route);
|
|
101
|
-
return acc;
|
|
102
|
-
}, {} as Record<MenuType, IRoute[]>);
|
|
103
|
-
|
|
104
|
-
// 提取按钮权限列表(使用 menuCode 作为权限标识)
|
|
105
|
-
const permissions = (routesByType[3] || [])
|
|
106
|
-
.filter(r => r.menuCode)
|
|
107
|
-
.map(r => r.menuCode!);
|
|
108
|
-
|
|
109
|
-
// 只处理目录和菜单(用于路由和菜单)
|
|
110
|
-
const directoriesAndMenus = [
|
|
111
|
-
...(routesByType[1] || []),
|
|
112
|
-
...(routesByType[2] || [])
|
|
113
|
-
].sort(sortByOrder);
|
|
114
|
-
|
|
115
|
-
// 构建路由树
|
|
116
|
-
const routeMap = new Map<string, RouteNode>();
|
|
117
|
-
const rootRoutes: RouteNode[] = [];
|
|
118
|
-
|
|
119
|
-
// 第一遍遍历:转换为 RouteNode
|
|
120
|
-
directoriesAndMenus.forEach(route => {
|
|
121
|
-
const parentId = route.parentId || '/';
|
|
122
|
-
const path = normalizePath(route.path);
|
|
123
|
-
|
|
124
|
-
const node: RouteNode = {
|
|
125
|
-
id: route.menuId,
|
|
126
|
-
path,
|
|
127
|
-
parentId,
|
|
128
|
-
// 将 title、icon 放入 handle 字段
|
|
129
|
-
handle: {
|
|
130
|
-
title: route.title,
|
|
131
|
-
icon: route.icon,
|
|
132
|
-
isShowMenu: route.isShowMenu ?? true,
|
|
133
|
-
isIndependentMenu: route.isIndependentMenu ?? false,
|
|
134
|
-
activeRouteMenuId: route.activeRouteMenuId ?? route.menuId
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// 菜单类型(2)需要组件(使用 menuCode 作为组件路径)
|
|
139
|
-
if(route.menuType === 2 && route.menuCode) {
|
|
140
|
-
const Component = createLazyComponent(route.menuCode, modules);
|
|
141
|
-
if(Component) {
|
|
142
|
-
node.Component = Component;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if(route.index) {
|
|
146
|
-
// index 路由需要特殊处理:不能同时有 path 和 index
|
|
147
|
-
node.index = true as any;
|
|
148
|
-
delete node.path;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if(route.protected !== false && protectedLoader) {
|
|
152
|
-
node.loader = protectedLoader;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
routeMap.set(route.menuId, node);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// 第二遍遍历:构建父子关系
|
|
160
|
-
directoriesAndMenus.forEach(route => {
|
|
161
|
-
const node = routeMap.get(route.menuId)!;
|
|
162
|
-
const parentId = route.parentId;
|
|
163
|
-
|
|
164
|
-
if(parentId && parentId !== '/') {
|
|
165
|
-
const parent = routeMap.get(parentId);
|
|
166
|
-
|
|
167
|
-
if(parent) {
|
|
168
|
-
if(!parent.children) parent.children = [];
|
|
169
|
-
parent.children.push(node);
|
|
170
|
-
} else {
|
|
171
|
-
rootRoutes.push(node);
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
rootRoutes.push(node);
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// 构建菜单树(只包含需要在菜单中显示的项)
|
|
179
|
-
const menuMap = new Map<string, MenuItem>();
|
|
180
|
-
const rootMenus: MenuItem[] = [];
|
|
181
|
-
|
|
182
|
-
// 过滤出需要在菜单中显示的项(isShowMenu !== false,默认为true)
|
|
183
|
-
const menuVisibleRoutes = directoriesAndMenus.filter(route => route.isShowMenu !== false);
|
|
184
|
-
|
|
185
|
-
// 第一遍遍历:转换为 MenuItem
|
|
186
|
-
menuVisibleRoutes.forEach(route => {
|
|
187
|
-
const routeNode = routeMap.get(route.menuId);
|
|
188
|
-
// 从 RouteNode 中提取信息,去除 children 字段
|
|
189
|
-
const routeNodeInfo: Omit<RouteNode, 'children'> | undefined = routeNode
|
|
190
|
-
? (() => {
|
|
191
|
-
const { children, ...rest } = routeNode;
|
|
192
|
-
return rest;
|
|
193
|
-
})()
|
|
194
|
-
: undefined;
|
|
195
|
-
|
|
196
|
-
const menuItem: MenuItem = {
|
|
197
|
-
key: route.menuId,
|
|
198
|
-
label: route.title || route.menuId,
|
|
199
|
-
icon: route.icon,
|
|
200
|
-
routeNode: routeNodeInfo,
|
|
201
|
-
menuType: route.menuType,
|
|
202
|
-
isIndependentMenu: route.isIndependentMenu
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
menuMap.set(route.menuId, menuItem);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
// 第二遍遍历:构建父子关系
|
|
209
|
-
menuVisibleRoutes.forEach(route => {
|
|
210
|
-
const menuItem = menuMap.get(route.menuId)!;
|
|
211
|
-
const parentId = route.parentId;
|
|
212
|
-
|
|
213
|
-
if(parentId && parentId !== '/') {
|
|
214
|
-
const parent = menuMap.get(parentId);
|
|
215
|
-
|
|
216
|
-
if(parent) {
|
|
217
|
-
if(!parent.children) parent.children = [];
|
|
218
|
-
parent.children.push(menuItem);
|
|
219
|
-
} else {
|
|
220
|
-
rootMenus.push(menuItem);
|
|
221
|
-
}
|
|
222
|
-
} else {
|
|
223
|
-
rootMenus.push(menuItem);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* 递归处理菜单:根据 isIndependentMenu 字段处理独立菜单
|
|
229
|
-
* 如果是独立菜单,在 menus 内移除对应目录,直接展示此菜单
|
|
230
|
-
* 独立菜单的判断只针对直接父级,不是无限向外查找
|
|
231
|
-
*/
|
|
232
|
-
function simplifyMenus(menus: MenuItem[]): MenuItem[] {
|
|
233
|
-
const result: MenuItem[] = [];
|
|
234
|
-
|
|
235
|
-
menus.forEach(menu => {
|
|
236
|
-
// 先递归处理子菜单
|
|
237
|
-
if(menu.children && menu.children.length > 0) {
|
|
238
|
-
menu.children = simplifyMenus(menu.children);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// 如果是目录,检查是否有独立菜单的子菜单
|
|
242
|
-
if(menu.menuType === 1 && menu.children && menu.children.length > 0) {
|
|
243
|
-
// 查找所有独立菜单的子菜单(只处理直接子菜单,不递归)
|
|
244
|
-
const independentMenus = menu.children.filter(child => {
|
|
245
|
-
// 只处理直接子菜单,且是独立菜单,且没有被提升过的
|
|
246
|
-
return child.isIndependentMenu === true &&
|
|
247
|
-
child.menuType === 2 &&
|
|
248
|
-
!(child as any).customType;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
if(independentMenus.length > 0) {
|
|
252
|
-
// 如果有独立菜单,移除目录,直接展示独立菜单
|
|
253
|
-
// 将独立菜单提升到当前层级,并添加内部标识
|
|
254
|
-
const promotedMenus = independentMenus.map(child => ({
|
|
255
|
-
...child,
|
|
256
|
-
customType: 'menu-promoted' // 内部标识,表示这个菜单是被提升的
|
|
257
|
-
}));
|
|
258
|
-
|
|
259
|
-
// 保留其他非独立菜单
|
|
260
|
-
const otherMenus = menu.children.filter(child => {
|
|
261
|
-
return !(child.isIndependentMenu === true && child.menuType === 2 && !(child as any).customType);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
result.push(...promotedMenus, ...otherMenus);
|
|
265
|
-
} else {
|
|
266
|
-
// 没有独立菜单,保留原目录
|
|
267
|
-
result.push(menu);
|
|
268
|
-
}
|
|
269
|
-
} else {
|
|
270
|
-
// 不是目录,或者没有子菜单,直接保留
|
|
271
|
-
result.push(menu);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
return result;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return {
|
|
279
|
-
routes: rootRoutes,
|
|
280
|
-
menus: simplifyMenus(rootMenus),
|
|
281
|
-
permissions
|
|
282
|
-
};
|
|
283
|
-
}
|
package/src/store/index.ts
DELETED