From 5a0f735efb741feca16e6b0944ce5dd9c4464728 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Thu, 25 Apr 2024 16:21:13 +0800 Subject: [PATCH] feat: show the startup status of the plugin (#5520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /area ui /kind feature /milestone 2.14.x #### What this PR does / why we need it: Show the startup status of the plugin image #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5254 #### Does this PR introduce a user-facing change? ```release-note 插件支持显示启动中的状态。 ``` --- .../modules/system/plugins/PluginList.vue | 21 ++- .../plugins/components/PluginListItem.vue | 165 ++++++++++-------- .../system/plugins/composables/use-plugin.ts | 27 ++- ui/src/locales/en.yaml | 1 + ui/src/locales/zh-CN.yaml | 1 + ui/src/locales/zh-TW.yaml | 1 + 6 files changed, 134 insertions(+), 82 deletions(-) diff --git a/ui/console-src/modules/system/plugins/PluginList.vue b/ui/console-src/modules/system/plugins/PluginList.vue index 70a20a9ed9..1cb99454a3 100644 --- a/ui/console-src/modules/system/plugins/PluginList.vue +++ b/ui/console-src/modules/system/plugins/PluginList.vue @@ -20,7 +20,7 @@ import { computed, onMounted, provide, ref, watch } from "vue"; import { apiClient } from "@/utils/api-client"; import { usePermission } from "@/utils/permission"; import { useQuery } from "@tanstack/vue-query"; -import type { Plugin } from "@halo-dev/api-client"; +import { PluginStatusPhaseEnum, type Plugin } from "@halo-dev/api-client"; import { useI18n } from "vue-i18n"; import { useRouteQuery } from "@vueuse/router"; import { usePluginBatchOperations } from "./composables/use-plugin"; @@ -63,11 +63,26 @@ const { data, isLoading, isFetching, refetch } = useQuery({ }, keepPreviousData: true, refetchInterval: (data) => { - const deletingPlugins = data?.filter( + const hasDeletingData = data?.some( (plugin) => !!plugin.metadata.deletionTimestamp ); - return deletingPlugins?.length ? 2000 : false; + if (hasDeletingData) { + return 1000; + } + + const hasStartingData = data?.some( + (plugin) => + plugin.spec.enabled && + plugin.status?.phase !== + (PluginStatusPhaseEnum.Started || PluginStatusPhaseEnum.Failed) + ); + + if (hasStartingData) { + return 3000; + } + + return false; }, }); diff --git a/ui/console-src/modules/system/plugins/components/PluginListItem.vue b/ui/console-src/modules/system/plugins/components/PluginListItem.vue index d4ceba34e6..3c84705a22 100644 --- a/ui/console-src/modules/system/plugins/components/PluginListItem.vue +++ b/ui/console-src/modules/system/plugins/components/PluginListItem.vue @@ -10,7 +10,7 @@ import { import type { Ref } from "vue"; import { computed, inject, markRaw, ref, toRefs } from "vue"; import { usePluginLifeCycle } from "../composables/use-plugin"; -import type { Plugin } from "@halo-dev/api-client"; +import { type Plugin, PluginStatusPhaseEnum } from "@halo-dev/api-client"; import { formatDatetime } from "@/utils/date"; import { usePermission } from "@/utils/permission"; import { apiClient } from "@/utils/api-client"; @@ -43,7 +43,7 @@ const { plugin } = toRefs(props); const selectedNames = inject>("selectedNames", ref([])); -const { getFailedMessage, uninstall } = usePluginLifeCycle(plugin); +const { getStatusMessage, uninstall } = usePluginLifeCycle(plugin); const pluginUpgradeModalVisible = ref(false); @@ -146,86 +146,105 @@ const { operationItems } = useOperationItemExtensionPoint( const { startFields, endFields } = useEntityFieldItemExtensionPoint( "plugin:list-item:field:create", plugin, - computed((): EntityFieldItem[] => [ - { - position: "start", - priority: 10, - component: markRaw(LogoField), - props: { - plugin: props.plugin, + computed((): EntityFieldItem[] => { + const { enabled } = props.plugin.spec || {}; + const { phase } = props.plugin.status || {}; + + const shouldHideStatusDot = + !enabled || (enabled && phase === PluginStatusPhaseEnum.Started); + + const getStatusDotState = () => { + if ( + enabled && + phase !== + (PluginStatusPhaseEnum.Started || PluginStatusPhaseEnum.Failed) + ) { + return "default"; + } + return "error"; + }; + + return [ + { + position: "start", + priority: 10, + component: markRaw(LogoField), + props: { + plugin: props.plugin, + }, }, - }, - { - position: "start", - priority: 20, - component: markRaw(VEntityField), - props: { - title: props.plugin.spec.displayName, - description: props.plugin.spec.description, - route: { - name: "PluginDetail", - params: { name: props.plugin.metadata.name }, + { + position: "start", + priority: 20, + component: markRaw(VEntityField), + props: { + title: props.plugin.spec.displayName, + description: props.plugin.spec.description, + route: { + name: "PluginDetail", + params: { name: props.plugin.metadata.name }, + }, }, }, - }, - { - position: "end", - priority: 10, - component: markRaw(StatusDotField), - props: { - tooltip: getFailedMessage(), - state: "error", - animate: true, + { + position: "end", + priority: 10, + component: markRaw(StatusDotField), + props: { + tooltip: getStatusMessage(), + state: getStatusDotState(), + animate: true, + }, + hidden: shouldHideStatusDot, }, - hidden: props.plugin.status?.phase !== "FAILED", - }, - { - position: "end", - priority: 20, - component: markRaw(StatusDotField), - props: { - tooltip: t("core.common.status.deleting"), - state: "warning", - animate: true, + { + position: "end", + priority: 20, + component: markRaw(StatusDotField), + props: { + tooltip: t("core.common.status.deleting"), + state: "warning", + animate: true, + }, + hidden: !props.plugin.metadata.deletionTimestamp, }, - hidden: !props.plugin.metadata.deletionTimestamp, - }, - { - position: "end", - priority: 30, - component: markRaw(AuthorField), - props: { - plugin: props.plugin, + { + position: "end", + priority: 30, + component: markRaw(AuthorField), + props: { + plugin: props.plugin, + }, + hidden: !props.plugin.spec.author, }, - hidden: !props.plugin.spec.author, - }, - { - position: "end", - priority: 40, - component: markRaw(VEntityField), - props: { - description: props.plugin.spec.version, + { + position: "end", + priority: 40, + component: markRaw(VEntityField), + props: { + description: props.plugin.spec.version, + }, }, - }, - { - position: "end", - priority: 50, - component: markRaw(VEntityField), - props: { - description: formatDatetime(props.plugin.metadata.creationTimestamp), + { + position: "end", + priority: 50, + component: markRaw(VEntityField), + props: { + description: formatDatetime(props.plugin.metadata.creationTimestamp), + }, + hidden: !props.plugin.metadata.creationTimestamp, }, - hidden: !props.plugin.metadata.creationTimestamp, - }, - { - position: "end", - priority: 60, - component: markRaw(SwitchField), - props: { - plugin: props.plugin, + { + position: "end", + priority: 60, + component: markRaw(SwitchField), + props: { + plugin: props.plugin, + }, + permissions: ["system:plugins:manage"], }, - permissions: ["system:plugins:manage"], - }, - ]) + ]; + }) );