From fbf2b06432e983e6e174a402b9d320954c2bb2e7 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Thu, 25 Apr 2024 08:33:09 +0800 Subject: [PATCH] feat: add extension points for user detail tab (#5763) 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.15.x #### What this PR does / why we need it: 为 Console 的用户详情页面的选项卡和个人中心的个人资料页面选项卡添加扩展点,支持通过插件扩展选项卡。 todo: - [x] 完善 docs.halo.run 的开发文档 https://github.com/halo-dev/docs/pull/340 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5745 #### Special notes for your reviewer: 可以使用 [plugin-starter-1.3.2-SNAPSHOT.jar.zip](https://github.com/halo-dev/halo/files/15059291/plugin-starter-1.3.2-SNAPSHOT.jar.zip) 进行测试。 #### Does this PR introduce a user-facing change? ```release-note 为 Console 的用户详情页面的选项卡和个人中心的个人资料页面选项卡添加扩展点 ``` --- .../modules/system/users/UserDetail.vue | 62 +++++++++++------- .../modules/system/users/tabs/Detail.vue | 6 +- ui/packages/shared/src/index.ts | 1 + ui/packages/shared/src/states/user-tab.ts | 17 +++++ ui/packages/shared/src/types/plugin.ts | 9 ++- ui/uc-src/modules/profile/Profile.vue | 63 ++++++++++++------- ui/uc-src/modules/profile/tabs/Detail.vue | 6 +- .../profile/tabs/NotificationPreferences.vue | 26 ++++---- 8 files changed, 126 insertions(+), 64 deletions(-) create mode 100644 ui/packages/shared/src/states/user-tab.ts diff --git a/ui/console-src/modules/system/users/UserDetail.vue b/ui/console-src/modules/system/users/UserDetail.vue index 2355ac07f..5cdd44883 100644 --- a/ui/console-src/modules/system/users/UserDetail.vue +++ b/ui/console-src/modules/system/users/UserDetail.vue @@ -2,40 +2,36 @@ import { apiClient } from "@/utils/api-client"; import { VButton, - VTabbar, VDropdown, VDropdownItem, + VTabbar, } from "@halo-dev/components"; -import { computed, provide, ref, type Ref } from "vue"; +import { + computed, + markRaw, + onMounted, + provide, + type Ref, + ref, + toRaw, +} from "vue"; import { useRoute } from "vue-router"; -import type { DetailedUser } from "@halo-dev/api-client"; import UserEditingModal from "./components/UserEditingModal.vue"; import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue"; import { usePermission } from "@/utils/permission"; import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; import UserAvatar from "@/components/user-avatar/UserAvatar.vue"; -import type { Raw } from "vue"; -import type { Component } from "vue"; -import { markRaw } from "vue"; import DetailTab from "./tabs/Detail.vue"; import { useRouteQuery } from "@vueuse/router"; import { useUserStore } from "@/stores/user"; +import { usePluginModuleStore } from "@/stores/plugin"; +import type { PluginModule, UserTab } from "@halo-dev/console-shared"; const { currentUserHasPermission } = usePermission(); const { t } = useI18n(); const { currentUser } = useUserStore(); -interface UserTab { - id: string; - label: string; - component: Raw; - props?: Record; - permissions?: string[]; - priority: number; - hidden?: boolean; -} - const editingModal = ref(false); const passwordChangeModal = ref(false); @@ -56,25 +52,44 @@ const { enabled: computed(() => !!params.name), }); -provide>("user", user); - -const tabs: UserTab[] = [ +const tabs = ref([ { id: "detail", label: t("core.user.detail.tabs.detail"), component: markRaw(DetailTab), priority: 10, }, -]; +]); -const activeTab = useRouteQuery("tab", tabs[0].id, { +// Collect user:detail:tabs:create extension points +onMounted(() => { + const { pluginModules } = usePluginModuleStore(); + + pluginModules.forEach((pluginModule: PluginModule) => { + const { extensionPoints } = pluginModule; + if (!extensionPoints?.["user:detail:tabs:create"]) { + return; + } + + const providers = extensionPoints["user:detail:tabs:create"]() as UserTab[]; + + tabs.value.push(...providers); + }); +}); + +const activeTab = useRouteQuery("tab", tabs.value[0].id, { mode: "push", }); provide>("activeTab", activeTab); const tabbarItems = computed(() => { - return tabs.map((tab) => ({ id: tab.id, label: tab.label })); + return toRaw(tabs) + .value.sort((a, b) => a.priority - b.priority) + .map((tab) => ({ + id: tab.id, + label: tab.label, + })); }); function handleRouteToUC() { @@ -142,7 +157,8 @@ function handleRouteToUC() { diff --git a/ui/console-src/modules/system/users/tabs/Detail.vue b/ui/console-src/modules/system/users/tabs/Detail.vue index 8fd1a2f9d..cad29bc0e 100644 --- a/ui/console-src/modules/system/users/tabs/Detail.vue +++ b/ui/console-src/modules/system/users/tabs/Detail.vue @@ -5,13 +5,13 @@ import { VDescriptionItem, VTag, } from "@halo-dev/components"; -import type { Ref } from "vue"; -import { inject } from "vue"; import type { DetailedUser } from "@halo-dev/api-client"; import { rbacAnnotations } from "@/constants/annotations"; import { formatDatetime } from "@/utils/date"; -const user = inject>("user"); +withDefaults(defineProps<{ user?: DetailedUser }>(), { + user: undefined, +});