From c554c4c0125845e61adc11f9c133fbb3ab58b5db Mon Sep 17 00:00:00 2001 From: liaozhiyang Date: Tue, 17 Mar 2026 10:18:50 +0800 Subject: [PATCH] =?UTF-8?q?@jeecg/online=E5=92=8C@jeecg/aiflow=E5=BA=93?= =?UTF-8?q?=E6=8C=89=E9=9C=80=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Form/src/componentMap.ts | 10 +- .../src/components/Form/src/hooks/useForm.ts | 9 -- .../src/router/helper/routeHelper.ts | 7 ++ .../src/utils/monorepo/registerPackages.ts | 107 ++++++++++++++++-- 4 files changed, 112 insertions(+), 21 deletions(-) diff --git a/jeecgboot-vue3/src/components/Form/src/componentMap.ts b/jeecgboot-vue3/src/components/Form/src/componentMap.ts index a53487b6c..58f8e4a35 100644 --- a/jeecgboot-vue3/src/components/Form/src/componentMap.ts +++ b/jeecgboot-vue3/src/components/Form/src/componentMap.ts @@ -179,7 +179,15 @@ componentMap.set('JInputSelect', JInputSelect); componentMap.set('JSelectDepartPost', JSelectDepartPost); componentMap.set('JSelectUserByDeptPost', JSelectUserByDeptPost); - +componentMap.set('OnlineSelectCascade', createAsyncComponent(() => { + return import('@jeecg/online').then(mod => mod.OnlineSelectCascade); +})); +componentMap.set('LinkTableCard', createAsyncComponent(() => { + return import('@jeecg/online').then(mod => mod.LinkTableCard); +})); +componentMap.set('LinkTableSelect', createAsyncComponent(() => { + return import('@jeecg/online').then(mod => mod.LinkTableSelect); +})); export function add(compName: ComponentType, component: Component) { componentMap.set(compName, component); diff --git a/jeecgboot-vue3/src/components/Form/src/hooks/useForm.ts b/jeecgboot-vue3/src/components/Form/src/hooks/useForm.ts index 705de2b74..a260be8b9 100644 --- a/jeecgboot-vue3/src/components/Form/src/hooks/useForm.ts +++ b/jeecgboot-vue3/src/components/Form/src/hooks/useForm.ts @@ -6,10 +6,6 @@ import { ref, onUnmounted, unref, nextTick, watch } from 'vue'; import { isProdMode } from '/@/utils/env'; import { error } from '/@/utils/log'; import { getDynamicProps, getValueType, getValueTypeBySchema } from '/@/utils'; -import { add } from "/@/components/Form/src/componentMap"; -//集成online专用控件 -import { OnlineSelectCascade, LinkTableCard, LinkTableSelect } from '@jeecg/online'; - export declare type ValidateFields = (nameList?: NamePath[], options?: ValidateOptions) => Promise; type Props = Partial>; @@ -18,11 +14,6 @@ export function useForm(props?: Props): UseFormReturnType { const formRef = ref>(null); const loadedRef = ref>(false); - //集成online专用控件 - add("OnlineSelectCascade", OnlineSelectCascade) - add("LinkTableCard", LinkTableCard) - add("LinkTableSelect", LinkTableSelect) - async function getForm() { const form = unref(formRef); if (!form) { diff --git a/jeecgboot-vue3/src/router/helper/routeHelper.ts b/jeecgboot-vue3/src/router/helper/routeHelper.ts index 9e4980637..2fa33dc18 100644 --- a/jeecgboot-vue3/src/router/helper/routeHelper.ts +++ b/jeecgboot-vue3/src/router/helper/routeHelper.ts @@ -9,6 +9,7 @@ import { getTenantId, getToken } from "/@/utils/auth"; import { URL_HASH_TAB, _eval } from '/@/utils'; //引入online lib路由 import { packageViews } from '/@/utils/monorepo/dynamicRouter'; +import { loadPackageComponent } from '/@/utils/monorepo/registerPackages'; import {useI18n} from "/@/hooks/web/useI18n"; export type LayoutMapKey = 'LAYOUT'; @@ -120,6 +121,12 @@ function dynamicImport(dynamicViewsModules: Record Promise { + return loadPackageComponent(component).then((factory) => (factory ? factory() : Promise.reject(`组件 ${component} 未找到`))); + }; + } } // Turn background objects into routing objects diff --git a/jeecgboot-vue3/src/utils/monorepo/registerPackages.ts b/jeecgboot-vue3/src/utils/monorepo/registerPackages.ts index 435bbcfa5..efeaa739c 100644 --- a/jeecgboot-vue3/src/utils/monorepo/registerPackages.ts +++ b/jeecgboot-vue3/src/utils/monorepo/registerPackages.ts @@ -1,24 +1,109 @@ import type { App } from 'vue'; import { warn } from '/@/utils/log'; import { registerDynamicRouter } from '/@/utils/monorepo/dynamicRouter'; -// 引入模块 -import PACKAGE_JEECG_ONLINE from '@jeecg/online'; -import PACKAGE_JEECG_AIFLOW from '@jeecg/aiflow'; +import { add } from '/@/components/Form/src/componentMap'; -export function registerPackages(app: App) { - use(app, PACKAGE_JEECG_ONLINE); - use(app, PACKAGE_JEECG_AIFLOW); -} +// 懒加载模块配置(按需加载,访问相关路由时才加载对应包) +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, }; -/** 注册模块 */ -function use(app: App, pkg) { - app.use(pkg, installOptions); - registerDynamicRouter(pkg.getViews); +export function registerPackages(app: App) { + // 仅保存 app 实例,不立即加载模块 + appInstance = app; +} + +/** 已加载的包缓存 */ +const loadedPackages = new Map(); +/** 正在加载的包 Promise 缓存(防止重复加载) */ +const loadingPromises = new Map>(); + +/** + * 按需加载包并注册 + */ +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) | 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) | 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