2025-12-17 10:38:15 +08:00
|
|
|
|
import { RouteRecordRaw } from 'vue-router';
|
|
|
|
|
|
import { useUserInfo } from '/@/stores/userInfo';
|
|
|
|
|
|
// import { useRequestOldRoutes } from '/@/stores/requestOldRoutes';
|
|
|
|
|
|
import pinia from '/@/stores/index';
|
|
|
|
|
|
import { Session } from '/@/utils/storage';
|
|
|
|
|
|
import { NextLoading } from '/@/utils/loading';
|
|
|
|
|
|
import { dynamicRoutes, notFoundAndNoPower } from '/@/router/route';
|
|
|
|
|
|
import { formatTwoStageRoutes, formatFlatteningRoutes, router } from '/@/router/index';
|
|
|
|
|
|
import { useRoutesList } from '/@/stores/routesList';
|
|
|
|
|
|
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
|
|
|
|
|
|
import SysMenuApi from '/@/api/SysMenuApi';
|
|
|
|
|
|
import { accessTokenKey } from '/@/utils/request';
|
|
|
|
|
|
|
|
|
|
|
|
// 后端控制路由
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取目录下的 .vue、.tsx 全部文件
|
|
|
|
|
|
* @method import.meta.glob
|
|
|
|
|
|
* @link 参考:https://cn.vitejs.dev/guide/features.html#json
|
|
|
|
|
|
*/
|
|
|
|
|
|
const layouModules: any = import.meta.glob('../layout/routerView/*.{vue,tsx}');
|
|
|
|
|
|
const viewsModules: any = import.meta.glob('../views/**/*.{vue,tsx}');
|
|
|
|
|
|
const dynamicViewsModules: Record<string, Function> = Object.assign({}, { ...layouModules }, { ...viewsModules });
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 后端控制路由:初始化方法,防止刷新时路由丢失
|
|
|
|
|
|
* @method NextLoading 界面 loading 动画开始执行
|
|
|
|
|
|
* @method useUserInfo().setUserInfos() 触发初始化用户信息 pinia
|
|
|
|
|
|
* @method useRequestOldRoutes().setRequestOldRoutes() 存储接口原始路由(未处理component),根据需求选择使用
|
|
|
|
|
|
* @method setAddRoute 添加动态路由
|
|
|
|
|
|
* @method setFilterMenuAndCacheTagsViewRoutes 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function initBackEndControlRoutes() {
|
|
|
|
|
|
// 界面 loading 动画开始执行
|
|
|
|
|
|
if (window.nextLoading === undefined) NextLoading.start();
|
|
|
|
|
|
// 无 token 停止执行下一步
|
|
|
|
|
|
if (!Session.get(accessTokenKey)) return false;
|
|
|
|
|
|
// 触发初始化用户信息 pinia
|
|
|
|
|
|
// https://gitee.com/lyt-top/vue-next-admin/issues/I5F1HP
|
|
|
|
|
|
await useUserInfo().getUserInfo();
|
|
|
|
|
|
// 获取路由菜单数据
|
|
|
|
|
|
const { data } = await getBackEndControlRoutes();
|
|
|
|
|
|
// 无登录权限时,添加判断
|
|
|
|
|
|
// https://gitee.com/lyt-top/vue-next-admin/issues/I64HVO
|
|
|
|
|
|
const routeList = data ?? [];
|
|
|
|
|
|
if (routeList.length === 0) return Promise.resolve(true);
|
|
|
|
|
|
// 存储接口原始路由(未处理component),根据需求选择使用
|
|
|
|
|
|
// useRequestOldRoutes().setRequestOldRoutes(JSON.parse(JSON.stringify(data ?? [])));
|
|
|
|
|
|
// 处理路由(component),替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
|
|
|
|
|
|
|
|
|
|
|
|
//静态路由
|
|
|
|
|
|
let staticRoute = [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/personal',
|
|
|
|
|
|
name: 'personal',
|
|
|
|
|
|
component: 'personal/index',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '个人中心',
|
|
|
|
|
|
isHide: true,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/system/config/design',
|
|
|
|
|
|
name: 'configDesign',
|
|
|
|
|
|
component: 'system/config/design',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '配置设计',
|
|
|
|
|
|
isKeepAlive: false,
|
|
|
|
|
|
isHide: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/system/config/render',
|
|
|
|
|
|
name: 'configRender',
|
|
|
|
|
|
component: 'system/config/render',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '编辑配置项',
|
|
|
|
|
|
isKeepAlive: false,
|
|
|
|
|
|
isHide: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/system/config/items',
|
|
|
|
|
|
name: 'configItems',
|
|
|
|
|
|
component: 'system/config/itemList',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '配置项列表',
|
|
|
|
|
|
isKeepAlive: false,
|
|
|
|
|
|
isHide: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/blog/article/operate',
|
|
|
|
|
|
name: 'articleOperate',
|
|
|
|
|
|
component: 'blog/article/operate',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '新增文章',
|
|
|
|
|
|
isKeepAlive: false,
|
|
|
|
|
|
isHide: true,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
2026-09-06 22:36:26 +08:00
|
|
|
|
// 工作台业务页需要携带赛事上下文,不能依赖菜单接口是否已经下发全部子菜单。
|
|
|
|
|
|
// 这些路由只作为隐藏的运行路由注册,左侧菜单仍以后端权限菜单为准。
|
2026-09-09 14:25:44 +08:00
|
|
|
|
// 兼容已部署数据库里的旧菜单数据:旧版“秩序册”曾错误复用 launcher 路由。
|
|
|
|
|
|
// 前端按稳定的路由名称/菜单编码修正运行地址,避免用户必须等待菜单种子重新入库。
|
|
|
|
|
|
const normalizedRouteList = normalizeScoringMenuRoutes(routeList);
|
|
|
|
|
|
const routesWithScoringFallback = ensureScoringOperationalRoutes(normalizedRouteList);
|
2026-09-06 22:36:26 +08:00
|
|
|
|
dynamicRoutes[0].children = await backEndComponent([...routesWithScoringFallback, ...staticRoute]);
|
2025-12-17 10:38:15 +08:00
|
|
|
|
// dynamicRoutes[0].children = await backEndComponent(routeList);
|
|
|
|
|
|
// 添加动态路由
|
|
|
|
|
|
await setAddRoute();
|
|
|
|
|
|
// 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
|
|
|
|
|
|
setFilterMenuAndCacheTagsViewRoutes();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置路由到 pinia routesList 中(已处理成多级嵌套路由)及缓存多级嵌套数组处理后的一维数组
|
|
|
|
|
|
* @description 用于左侧菜单、横向菜单的显示
|
|
|
|
|
|
* @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function setFilterMenuAndCacheTagsViewRoutes() {
|
|
|
|
|
|
const storesRoutesList = useRoutesList(pinia);
|
|
|
|
|
|
storesRoutesList.setRoutesList(dynamicRoutes[0].children as any);
|
|
|
|
|
|
setCacheTagsViewRoutes();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 缓存多级嵌套数组处理后的一维数组
|
|
|
|
|
|
* @description 用于 tagsView、菜单搜索中:未过滤隐藏的(isHide)
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function setCacheTagsViewRoutes() {
|
|
|
|
|
|
const storesTagsView = useTagsViewRoutes(pinia);
|
|
|
|
|
|
storesTagsView.setTagsViewRoutes(formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes))[0].children);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理路由格式及添加捕获所有路由或 404 Not found 路由
|
|
|
|
|
|
* @description 替换 dynamicRoutes(/@/router/route)第一个顶级 children 的路由
|
|
|
|
|
|
* @returns 返回替换后的路由数组
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function setFilterRouteEnd() {
|
|
|
|
|
|
let filterRouteEnd: any = formatTwoStageRoutes(formatFlatteningRoutes(dynamicRoutes));
|
|
|
|
|
|
// notFoundAndNoPower 防止 404、401 不在 layout 布局中,不设置的话,404、401 界面将全屏显示
|
|
|
|
|
|
// 关联问题 No match found for location with path 'xxx'
|
|
|
|
|
|
filterRouteEnd[0].children = [...filterRouteEnd[0].children, ...notFoundAndNoPower];
|
|
|
|
|
|
return filterRouteEnd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 添加动态路由
|
|
|
|
|
|
* @method router.addRoute
|
|
|
|
|
|
* @description 此处循环为 dynamicRoutes(/@/router/route)第一个顶级 children 的路由一维数组,非多级嵌套
|
|
|
|
|
|
* @link 参考:https://next.router.vuejs.org/zh/api/#addroute
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function setAddRoute() {
|
|
|
|
|
|
await setFilterRouteEnd().forEach((route: RouteRecordRaw) => {
|
|
|
|
|
|
router.addRoute(route);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 请求后端路由菜单接口
|
|
|
|
|
|
* @description isRequestRoutes 为 true,则开启后端控制路由
|
|
|
|
|
|
* @returns 返回后端路由菜单数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function getBackEndControlRoutes() {
|
|
|
|
|
|
return SysMenuApi.getMenus();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-09 14:25:44 +08:00
|
|
|
|
function normalizeScoringMenuRoutes(routes: any[]) {
|
|
|
|
|
|
return routes.map((item) => {
|
|
|
|
|
|
const children = item?.children?.length ? normalizeScoringMenuRoutes(item.children) : item?.children;
|
|
|
|
|
|
const routeName = String(item?.name || item?.routeName || '');
|
|
|
|
|
|
const menuCode = String(item?.code || item?.meta?.code || '');
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const title = String(item?.meta?.title || item?.title || '');
|
2026-09-09 18:30:09 +08:00
|
|
|
|
const path = String(item?.path || '');
|
2026-09-10 16:28:27 +08:00
|
|
|
|
|
|
|
|
|
|
// 识别 launcher:
|
|
|
|
|
|
// 旧版种子中曾把 scoring:launcher 或 /scoring/launcher 误命名为“秩序册”;
|
|
|
|
|
|
// 无论其 title 叫“秩序册”、“工作台”还是“打分系统工作台”,只要编码/路由为 launcher,就规范为 launcher
|
|
|
|
|
|
const isLauncher =
|
|
|
|
|
|
routeName === 'scoringLauncher' ||
|
|
|
|
|
|
menuCode === 'scoring:launcher' ||
|
|
|
|
|
|
path === '/scoring/launcher' ||
|
|
|
|
|
|
((title === '工作台' || title === '计分工作台') && path.startsWith('/scoring'));
|
|
|
|
|
|
|
|
|
|
|
|
const isOrderBook =
|
|
|
|
|
|
!isLauncher &&
|
|
|
|
|
|
(routeName === 'scoringOrderBook' ||
|
|
|
|
|
|
menuCode === 'scoring:orderbook' ||
|
|
|
|
|
|
path === '/scoring/orderbook' ||
|
|
|
|
|
|
(title === '秩序册' && path.startsWith('/scoring')));
|
|
|
|
|
|
|
|
|
|
|
|
const isWorkbench =
|
|
|
|
|
|
routeName === 'scoringWorkbench' ||
|
|
|
|
|
|
menuCode === 'scoring:workbench' ||
|
|
|
|
|
|
path === '/scoring/workbench' ||
|
|
|
|
|
|
(title === '成绩管理' && path.startsWith('/scoring'));
|
|
|
|
|
|
|
|
|
|
|
|
const isScoringCast =
|
|
|
|
|
|
routeName === 'scoringCast' ||
|
|
|
|
|
|
menuCode === 'scoring:cast' ||
|
|
|
|
|
|
path === '/scoring/cast' ||
|
|
|
|
|
|
((title === '成绩投屏' || title === '赛事大屏') && path.startsWith('/scoring'));
|
|
|
|
|
|
|
2026-09-09 18:30:09 +08:00
|
|
|
|
if (isScoringCast) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
path: '/scoring/cast',
|
2026-09-10 16:28:27 +08:00
|
|
|
|
component: 'scoring/cast/index',
|
|
|
|
|
|
children,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isWorkbench) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
path: '/scoring/workbench',
|
|
|
|
|
|
component: 'scoring/workbench/index',
|
2026-09-09 18:30:09 +08:00
|
|
|
|
children,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2026-09-10 16:28:27 +08:00
|
|
|
|
if (isLauncher) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
path: '/scoring/launcher',
|
|
|
|
|
|
component: 'scoring/launcher/index',
|
|
|
|
|
|
children,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isOrderBook) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
path: '/scoring/orderbook',
|
|
|
|
|
|
component: 'scoring/orderbook/index',
|
|
|
|
|
|
children,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return children === item?.children ? item : { ...item, children };
|
2026-09-09 14:25:44 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-10 16:28:27 +08:00
|
|
|
|
function isScoringRelatedRoute(item: any): boolean {
|
|
|
|
|
|
if (!item) return false;
|
|
|
|
|
|
const path = String(item?.path || '');
|
|
|
|
|
|
const code = String(item?.code || item?.meta?.code || '');
|
|
|
|
|
|
const name = String(item?.name || item?.routeName || '');
|
|
|
|
|
|
const title = String(item?.meta?.title || item?.title || '');
|
|
|
|
|
|
return (
|
|
|
|
|
|
path === '/scoring' ||
|
|
|
|
|
|
path.startsWith('/scoring/') ||
|
|
|
|
|
|
code === 'scoring' ||
|
|
|
|
|
|
code.startsWith('scoring:') ||
|
|
|
|
|
|
name === 'scoring' ||
|
|
|
|
|
|
name.startsWith('scoring') ||
|
|
|
|
|
|
title.includes('打分系统工作台') ||
|
|
|
|
|
|
title.includes('计分工作台') ||
|
|
|
|
|
|
title === '工作台'
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-06 22:36:26 +08:00
|
|
|
|
function ensureScoringOperationalRoutes(routes: any[]) {
|
|
|
|
|
|
const paths = new Set<string>();
|
2026-09-10 16:28:27 +08:00
|
|
|
|
const collectPaths = (items: any[]) =>
|
|
|
|
|
|
items.forEach((item) => {
|
|
|
|
|
|
if (item?.path) paths.add(item.path);
|
|
|
|
|
|
if (item?.children?.length) collectPaths(item.children);
|
|
|
|
|
|
});
|
2026-09-06 22:36:26 +08:00
|
|
|
|
collectPaths(routes);
|
2026-09-10 16:28:27 +08:00
|
|
|
|
|
|
|
|
|
|
const scoringEnabled =
|
|
|
|
|
|
paths.has('/scoring') ||
|
|
|
|
|
|
paths.has('/scoring/launcher') ||
|
|
|
|
|
|
paths.has('/scoring/workbench') ||
|
|
|
|
|
|
paths.has('/scoring/orderbook') ||
|
|
|
|
|
|
paths.has('/scoring/cast') ||
|
|
|
|
|
|
routes.some((item) => isScoringRelatedRoute(item));
|
|
|
|
|
|
|
2026-09-06 22:36:26 +08:00
|
|
|
|
if (!scoringEnabled) return routes;
|
|
|
|
|
|
|
2026-09-10 16:28:27 +08:00
|
|
|
|
// 提取原有菜单中的图标(若有),优先使用
|
|
|
|
|
|
let scoringIcon = 'ele-Monitor';
|
|
|
|
|
|
const findScoringIcon = (items: any[]) => {
|
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
|
const icon = item?.meta?.icon || item?.icon;
|
|
|
|
|
|
if (isScoringRelatedRoute(item) && icon) {
|
|
|
|
|
|
scoringIcon = icon;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item?.children?.length) findScoringIcon(item.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
findScoringIcon(routes);
|
|
|
|
|
|
|
|
|
|
|
|
// 从原有树中清理所有打分系统相关节点,并记录首个打分节点的插入位置,彻底避免出现重复菜单或多余的秩序册
|
|
|
|
|
|
let firstScoringIndex = -1;
|
|
|
|
|
|
const cleanRoutes = (items: any[]): any[] => {
|
|
|
|
|
|
const result: any[] = [];
|
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
|
const item = items[i];
|
|
|
|
|
|
if (isScoringRelatedRoute(item)) {
|
|
|
|
|
|
if (firstScoringIndex === -1) {
|
|
|
|
|
|
firstScoringIndex = result.length;
|
|
|
|
|
|
}
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item?.children?.length) {
|
|
|
|
|
|
result.push({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
children: cleanRoutes(item.children),
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
result.push(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const routesWithoutScoring = cleanRoutes(routes);
|
|
|
|
|
|
if (firstScoringIndex === -1) firstScoringIndex = routesWithoutScoring.length;
|
|
|
|
|
|
|
|
|
|
|
|
// 唯一的打分系统工作台父级目录及其 4 个标准业务子菜单:
|
|
|
|
|
|
// 1. 打分系统工作台:选择计分模式和赛事
|
|
|
|
|
|
// 2. 秩序册:秩序册导入
|
|
|
|
|
|
// 3. 成绩管理:出场名单、打分单与更正
|
|
|
|
|
|
// 4. 成绩投屏:大屏投屏操作台
|
|
|
|
|
|
const scoringParent = {
|
|
|
|
|
|
path: '/scoring',
|
|
|
|
|
|
name: 'scoring',
|
|
|
|
|
|
component: 'layout/routerView/parent',
|
|
|
|
|
|
redirect: '/scoring/launcher',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '打分系统工作台',
|
|
|
|
|
|
isHide: false,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
icon: scoringIcon || 'ele-Trophy',
|
|
|
|
|
|
},
|
|
|
|
|
|
children: [
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/scoring/launcher',
|
|
|
|
|
|
name: 'scoringLauncher',
|
|
|
|
|
|
component: 'scoring/launcher/index',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '打分系统工作台',
|
|
|
|
|
|
isHide: false,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
icon: 'ele-Monitor',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/scoring/orderbook',
|
|
|
|
|
|
name: 'scoringOrderBook',
|
|
|
|
|
|
component: 'scoring/orderbook/index',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '秩序册',
|
|
|
|
|
|
isHide: false,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
icon: 'ele-Notebook',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/scoring/workbench',
|
|
|
|
|
|
name: 'scoringWorkbench',
|
|
|
|
|
|
component: 'scoring/workbench/index',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '成绩管理',
|
|
|
|
|
|
isHide: false,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
icon: 'ele-Management',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
path: '/scoring/cast',
|
|
|
|
|
|
name: 'scoringCast',
|
|
|
|
|
|
component: 'scoring/cast/index',
|
|
|
|
|
|
meta: {
|
|
|
|
|
|
title: '成绩投屏',
|
|
|
|
|
|
isHide: false,
|
|
|
|
|
|
isKeepAlive: true,
|
|
|
|
|
|
icon: 'ele-DataLine',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
routesWithoutScoring.splice(firstScoringIndex, 0, scoringParent);
|
|
|
|
|
|
return routesWithoutScoring;
|
2026-09-06 22:36:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-17 10:38:15 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 重新请求后端路由菜单接口
|
|
|
|
|
|
* @description 用于菜单管理界面刷新菜单(未进行测试)
|
|
|
|
|
|
* @description 路径:/src/views/system/menu/component/addMenu.vue
|
|
|
|
|
|
*/
|
|
|
|
|
|
export async function setBackEndControlRefreshRoutes() {
|
|
|
|
|
|
await getBackEndControlRoutes();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 后端路由 component 转换
|
|
|
|
|
|
* @param routes 后端返回的路由表数组
|
|
|
|
|
|
* @returns 返回处理成函数后的 component
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function backEndComponent(routes: any) {
|
|
|
|
|
|
if (!routes) return;
|
|
|
|
|
|
return routes.map((item: any) => {
|
|
|
|
|
|
if (item.component === '') {
|
|
|
|
|
|
item.component = 'layout/routerView/parent';
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.component) item.component = dynamicImport(dynamicViewsModules, item.component as string);
|
|
|
|
|
|
item.children && item.children.length > 0 && backEndComponent(item.children);
|
|
|
|
|
|
return item;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 后端路由 component 转换函数
|
|
|
|
|
|
* @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件
|
|
|
|
|
|
* @param component 当前要处理项 component
|
|
|
|
|
|
* @returns 返回处理成函数后的 component
|
|
|
|
|
|
*/
|
|
|
|
|
|
export function dynamicImport(dynamicViewsModules: Record<string, Function>, component: string) {
|
|
|
|
|
|
const keys = Object.keys(dynamicViewsModules);
|
|
|
|
|
|
const matchKeys = keys.filter((key) => {
|
|
|
|
|
|
const k = key.replace(/..\/views|../, '');
|
|
|
|
|
|
return k.startsWith(`${component}`) || k.startsWith(`/${component}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
if (matchKeys?.length === 1) {
|
|
|
|
|
|
const matchKey = matchKeys[0];
|
|
|
|
|
|
return dynamicViewsModules[matchKey];
|
|
|
|
|
|
}
|
|
|
|
|
|
if (matchKeys?.length > 1) {
|
2026-09-09 14:10:46 +08:00
|
|
|
|
const exactKey = matchKeys.find((key) => {
|
|
|
|
|
|
const k = key.replace(/..\/views|../, '');
|
|
|
|
|
|
return k === `${component}.vue` || k === `/${component}.vue` || k === `${component}.tsx` || k === `/${component}.tsx`;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (exactKey) return dynamicViewsModules[exactKey];
|
2025-12-17 10:38:15 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-09 14:10:46 +08:00
|
|
|
|
|