Files
tms-erp-web/src/router/avue-router.js
T
2026-09-18 18:25:52 +08:00

238 lines
8.0 KiB
JavaScript
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 website from '@/config/website';
import { getToken } from '@/utils/auth';
import store from '@/store';
import { generateIframePath, processUrlForQuery, isURL } from './router';
import { wrapViewLoader } from '@/utils/chunk-reload';
// 保持懒加载,避免 eager 与 store 循环依赖(Cannot access 'store' before initialization
const modules = import.meta.glob('../**/**/*.vue');
// 将多级路由扁平化为二级路由,支持 keep-alive 跨层级缓存
function flattenRouteChildren(children) {
const result = [];
for (const child of children) {
if (child.children && child.children.length > 0) {
result.push(...flattenRouteChildren(child.children));
} else {
result.push(child);
}
}
return result;
}
let RouterPlugin = function () {
this.$router = null;
this.$store = null;
};
RouterPlugin.install = function (option = {}) {
this.$router = option.router;
this.$store = option.store;
let i18n = option.i18n.global;
this.$router.$avueRouter = {
safe: this,
// 设置标题
setTitle: title => {
const defaultTitle = i18n.t('title');
title = title ? `${title} | ${defaultTitle}` : defaultTitle;
document.title = title;
},
closeTag: value => {
let tag = value || this.$store.getters.tag;
if (typeof value === 'string') {
tag = this.$store.getters.tagList.find(ele => ele.fullPath === value);
}
this.$store.commit('DEL_TAG', tag);
},
generateTitle: (item, props = {}) => {
let query = item[props.query || 'query'] || {};
let title = query.name || item[props.label || 'label'];
let meta = item[props.meta || 'meta'] || {};
let key = meta.i18n;
if (key) {
const hasKey = i18n.te('route.' + key);
if (hasKey) return i18n.t('route.' + key);
}
return title ? title.split(',')[0] : title;
},
//动态路由
formatRoutes: function (aMenu = [], first) {
const aRouter = [];
const propsDefault = website.menu;
if (aMenu && aMenu.length === 0) return;
for (let i = 0; i < aMenu.length; i++) {
const oMenu = aMenu[i];
let path = oMenu[propsDefault.path],
isComponent = true,
component = oMenu.component,
name = oMenu[propsDefault.label] + ',' + oMenu.id,
icon = oMenu[propsDefault.icon],
children = oMenu[propsDefault.children],
query = oMenu[propsDefault.query],
meta = oMenu[propsDefault.meta];
// 处理iframe场景,如果path是URL且是一级路由,需要生成一个内部路径
const isUrlPath = isURL(path);
if (isUrlPath && first && !children?.length) {
// 保存原始URL到query中(如果formatPath还没处理)
if (!query || !query.url) {
query = { url: processUrlForQuery(path) };
}
// 生成一个内部路径用于路由
const generatedPath = generateIframePath(oMenu, propsDefault.path);
if (generatedPath !== path) {
path = generatedPath;
oMenu[propsDefault.path] = path;
}
// 确保使用iframe组件
if (!component || component.indexOf('iframe') === -1) {
component = 'components/iframe/main';
oMenu.component = component;
}
}
if (option.keepAlive) {
meta.keepAlive = option.keepAlive;
}
const isChild = !!(children && children.length !== 0);
const oRouter = {
path: path,
component: (() => {
// 判断是否为首路由
if (first) {
return wrapViewLoader(
modules[
option.store.getters.isMacOs || !website.setting.menu
? '../page/index/layout.vue'
: '../page/index/index.vue'
]
);
// 判断是否为多层路由
} else if (isChild && !first) {
return wrapViewLoader(modules['../page/index/layout.vue']);
// 判断是否为最终的页面视图
} else {
let result = modules[`../${component}.vue`];
if (!result) {
isComponent = false;
}
return wrapViewLoader(result);
}
})(),
name,
icon,
meta,
query,
redirect: (() => {
if (!isChild && first) return `${path}`;
else return '';
})(),
// 处理是否为一级路由
children: !isChild
? (() => {
if (first) {
oMenu[propsDefault.path] = `${path}`;
let componentPath = oMenu.component || component;
let result = wrapViewLoader(modules[`../${componentPath}.vue`]);
if (!result) {
isComponent = false;
}
let childName = name + '_index';
let childQuery = oMenu[propsDefault.query] || query;
return [
{
component: result,
icon: icon,
name: childName,
meta: meta,
query: childQuery,
path: '',
},
];
}
return [];
})()
: (() => {
return this.formatRoutes(children, false);
})(),
};
const isIframeRoute =
component === 'components/iframe/main' || oMenu.component === 'components/iframe/main';
if ((!isURL(path) || isIframeRoute) && isComponent) aRouter.push(oRouter);
}
if (first) {
aRouter.forEach(ele => {
// 将三级及更深路由扁平化为二级,确保所有叶子组件都由 index.vue 的 keep-alive 统一管理
if (ele.children && ele.children.length > 0) {
ele.children = flattenRouteChildren(ele.children);
}
this.safe.$router.addRoute(ele);
});
} else {
return aRouter;
}
},
};
};
export const formatPath = (ele, first) => {
const propsDefault = website.menu;
const icon = ele[propsDefault.icon];
ele[propsDefault.icon] = icon || '';
ele.meta = {
keepAlive: true,
};
const iframeComponent = 'components/iframe/main';
const iframeSrc = href => {
// 替换&为#
let processedHref = href.replace(/&/g, '#');
// 获取用户信息
const userInfo = store.getters.userInfo || {};
const userToken = getToken() || '';
// 定义替换参数映射
const replacements = {
token: userToken,
userId: userInfo.userId || '',
userName: userInfo.userName || '',
roleName: userInfo.roleName || '',
};
// 统一替换所有参数
Object.entries(replacements).forEach(([key, value]) => {
const pattern = new RegExp(`\\$\\{${key}\\}`, 'g');
processedHref = processedHref.replace(pattern, value);
});
return processedHref;
};
const isChild = !!(ele[propsDefault.children] && ele[propsDefault.children].length !== 0);
if (!isChild && first) {
ele.component = 'views' + ele[propsDefault.path];
if (isURL(ele[propsDefault.href])) {
let href = ele[propsDefault.href];
ele.component = iframeComponent;
ele[propsDefault.query] = {
url: iframeSrc(href),
};
}
} else {
ele[propsDefault.children] &&
ele[propsDefault.children].forEach(child => {
child.component = 'views' + child[propsDefault.path];
child.meta = {
keepAlive: true,
};
if (isURL(child[propsDefault.href])) {
let href = child[propsDefault.href];
child[propsDefault.path] = ele[propsDefault.path] + '/' + child.code;
child.component = iframeComponent;
child[propsDefault.query] = {
url: iframeSrc(href),
};
}
formatPath(child);
});
}
};
export default RouterPlugin;