diff --git a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java index e1ab070fd..817ea1d05 100644 --- a/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java +++ b/application/src/main/java/run/halo/app/core/extension/service/impl/PluginServiceImpl.java @@ -230,10 +230,16 @@ public class PluginServiceImpl implements PluginService, InitializingBean, Dispo public Flux uglifyJsBundle() { var startedPlugins = List.copyOf(pluginManager.getStartedPlugins()); String plugins = """ - this.enabledPluginNames = [%s]; + this.enabledPlugins = [%s] """.formatted(startedPlugins.stream() - .map(PluginWrapper::getPluginId) - .collect(Collectors.joining("','", "'", "'"))); + .map(plugin -> """ + { + "name": "%s", + "version": "%s" + } + """.formatted(plugin.getPluginId(), plugin.getDescriptor().getVersion()) + ) + .collect(Collectors.joining(", "))); return Flux.fromIterable(startedPlugins) .mapNotNull(pluginWrapper -> { var pluginName = pluginWrapper.getPluginId(); diff --git a/ui/console-src/modules/system/plugins/components/PluginListItem.vue b/ui/console-src/modules/system/plugins/components/PluginListItem.vue index fa0737748..b0147e192 100644 --- a/ui/console-src/modules/system/plugins/components/PluginListItem.vue +++ b/ui/console-src/modules/system/plugins/components/PluginListItem.vue @@ -29,6 +29,7 @@ import { useI18n } from "vue-i18n"; import { useRouter } from "vue-router"; import AuthorField from "./entity-fields/AuthorField.vue"; import LogoField from "./entity-fields/LogoField.vue"; +import ReloadField from "./entity-fields/ReloadField.vue"; import SwitchField from "./entity-fields/SwitchField.vue"; const { currentUserHasPermission } = usePermission(); @@ -219,6 +220,14 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint( description: props.plugin.spec.version, }, }, + { + position: "end", + priority: 41, + component: markRaw(ReloadField), + props: { + plugin: props.plugin, + }, + }, { position: "end", priority: 50, diff --git a/ui/console-src/modules/system/plugins/components/entity-fields/ReloadField.vue b/ui/console-src/modules/system/plugins/components/entity-fields/ReloadField.vue new file mode 100644 index 000000000..af6e8f68a --- /dev/null +++ b/ui/console-src/modules/system/plugins/components/entity-fields/ReloadField.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/ui/console-src/setup/setupModules.ts b/ui/console-src/setup/setupModules.ts index e7ded8bf2..00aaa20f8 100644 --- a/ui/console-src/setup/setupModules.ts +++ b/ui/console-src/setup/setupModules.ts @@ -10,46 +10,80 @@ import type { App } from "vue"; import type { RouteRecordRaw } from "vue-router"; export function setupCoreModules(app: App) { - modules.forEach((module) => { - registerModule(app, module, true); - }); + modules.forEach((module) => registerModule(app, module, true)); } export async function setupPluginModules(app: App) { const pluginModuleStore = usePluginModuleStore(); - try { - const { load } = useScriptTag( - `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.js?t=${Date.now()}` - ); - - await load(); - - const enabledPluginNames = window["enabledPluginNames"] as string[]; - - enabledPluginNames.forEach((name) => { - const module = window[name]; - if (module) { - registerModule(app, module, false); - pluginModuleStore.registerPluginModule(name, module); - } - }); - } catch (e) { - const message = i18n.global.t("core.plugin.loader.toast.entry_load_failed"); - console.error(message, e); - Toast.error(message); - } try { - await loadStyle( - `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.css?t=${Date.now()}` - ); - } catch (e) { - const message = i18n.global.t("core.plugin.loader.toast.style_load_failed"); - console.error(message, e); - Toast.error(message); + await loadPluginBundle(); + await registerEnabledPlugins(app, pluginModuleStore); + await loadPluginStyles(); + } catch (error) { + handleError(error); } } +async function loadPluginBundle() { + const { load } = useScriptTag( + `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.js?t=${Date.now()}` + ); + await load(); +} + +async function registerEnabledPlugins( + app: App, + pluginModuleStore: ReturnType +) { + const enabledPlugins = window["enabledPlugins"] as { + name: string; + value: string; + }[]; + + if (enabledPlugins) { + enabledPlugins.forEach((plugin) => + registerPluginIfAvailable(app, pluginModuleStore, plugin.name) + ); + } + + // @Deprecated: Compatibility solution, will be removed in the future + const enabledPluginNames = window["enabledPluginNames"] as string[]; + if (enabledPluginNames) { + enabledPluginNames.forEach((name) => + registerPluginIfAvailable(app, pluginModuleStore, name) + ); + } +} + +function registerPluginIfAvailable( + app: App, + pluginModuleStore: ReturnType, + name: string +) { + const module = window[name]; + if (module) { + registerModule(app, module, false); + pluginModuleStore.registerPluginModule(name, module); + } +} + +async function loadPluginStyles() { + await loadStyle( + `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.css?t=${Date.now()}` + ); +} + +function handleError(error: unknown) { + const message = + error instanceof Error && error.message.includes("style") + ? i18n.global.t("core.plugin.loader.toast.style_load_failed") + : i18n.global.t("core.plugin.loader.toast.entry_load_failed"); + + console.error(message, error); + Toast.error(message); +} + function registerModule(app: App, pluginModule: PluginModule, core: boolean) { if (pluginModule.components) { Object.keys(pluginModule.components).forEach((key) => { diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml index 64720797b..0d80231f8 100644 --- a/ui/src/locales/en.yaml +++ b/ui/src/locales/en.yaml @@ -940,6 +940,8 @@ core: remote_download: title: Remote download address detected, do you want to download? description: "Please carefully verify whether this address can be trusted: {url}" + reload_window: + button: Reload required filters: status: items: diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml index bb00b279b..2c5faf0e5 100644 --- a/ui/src/locales/zh-CN.yaml +++ b/ui/src/locales/zh-CN.yaml @@ -876,6 +876,8 @@ core: remote_download: title: 检测到了远程下载地址,是否需要下载? description: 请仔细鉴别此地址是否可信:{url} + reload_window: + button: 需要重载页面 filters: status: items: diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml index dd607a15e..fbea81c81 100644 --- a/ui/src/locales/zh-TW.yaml +++ b/ui/src/locales/zh-TW.yaml @@ -856,6 +856,8 @@ core: remote_download: title: 偵測到遠端下載地址,是否需要下載? description: 請仔細鑑別此地址是否可信:{url} + reload_window: + button: 需要重載頁面 filters: status: items: diff --git a/ui/uc-src/setup/setupModules.ts b/ui/uc-src/setup/setupModules.ts index dd67050aa..c134fba9c 100644 --- a/ui/uc-src/setup/setupModules.ts +++ b/ui/uc-src/setup/setupModules.ts @@ -10,46 +10,80 @@ import type { App } from "vue"; import type { RouteRecordRaw } from "vue-router"; export function setupCoreModules(app: App) { - modules.forEach((module) => { - registerModule(app, module, true); - }); + modules.forEach((module) => registerModule(app, module, true)); } export async function setupPluginModules(app: App) { const pluginModuleStore = usePluginModuleStore(); - try { - const { load } = useScriptTag( - `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.js?t=${Date.now()}` - ); - - await load(); - - const enabledPluginNames = window["enabledPluginNames"] as string[]; - - enabledPluginNames.forEach((name) => { - const module = window[name]; - if (module) { - registerModule(app, module, false); - pluginModuleStore.registerPluginModule(name, module); - } - }); - } catch (e) { - const message = i18n.global.t("core.plugin.loader.toast.entry_load_failed"); - console.error(message, e); - Toast.error(message); - } try { - await loadStyle( - `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.css?t=${Date.now()}` - ); - } catch (e) { - const message = i18n.global.t("core.plugin.loader.toast.style_load_failed"); - console.error(message, e); - Toast.error(message); + await loadPluginBundle(); + await registerEnabledPlugins(app, pluginModuleStore); + await loadPluginStyles(); + } catch (error) { + handleError(error); } } +async function loadPluginBundle() { + const { load } = useScriptTag( + `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.js?t=${Date.now()}` + ); + await load(); +} + +async function registerEnabledPlugins( + app: App, + pluginModuleStore: ReturnType +) { + const enabledPlugins = window["enabledPlugins"] as { + name: string; + value: string; + }[]; + + if (enabledPlugins) { + enabledPlugins.forEach((plugin) => + registerPluginIfAvailable(app, pluginModuleStore, plugin.name) + ); + } + + // @Deprecated: Compatibility solution, will be removed in the future + const enabledPluginNames = window["enabledPluginNames"] as string[]; + if (enabledPluginNames) { + enabledPluginNames.forEach((name) => + registerPluginIfAvailable(app, pluginModuleStore, name) + ); + } +} + +function registerPluginIfAvailable( + app: App, + pluginModuleStore: ReturnType, + name: string +) { + const module = window[name]; + if (module) { + registerModule(app, module, false); + pluginModuleStore.registerPluginModule(name, module); + } +} + +async function loadPluginStyles() { + await loadStyle( + `/apis/api.console.halo.run/v1alpha1/plugins/-/bundle.css?t=${Date.now()}` + ); +} + +function handleError(error: unknown) { + const message = + error instanceof Error && error.message.includes("style") + ? i18n.global.t("core.plugin.loader.toast.style_load_failed") + : i18n.global.t("core.plugin.loader.toast.entry_load_failed"); + + console.error(message, error); + Toast.error(message); +} + function registerModule(app: App, pluginModule: PluginModule, core: boolean) { if (pluginModule.components) { Object.keys(pluginModule.components).forEach((key) => {