From 520180f6f59ad21bd8a9700c622f4e97746c0d7c Mon Sep 17 00:00:00 2001 From: jxxghp Date: Tue, 6 May 2025 11:44:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A8=A1=E5=9D=97=E8=81=94?= =?UTF-8?q?=E9=82=A6=E6=96=87=E6=A1=A3=EF=BC=8C=E8=B0=83=E6=95=B4=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E7=BB=84=E4=BB=B6API=E8=B7=AF=E5=BE=84=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E4=BC=98=E5=8C=96=E7=BB=84=E4=BB=B6=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A7=BB=E9=99=A4=E4=B8=8D?= =?UTF-8?q?=E5=BF=85=E8=A6=81=E7=9A=84=E6=B3=A8=E5=86=8C=E6=AD=A5=E9=AA=A4?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=BC=BA=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/module-federation-guide.md | 8 +++--- src/api/index.ts | 14 --------- src/components/dialog/PluginConfigDialog.vue | 30 ++++---------------- src/components/dialog/PluginDataDialog.vue | 28 +++--------------- src/components/misc/DashboardElement.vue | 18 ++---------- src/main.ts | 2 +- src/utils/federationLoader.ts | 18 +++++------- src/utils/globalSetting.ts | 14 +++++++++ 8 files changed, 38 insertions(+), 94 deletions(-) create mode 100644 src/utils/globalSetting.ts diff --git a/docs/module-federation-guide.md b/docs/module-federation-guide.md index fa7db907..65ae7b78 100644 --- a/docs/module-federation-guide.md +++ b/docs/module-federation-guide.md @@ -194,10 +194,10 @@ npm run build 这些文件需要部署到后端,并通过以下URL可访问: -- `/api/plugin/component/{插件ID}/remoteEntry.js` -- `/api/plugin/component/{插件ID}/Page.js` -- `/api/plugin/component/{插件ID}/Config.js` -- `/api/plugin/component/{插件ID}/Dashboard.js` +- `/api/v1/plugin/file/{插件ID}/dist/remoteEntry.js` +- `/api/v1/plugin/file/{插件ID}/dist/Page.js` +- `/api/v1/plugin/file/{插件ID}/dist/Config.js` +- `/api/v1/plugin/file/{插件ID}/dist/Dashboard.js` ## 7. 后端API要求 diff --git a/src/api/index.ts b/src/api/index.ts index 3e41c876..b3141e67 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -51,17 +51,3 @@ api.interceptors.response.use( ) export default api - -export async function fetchGlobalSettings() { - try { - const result: { [key: string]: any } = await api.get('system/global', { - params: { - token: 'moviepilot', - }, - }) - return result.data || {} - } catch (error) { - console.error('Failed to fetch global settings', error) - throw error - } -} diff --git a/src/components/dialog/PluginConfigDialog.vue b/src/components/dialog/PluginConfigDialog.vue index a19ef27c..a0e7ce70 100644 --- a/src/components/dialog/PluginConfigDialog.vue +++ b/src/components/dialog/PluginConfigDialog.vue @@ -8,13 +8,7 @@ import FormRender from '../render/FormRender.vue' import ProgressDialog from '../dialog/ProgressDialog.vue' import { useI18n } from 'vue-i18n' import { defineAsyncComponent } from 'vue' -import { - loadRemoteComponent, - clearRemoteComponentCache, - registerRemotePlugin, - isRemoteComponentLoaded, - ComponentType, -} from '@/utils/federationLoader' +import { loadRemoteComponent, clearRemoteComponentCache, ComponentType } from '@/utils/federationLoader' // 国际化 const { t } = useI18n() @@ -64,14 +58,8 @@ const dynamicComponent = defineAsyncComponent({ } try { - componentMounted.value = false - - // 确保插件已注册 - if (!isRemoteComponentLoaded(props.plugin.id, ComponentType.CONFIG)) { - await registerRemotePlugin(props.plugin.id) - } - // 加载配置组件 + componentMounted.value = false const component = await loadRemoteComponent(props.plugin.id, ComponentType.CONFIG) componentMounted.value = true @@ -82,7 +70,6 @@ const dynamicComponent = defineAsyncComponent({ return component } catch (error: any) { console.error(`加载插件配置组件失败: ${props.plugin.id}`, error) - $toast.error(`加载插件组件失败: ${error.message || '未知错误'}`) return { render: () => h('div', { class: 'text-error pa-4' }, `加载失败: ${error.message || '未知错误'}`), } @@ -91,7 +78,7 @@ const dynamicComponent = defineAsyncComponent({ loadingComponent: { render: () => h('div', { class: 'text-center pa-4' }, [ - h('v-progress-circular', { indeterminate: true, class: 'mr-2' }), + h('VProgressCircular', { indeterminate: true, class: 'mr-2' }), '加载组件中...', ]), }, @@ -110,7 +97,6 @@ async function loadPluginUIData() { pluginFormItems = [] pluginConfigForm.value = {} renderMode.value = 'vuetify' - componentMounted.value = false // 清除组件缓存 if (props.plugin?.id) { @@ -126,11 +112,6 @@ async function loadPluginUIData() { } renderMode.value = result.render_mode if (renderMode.value === 'vue') { - // 注册远程插件 (如果提供了组件URL,则使用它) - if (props.plugin?.id) { - registerRemotePlugin(props.plugin.id, result.component_url) - } - // Vue模式下,初始配置在同一个API返回 if (!isNullOrEmptyObject(result.model)) { pluginConfigForm.value = result.model @@ -191,7 +172,8 @@ onUnmounted(() => { - + +
@@ -201,8 +183,6 @@ onUnmounted(() => {
- -
加载中...
diff --git a/src/components/dialog/PluginDataDialog.vue b/src/components/dialog/PluginDataDialog.vue index 61d32cbb..d3662429 100644 --- a/src/components/dialog/PluginDataDialog.vue +++ b/src/components/dialog/PluginDataDialog.vue @@ -5,13 +5,7 @@ import PageRender from '@/components/render/PageRender.vue' import api from '@/api' import { useToast } from 'vue-toast-notification' import { defineAsyncComponent } from 'vue' -import { - loadRemoteComponent, - clearRemoteComponentCache, - registerRemotePlugin, - isRemoteComponentLoaded, - ComponentType, -} from '@/utils/federationLoader' +import { loadRemoteComponent, clearRemoteComponentCache, ComponentType } from '@/utils/federationLoader' // 输入参数 const props = defineProps({ @@ -51,14 +45,8 @@ const dynamicComponent = defineAsyncComponent({ } try { - componentMounted.value = false - - // 确保插件已注册 - if (!isRemoteComponentLoaded(props.plugin.id, ComponentType.PAGE)) { - await registerRemotePlugin(props.plugin.id) - } - // 加载页面组件 + componentMounted.value = false const component = await loadRemoteComponent(props.plugin.id, ComponentType.PAGE) componentMounted.value = true @@ -69,7 +57,6 @@ const dynamicComponent = defineAsyncComponent({ return component } catch (error: any) { console.error(`加载插件页面组件失败: ${props.plugin.id}`, error) - $toast.error(`加载插件组件失败: ${error.message || '未知错误'}`) return { render: () => h('div', { class: 'text-error pa-4' }, `加载失败: ${error.message || '未知错误'}`), } @@ -78,7 +65,7 @@ const dynamicComponent = defineAsyncComponent({ loadingComponent: { render: () => h('div', { class: 'text-center pa-4' }, [ - h('v-progress-circular', { indeterminate: true, class: 'mr-2' }), + h('VProgressCircular', { indeterminate: true, class: 'mr-2' }), '加载组件中...', ]), }, @@ -95,7 +82,6 @@ async function loadPluginUIData() { isRefreshed.value = false pluginPageItems.value = [] renderMode.value = 'vuetify' - componentMounted.value = false // 清除组件缓存 if (props.plugin?.id) { @@ -108,14 +94,8 @@ async function loadPluginUIData() { console.error(`插件 ${props.plugin?.plugin_name} UI数据加载失败:无效的响应`) return } - renderMode.value = result.render_mode - if (renderMode.value === 'vue') { - // 注册远程插件 (如果提供了组件URL,则使用它) - if (props.plugin?.id) { - registerRemotePlugin(props.plugin.id, result.component_url) - } - } else { + if (renderMode.value === 'vuetify') { // Vuetify模式 pluginPageItems.value = result.page || [] } diff --git a/src/components/misc/DashboardElement.vue b/src/components/misc/DashboardElement.vue index 21408552..8093f948 100644 --- a/src/components/misc/DashboardElement.vue +++ b/src/components/misc/DashboardElement.vue @@ -13,13 +13,7 @@ import MediaServerPlaying from '@/views/dashboard/MediaServerPlaying.vue' import DashboardRender from '@/components/render/DashboardRender.vue' import { isNullOrEmptyObject } from '@/@core/utils' import { defineAsyncComponent } from 'vue' -import { - loadRemoteComponent, - clearRemoteComponentCache, - registerRemotePlugin, - isRemoteComponentLoaded, - ComponentType, -} from '@/utils/federationLoader' +import { loadRemoteComponent, clearRemoteComponentCache, ComponentType } from '@/utils/federationLoader' // 输入参数 const props = defineProps({ @@ -50,14 +44,8 @@ const dynamicPluginComponent = defineAsyncComponent({ } try { - componentMounted.value = false - - // 确保插件已注册 - if (!isRemoteComponentLoaded(props.config.id, ComponentType.DASHBOARD)) { - await registerRemotePlugin(props.config.id, props.config.component_url) - } - // 加载仪表板组件 + componentMounted.value = false const component = await loadRemoteComponent(props.config.id, ComponentType.DASHBOARD) componentMounted.value = true @@ -76,7 +64,7 @@ const dynamicPluginComponent = defineAsyncComponent({ loadingComponent: { render: () => h('div', { class: 'text-center pa-4' }, [ - h('v-progress-circular', { indeterminate: true, class: 'mr-2' }), + h('VProgressCircular', { indeterminate: true, class: 'mr-2' }), '加载组件中...', ]), }, diff --git a/src/main.ts b/src/main.ts index 1eb79670..47e86147 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,9 +18,9 @@ import { PerfectScrollbarPlugin } from 'vue3-perfect-scrollbar' import { CronVuetify } from '@vue-js-cron/vuetify' // 4. 工具函数和其他辅助模块 -import { fetchGlobalSettings } from './api' import { isPWA } from './@core/utils/navigator' import { loadRemoteComponents } from './utils/federationLoader' +import { fetchGlobalSettings } from './utils/globalSetting' // 5. 其他插件和功能模块 import ToastPlugin from 'vue-toast-notification' diff --git a/src/utils/federationLoader.ts b/src/utils/federationLoader.ts index b1ed8d4e..2b6c28c4 100644 --- a/src/utils/federationLoader.ts +++ b/src/utils/federationLoader.ts @@ -54,13 +54,12 @@ async function loadRemoteEntry(url: string): Promise { return new Promise((resolve, reject) => { // 创建script标签 const script = document.createElement('script') - script.src = url + script.src = `${import.meta.env.VITE_API_BASE_URL}${url}` script.type = 'text/javascript' script.async = true // 加载成功 script.onload = () => { - console.log(`远程模块加载成功: ${url}`) resolve() } @@ -84,11 +83,11 @@ async function loadRemoteEntry(url: string): Promise { function getRemoteEntryUrl(pluginId: string, url?: string): string { // 如果提供了完整URL则直接使用 if (url && (url.startsWith('http://') || url.startsWith('https://') || url.startsWith('/'))) { - return url.endsWith('/remoteEntry.js') ? url : `${url}/remoteEntry.js` + return url } // 否则使用默认路径格式 - return `/api/plugin/component/${pluginId}/remoteEntry.js` + return `/api/v1/plugin/file/${pluginId.toLowerCase()}/dist/remoteEntry.js` } /** @@ -116,7 +115,7 @@ function getComponentModule(componentType: ComponentType): string { export async function loadRemoteComponents(): Promise { try { // 调用后端API获取远程组件列表 - const result = await api.get('plugins/remotes') + const result = await api.get('plugin/remotes?token=moviepilot') if (!result || !Array.isArray(result)) { console.error('获取远程组件列表失败:无效的响应格式') @@ -175,16 +174,16 @@ export async function registerRemotePlugin(pluginId: string, url?: string): Prom // 动态导入远程模块 return async () => { // 这里可能需要根据实际情况调整 + const moduleName = module.replace('./', '') try { // 理论上这里应该使用模块联邦的import机制 // 但由于我们是运行时加载,需要用一种变通方式 - const moduleUrl = `${remoteEntryUrl.replace('remoteEntry.js', '')}${module.replace('./', '')}.js` - + const moduleUrl = `${remoteEntryUrl.replace('remoteEntry.js', '')}${moduleName}.js` // 使用动态导入 const moduleExports = await import(/* @vite-ignore */ moduleUrl) return { default: moduleExports.default } } catch (error) { - console.error(`加载远程模块失败: ${remoteId}/${module}`, error) + console.error(`加载远程模块失败: ${remoteId}/${moduleName}`, error) throw error } } @@ -254,8 +253,6 @@ export async function loadRemoteComponent(pluginId: string, componentType: Compo error: error, } } - - console.error(`加载远程组件失败: ${pluginId}/${componentType}`, error) return null } } @@ -281,7 +278,6 @@ async function loadRemoteComponentModule(pluginId: string, componentType: Compon // 返回组件 return Module.default } catch (error) { - console.error(`加载远程组件模块失败: ${remoteId}/${moduleName}`, error) throw error } } diff --git a/src/utils/globalSetting.ts b/src/utils/globalSetting.ts new file mode 100644 index 00000000..409fc465 --- /dev/null +++ b/src/utils/globalSetting.ts @@ -0,0 +1,14 @@ +import api from '@/api' +export async function fetchGlobalSettings() { + try { + const result: { [key: string]: any } = await api.get('system/global', { + params: { + token: 'moviepilot', + }, + }) + return result.data || {} + } catch (error) { + console.error('Failed to fetch global settings', error) + throw error + } +}