JeecgBoot/jeecgboot-vue3/src/utils/monorepo/registerPackages.ts
2026-03-17 10:18:50 +08:00

135 lines
4.1 KiB
Vue
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 { App } from 'vue';
import { warn } from '/@/utils/log';
import { registerDynamicRouter } from '/@/utils/monorepo/dynamicRouter';
import { add } from '/@/components/Form/src/componentMap';
// 懒加载模块配置(按需加载,访问相关路由时才加载对应包)
const lazyPackages = [
{ name: '@jeecg/online', importer: () => import('@jeecg/online') },
{ name: '@jeecg/aiflow', importer: () => import('@jeecg/aiflow') },
];
let appInstance: App | null = null;
// noinspection JSUnusedGlobalSymbols
const installOptions = {
baseImport,
};
export function registerPackages(app: App) {
// 仅保存 app 实例,不立即加载模块
appInstance = app;
}
/** 已加载的包缓存 */
const loadedPackages = new Map<string, any>();
/** 正在加载的包 Promise 缓存(防止重复加载) */
const loadingPromises = new Map<string, Promise<any>>();
/**
* 按需加载包并注册
*/
async function ensurePackageLoaded(pkgConfig: typeof lazyPackages[number]) {
const { name, importer } = pkgConfig;
if (loadedPackages.has(name)) {
return loadedPackages.get(name);
}
if (!loadingPromises.has(name)) {
const promise = importer().then((pkg) => {
const mod = pkg.default || pkg;
if (appInstance) {
appInstance.use(mod, installOptions);
registerDynamicRouter(mod.getViews);
}
loadedPackages.set(name, mod);
loadingPromises.delete(name);
return mod;
});
loadingPromises.set(name, promise);
}
return loadingPromises.get(name);
}
/**
* 根据 component 路径关键字匹配优先加载的包
*/
function getMatchedPackage(component: string): typeof lazyPackages[number] | null {
const lc = component.toLowerCase();
for (const pkgConfig of lazyPackages) {
// 从包名中提取关键字,如 @jeecg/online -> online, @jeecg/aiflow -> aiflow
const keyword = pkgConfig.name.split('/').pop()!;
if (lc.includes(keyword)) {
return pkgConfig;
}
}
return null;
}
/**
* 从指定包中查找组件
*/
async function findComponentInPackage(pkgConfig: typeof lazyPackages[number], component: string): Promise<(() => Promise<Recordable>) | null> {
try {
const mod = await ensurePackageLoaded(pkgConfig);
const views = mod.getViews();
for (const key of Object.keys(views)) {
const k = key.replace('./src/views', '');
const startFlag = component.startsWith('/');
const endFlag = component.endsWith('.vue') || component.endsWith('.tsx');
const startIndex = startFlag ? 0 : 1;
const lastIndex = endFlag ? k.length : k.lastIndexOf('.');
if (k.substring(startIndex, lastIndex) === component) {
return views[key];
}
}
} catch (e) {
// 包不存在或加载失败,跳过
}
return null;
}
/**
* 按需加载包组件:当路由匹配不到本地组件时调用
* 根据 component 路径中的关键字优先匹配对应包,避免无意义的遍历
*/
export async function loadPackageComponent(component: string): Promise<(() => Promise<Recordable>) | null> {
// 优先根据关键字精准匹配包
const matched = getMatchedPackage(component);
if (matched) {
return findComponentInPackage(matched, component);
}
// 未匹配到关键字,依次尝试所有包
for (const pkgConfig of lazyPackages) {
const result = await findComponentInPackage(pkgConfig, component);
if (result) return result;
}
return null;
}
// 模块里可使用的import
const importGlobs = [import.meta.glob('../../utils/**/*.{ts,js,tsx}'), import.meta.glob('../../hooks/**/*.{ts,js,tsx}')];
/**
* 基础项目导包
* 目前支持导入如下
* /@/utils/**
* /@/hooks/**
*
* @param path 文件路径ts无需输入后缀名。如/@/utils/common/compUtils
*/
async function baseImport(path: string) {
if (path) {
// 将 /@/ 替换成 ../../
path = path.replace(/^\/@\//, '../../');
for (const glob of importGlobs) {
for (const key of Object.keys(glob)) {
if (path === key || `${path}.ts` === key || `${path}.tsx` === key) {
return glob[key]();
}
}
}
warn(`引入失败:${path} 不存在`);
}
return null;
}