2024-07-13 16:35:47 +08:00
|
|
|
import type {
|
|
|
|
ComponentRecordType,
|
|
|
|
GenerateMenuAndRoutesOptions,
|
2024-08-07 08:57:56 +08:00
|
|
|
RouteRecordStringComponent,
|
2024-07-13 16:35:47 +08:00
|
|
|
} from '@vben/types';
|
2024-06-30 15:03:37 +08:00
|
|
|
|
2024-07-13 16:35:47 +08:00
|
|
|
import { generateAccessible } from '@vben/access';
|
2024-07-23 00:03:59 +08:00
|
|
|
import { preferences } from '@vben/preferences';
|
2024-06-30 15:03:37 +08:00
|
|
|
|
|
|
|
import { message } from 'ant-design-vue';
|
2024-08-07 08:57:56 +08:00
|
|
|
import { cloneDeep } from 'lodash-es';
|
2024-06-30 15:03:37 +08:00
|
|
|
|
2024-08-08 08:03:04 +08:00
|
|
|
import { getAllMenusApi, type Menu } from '#/api';
|
2024-06-30 15:03:37 +08:00
|
|
|
import { BasicLayout, IFrameView } from '#/layouts';
|
2024-07-07 00:17:44 +08:00
|
|
|
import { $t } from '#/locales';
|
2024-06-30 15:03:37 +08:00
|
|
|
|
2024-07-13 16:52:08 +08:00
|
|
|
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
|
2024-08-07 08:57:56 +08:00
|
|
|
const NotFoundComponent = () => import('#/views/_core/fallback/not-found.vue');
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 这里放本地路由
|
|
|
|
*/
|
|
|
|
const localMenuList: RouteRecordStringComponent[] = [
|
|
|
|
{
|
|
|
|
component: 'BasicLayout',
|
|
|
|
meta: {
|
|
|
|
order: -1,
|
|
|
|
title: 'page.dashboard.title',
|
|
|
|
},
|
|
|
|
name: 'Dashboard',
|
|
|
|
path: '/',
|
|
|
|
redirect: '/analytics',
|
|
|
|
children: [
|
|
|
|
{
|
|
|
|
name: 'Analytics',
|
|
|
|
path: '/analytics',
|
|
|
|
component: '/dashboard/analytics/index',
|
|
|
|
meta: {
|
|
|
|
affixTab: true,
|
|
|
|
title: 'page.dashboard.analytics',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'Workspace',
|
|
|
|
path: '/workspace',
|
|
|
|
component: '/dashboard/workspace/index',
|
|
|
|
meta: {
|
|
|
|
title: 'page.dashboard.workspace',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: 'VbenDocument',
|
|
|
|
path: '/vben-admin/document',
|
|
|
|
component: 'IFrameView',
|
|
|
|
meta: {
|
|
|
|
icon: 'lucide:book-open-text',
|
|
|
|
iframeSrc: 'https://dapdap.top',
|
|
|
|
keepAlive: true,
|
|
|
|
title: $t('page.vben.document'),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
component: 'BasicLayout',
|
|
|
|
meta: {
|
|
|
|
hideChildrenInMenu: true,
|
|
|
|
icon: 'lucide:copyright',
|
|
|
|
order: 9999,
|
|
|
|
title: $t('page.vben.about'),
|
|
|
|
},
|
|
|
|
name: 'About',
|
|
|
|
path: '/about',
|
|
|
|
children: [
|
|
|
|
{
|
2024-08-20 08:45:45 +08:00
|
|
|
component: '/_core/about/index',
|
2024-08-07 08:57:56 +08:00
|
|
|
meta: {
|
|
|
|
title: $t('page.vben.about'),
|
|
|
|
},
|
|
|
|
name: 'VbenAbout',
|
|
|
|
path: '/vben-admin/about',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
];
|
2024-06-30 15:03:37 +08:00
|
|
|
|
2024-07-13 16:35:47 +08:00
|
|
|
async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
2024-06-30 15:03:37 +08:00
|
|
|
const pageMap: ComponentRecordType = import.meta.glob('../views/**/*.vue');
|
|
|
|
|
|
|
|
const layoutMap: ComponentRecordType = {
|
|
|
|
BasicLayout,
|
|
|
|
IFrameView,
|
2024-08-07 08:57:56 +08:00
|
|
|
NotFoundComponent,
|
2024-06-30 15:03:37 +08:00
|
|
|
};
|
|
|
|
|
2024-08-07 08:57:56 +08:00
|
|
|
/**
|
2024-08-30 13:59:33 +08:00
|
|
|
* 优化后的后台路由转 vben 路由
|
2024-08-07 08:57:56 +08:00
|
|
|
*
|
|
|
|
* @param menuList 后台菜单
|
|
|
|
* @param parentPath 上级目录
|
2024-08-30 13:59:33 +08:00
|
|
|
* @returns vben 路由
|
2024-08-07 08:57:56 +08:00
|
|
|
*/
|
|
|
|
function backMenuToVbenMenu(
|
|
|
|
menuList: Menu[],
|
|
|
|
parentPath = '',
|
|
|
|
): RouteRecordStringComponent[] {
|
|
|
|
const resultList: RouteRecordStringComponent[] = [];
|
2024-08-30 13:59:33 +08:00
|
|
|
|
2024-08-07 08:57:56 +08:00
|
|
|
menuList.forEach((menu) => {
|
2024-08-30 13:59:33 +08:00
|
|
|
// 根目录处理
|
|
|
|
if (isRootMenu(menu)) {
|
2024-08-08 08:03:04 +08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
|
|
menu.meta = menu.children[0]!.meta;
|
2024-08-07 08:57:56 +08:00
|
|
|
menu.component = 'RootMenu';
|
|
|
|
}
|
2024-08-30 13:59:33 +08:00
|
|
|
|
|
|
|
// 外链处理
|
|
|
|
if (isExternalLink(menu)) {
|
2024-08-07 08:57:56 +08:00
|
|
|
menu.component = 'Link';
|
|
|
|
}
|
2024-08-30 13:59:33 +08:00
|
|
|
|
|
|
|
// 内嵌 iframe 处理
|
|
|
|
if (isIframe(menu)) {
|
2024-08-07 08:57:56 +08:00
|
|
|
menu.component = 'IFrameView';
|
|
|
|
}
|
|
|
|
|
2024-08-30 13:59:33 +08:00
|
|
|
// 处理路径
|
|
|
|
menu.path = formatPath(menu.path, parentPath);
|
2024-08-07 08:57:56 +08:00
|
|
|
|
2024-08-30 13:59:33 +08:00
|
|
|
const vbenRoute: RouteRecordStringComponent = createVbenRoute(menu);
|
2024-08-07 08:57:56 +08:00
|
|
|
|
2024-08-30 13:59:33 +08:00
|
|
|
// 处理组件类型
|
|
|
|
handleComponentType(menu, vbenRoute);
|
2024-08-07 08:57:56 +08:00
|
|
|
|
2024-08-30 13:59:33 +08:00
|
|
|
// 递归处理子路由
|
2024-08-07 08:57:56 +08:00
|
|
|
if (menu.children && menu.children.length > 0) {
|
|
|
|
vbenRoute.children = backMenuToVbenMenu(menu.children, menu.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
resultList.push(vbenRoute);
|
|
|
|
});
|
2024-08-30 13:59:33 +08:00
|
|
|
|
2024-08-07 08:57:56 +08:00
|
|
|
return resultList;
|
|
|
|
}
|
|
|
|
|
2024-08-30 13:59:33 +08:00
|
|
|
/**
|
|
|
|
* 根目录处理
|
|
|
|
* @param menu
|
|
|
|
*/
|
|
|
|
function isRootMenu(menu: Menu): boolean {
|
|
|
|
return menu.path === '/' && menu.children && menu.children.length === 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 外链判断
|
|
|
|
* @param menu
|
|
|
|
*/
|
|
|
|
function isExternalLink(menu: Menu): boolean {
|
|
|
|
return (
|
|
|
|
/^https?:\/\//.test(menu.path) &&
|
|
|
|
(menu.component === 'Layout' || menu.component === 'ParentView')
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 内嵌 iframe 判断
|
|
|
|
* @param menu
|
|
|
|
*/
|
|
|
|
function isIframe(menu: Menu): boolean {
|
|
|
|
return !!menu.meta?.link && menu.component === 'InnerLink';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 格式化路径
|
|
|
|
* @param path
|
|
|
|
* @param parentPath
|
|
|
|
*/
|
|
|
|
function formatPath(path: string, parentPath: string): string {
|
|
|
|
// 如果父路径是根目录且当前路径不是根目录,避免多余的斜杠
|
|
|
|
if (parentPath === '/' && path !== '/') {
|
|
|
|
return `/${path}`;
|
|
|
|
}
|
|
|
|
// 如果当前路径是根目录,直接返回
|
|
|
|
if (path === '/') {
|
|
|
|
return parentPath ? `${parentPath}` : path;
|
|
|
|
}
|
|
|
|
// 处理其他正常情况
|
|
|
|
return parentPath ? `${parentPath}/${path}` : path;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 创建 vben 路由
|
|
|
|
* @param menu
|
|
|
|
*/
|
|
|
|
function createVbenRoute(menu: Menu): RouteRecordStringComponent {
|
|
|
|
return {
|
|
|
|
component: menu.component,
|
|
|
|
meta: {
|
|
|
|
hideInMenu: menu.hidden,
|
|
|
|
icon: menu.meta?.icon,
|
|
|
|
keepAlive: !menu.meta?.noCache,
|
|
|
|
title: menu.meta?.title,
|
|
|
|
},
|
|
|
|
name: menu.name,
|
|
|
|
path: menu.path,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 处理不同组件类型
|
|
|
|
* @param menu
|
|
|
|
* @param vbenRoute
|
|
|
|
*/
|
|
|
|
function handleComponentType(
|
|
|
|
menu: Menu,
|
|
|
|
vbenRoute: RouteRecordStringComponent,
|
|
|
|
) {
|
|
|
|
switch (menu.component) {
|
|
|
|
case 'Layout': {
|
|
|
|
vbenRoute.component = 'BasicLayout';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* iframe内嵌
|
|
|
|
*/
|
|
|
|
case 'IFrameView': {
|
|
|
|
vbenRoute.component = 'IFrameView';
|
|
|
|
handleIframePath(vbenRoute);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 外链 新窗口打开
|
|
|
|
*/
|
|
|
|
case 'Link': {
|
|
|
|
vbenRoute.component = 'BasicLayout';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 根目录菜单
|
|
|
|
*/
|
|
|
|
case 'RootMenu': {
|
|
|
|
if (vbenRoute.meta) {
|
|
|
|
vbenRoute.meta.hideChildrenInMenu = true;
|
|
|
|
}
|
|
|
|
vbenRoute.component = 'BasicLayout';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 不能为layout 会套两层BasicLayout
|
|
|
|
*/
|
|
|
|
case 'ParentView': {
|
|
|
|
vbenRoute.component = '';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 其他自定义组件 如system/user/index 拼接/
|
|
|
|
*/
|
|
|
|
default: {
|
|
|
|
vbenRoute.component = `/${menu.component}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 处理 iframe 路径中的特殊字符
|
|
|
|
* @param vbenRoute
|
|
|
|
*/
|
|
|
|
function handleIframePath(vbenRoute: RouteRecordStringComponent) {
|
|
|
|
if (vbenRoute.meta) {
|
|
|
|
vbenRoute.meta.iframeSrc = vbenRoute.meta.link;
|
|
|
|
|
|
|
|
const specialChars = ['#', '?', '&'];
|
|
|
|
specialChars.forEach((char) => {
|
|
|
|
if (vbenRoute.path.includes(char)) {
|
|
|
|
vbenRoute.path = vbenRoute.path.replace(char, '');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-13 16:35:47 +08:00
|
|
|
return await generateAccessible(preferences.app.accessMode, {
|
2024-06-30 15:03:37 +08:00
|
|
|
...options,
|
|
|
|
fetchMenuListAsync: async () => {
|
|
|
|
message.loading({
|
2024-07-10 21:55:16 +08:00
|
|
|
content: `${$t('common.loadingMenu')}...`,
|
2024-08-07 08:57:56 +08:00
|
|
|
duration: 1,
|
2024-06-30 15:03:37 +08:00
|
|
|
});
|
2024-08-30 13:59:33 +08:00
|
|
|
|
2024-08-07 08:57:56 +08:00
|
|
|
// 后台返回路由/菜单
|
2024-08-08 08:03:04 +08:00
|
|
|
const backMenuList = await getAllMenusApi();
|
2024-08-07 08:57:56 +08:00
|
|
|
// 转换为vben能用的路由
|
|
|
|
const vbenMenuList = backMenuToVbenMenu(backMenuList);
|
|
|
|
// 特别注意 这里要深拷贝
|
|
|
|
const menuList = [...cloneDeep(localMenuList), ...vbenMenuList];
|
|
|
|
console.log('menuList', menuList);
|
2024-08-30 13:59:33 +08:00
|
|
|
|
2024-08-07 08:57:56 +08:00
|
|
|
return menuList;
|
2024-06-30 15:03:37 +08:00
|
|
|
},
|
|
|
|
// 可以指定没有权限跳转403页面
|
2024-07-10 21:20:11 +08:00
|
|
|
forbiddenComponent,
|
2024-06-30 15:03:37 +08:00
|
|
|
// 如果 route.meta.menuVisibleWithForbidden = true
|
|
|
|
layoutMap,
|
|
|
|
pageMap,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export { generateAccess };
|