mirror of https://github.com/halo-dev/halo
feat: enable asynchronous resolving for UI extension points (#6018)
#### What type of PR is this? /area ui /kind feature /milestone 2.16.x #### What this PR does / why we need it: 优化 UI 部分的扩展点获取实现,让部分扩展点支持异步获取,之前的实现与文档不符。 比如: ```ts import { definePlugin } from "@halo-dev/console-shared"; import axios from "axios"; export default definePlugin({ components: {}, routes: [], extensionPoints: { "attachment:selector:create": async () => { const { data } = await axios.get( "/apis/v1alpha1/fake.halo.run/attachments/selectors" ); return data; }, }, }); ``` #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/6008 #### Does this PR introduce a user-facing change? ```release-note 优化 UI 部分的扩展点获取实现,让部分扩展点支持异步获取。 ```pull/6022/head
parent
bf75a36df7
commit
4c6abdcaa1
|
@ -1,14 +1,13 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
import { VButton, VModal, VSpace, VTabbar } from "@halo-dev/components";
|
import { VButton, VModal, VSpace, VTabbar } from "@halo-dev/components";
|
||||||
import { ref, markRaw, onMounted, computed } from "vue";
|
|
||||||
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
|
|
||||||
import type {
|
import type {
|
||||||
AttachmentLike,
|
AttachmentLike,
|
||||||
AttachmentSelectProvider,
|
AttachmentSelectProvider,
|
||||||
PluginModule,
|
|
||||||
} from "@halo-dev/console-shared";
|
} from "@halo-dev/console-shared";
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
import { computed, markRaw, onMounted, ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -46,23 +45,22 @@ const attachmentSelectProviders = ref<AttachmentSelectProvider[]>([
|
||||||
// resolve plugin extension points
|
// resolve plugin extension points
|
||||||
const { pluginModules } = usePluginModuleStore();
|
const { pluginModules } = usePluginModuleStore();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
for (const pluginModule of pluginModules) {
|
||||||
const { extensionPoints } = pluginModule;
|
try {
|
||||||
if (!extensionPoints?.["attachment:selector:create"]) {
|
const callbackFunction =
|
||||||
return;
|
pluginModule?.extensionPoints?.["attachment:selector:create"];
|
||||||
}
|
|
||||||
|
|
||||||
const providers = extensionPoints[
|
if (typeof callbackFunction !== "function") {
|
||||||
"attachment:selector:create"
|
continue;
|
||||||
]() as AttachmentSelectProvider[];
|
}
|
||||||
|
|
||||||
if (providers) {
|
const providers = await callbackFunction();
|
||||||
providers.forEach((provider) => {
|
attachmentSelectProviders.value.push(...providers);
|
||||||
attachmentSelectProviders.value.push(provider);
|
} catch (error) {
|
||||||
});
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeId = ref(attachmentSelectProviders.value[0].id);
|
const activeId = ref(attachmentSelectProviders.value[0].id);
|
||||||
|
|
|
@ -1,4 +1,15 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { formatDatetime } from "@/utils/date";
|
||||||
|
import { usePermission } from "@/utils/permission";
|
||||||
|
import type {
|
||||||
|
Extension,
|
||||||
|
ListedComment,
|
||||||
|
ListedReply,
|
||||||
|
Post,
|
||||||
|
SinglePage,
|
||||||
|
} from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
IconAddCircle,
|
IconAddCircle,
|
||||||
|
@ -15,28 +26,16 @@ import {
|
||||||
VStatusDot,
|
VStatusDot,
|
||||||
VTag,
|
VTag,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
|
||||||
import type {
|
|
||||||
Extension,
|
|
||||||
ListedComment,
|
|
||||||
ListedReply,
|
|
||||||
Post,
|
|
||||||
SinglePage,
|
|
||||||
} from "@halo-dev/api-client";
|
|
||||||
import { formatDatetime } from "@/utils/date";
|
|
||||||
import { computed, onMounted, provide, ref, type Ref } from "vue";
|
|
||||||
import ReplyListItem from "./ReplyListItem.vue";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import { cloneDeep } from "lodash-es";
|
|
||||||
import { usePermission } from "@/utils/permission";
|
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
import type {
|
import type {
|
||||||
CommentSubjectRefProvider,
|
CommentSubjectRefProvider,
|
||||||
CommentSubjectRefResult,
|
CommentSubjectRefResult,
|
||||||
PluginModule,
|
|
||||||
} from "@halo-dev/console-shared";
|
} from "@halo-dev/console-shared";
|
||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
|
import { computed, onMounted, provide, ref, type Ref } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
||||||
|
import ReplyListItem from "./ReplyListItem.vue";
|
||||||
|
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -250,25 +249,21 @@ const SubjectRefProviders = ref<CommentSubjectRefProvider[]>([
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const { pluginModules } = usePluginModuleStore();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const { pluginModules } = usePluginModuleStore();
|
for (const pluginModule of pluginModules) {
|
||||||
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["comment:subject-ref:create"];
|
||||||
|
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
if (typeof callbackFunction !== "function") {
|
||||||
const { extensionPoints } = pluginModule;
|
continue;
|
||||||
if (!extensionPoints?.["comment:subject-ref:create"]) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const providers = extensionPoints[
|
const providers = callbackFunction();
|
||||||
"comment:subject-ref:create"
|
|
||||||
]() as CommentSubjectRefProvider[];
|
|
||||||
|
|
||||||
if (providers) {
|
SubjectRefProviders.value.push(...providers);
|
||||||
providers.forEach((provider) => {
|
}
|
||||||
SubjectRefProviders.value.push(provider);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const subjectRefResult = computed(() => {
|
const subjectRefResult = computed(() => {
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
|
import { usePermission } from "@/utils/permission";
|
||||||
|
import type { Theme } from "@halo-dev/api-client";
|
||||||
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
||||||
|
import type { ThemeListTab } from "@halo-dev/console-shared";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
inject,
|
inject,
|
||||||
|
@ -8,19 +13,14 @@ import {
|
||||||
onMounted,
|
onMounted,
|
||||||
provide,
|
provide,
|
||||||
ref,
|
ref,
|
||||||
type Ref,
|
|
||||||
watch,
|
watch,
|
||||||
|
type Ref,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import type { Theme } from "@halo-dev/api-client";
|
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import InstalledThemes from "./list-tabs/InstalledThemes.vue";
|
import InstalledThemes from "./list-tabs/InstalledThemes.vue";
|
||||||
import NotInstalledThemes from "./list-tabs/NotInstalledThemes.vue";
|
|
||||||
import LocalUpload from "./list-tabs/LocalUpload.vue";
|
import LocalUpload from "./list-tabs/LocalUpload.vue";
|
||||||
|
import NotInstalledThemes from "./list-tabs/NotInstalledThemes.vue";
|
||||||
import RemoteDownload from "./list-tabs/RemoteDownload.vue";
|
import RemoteDownload from "./list-tabs/RemoteDownload.vue";
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
import type { PluginModule, ThemeListTab } from "@halo-dev/console-shared";
|
|
||||||
import { usePermission } from "@/utils/permission";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
|
@ -92,22 +92,30 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pluginModules } = usePluginModuleStore();
|
const { pluginModules } = usePluginModuleStore();
|
||||||
onMounted(() => {
|
|
||||||
|
onMounted(async () => {
|
||||||
const tabsFromPlugins: ThemeListTab[] = [];
|
const tabsFromPlugins: ThemeListTab[] = [];
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["theme:list:tabs:create"]) {
|
try {
|
||||||
return;
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["theme:list:tabs:create"];
|
||||||
|
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = await callbackFunction();
|
||||||
|
|
||||||
|
tabsFromPlugins.push(
|
||||||
|
...items.filter((item) => {
|
||||||
|
return currentUserHasPermission(item.permissions);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let items = extensionPoints["theme:list:tabs:create"]() as ThemeListTab[];
|
|
||||||
|
|
||||||
items = items.filter((item) => {
|
|
||||||
return currentUserHasPermission(item.permissions);
|
|
||||||
});
|
|
||||||
|
|
||||||
tabsFromPlugins.push(...items);
|
|
||||||
});
|
|
||||||
|
|
||||||
tabs.value = tabs.value.concat(tabsFromPlugins).sort((a, b) => {
|
tabs.value = tabs.value.concat(tabsFromPlugins).sort((a, b) => {
|
||||||
return a.priority - b.priority;
|
return a.priority - b.priority;
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import {
|
||||||
VPageHeader,
|
|
||||||
VCard,
|
|
||||||
VButton,
|
|
||||||
VTabbar,
|
|
||||||
IconAddCircle,
|
IconAddCircle,
|
||||||
IconServerLine,
|
IconServerLine,
|
||||||
|
VButton,
|
||||||
|
VCard,
|
||||||
|
VPageHeader,
|
||||||
|
VTabbar,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
|
|
||||||
import { onMounted, shallowRef } from "vue";
|
|
||||||
import ListTab from "./tabs/List.vue";
|
|
||||||
import RestoreTab from "./tabs/Restore.vue";
|
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import { markRaw } from "vue";
|
|
||||||
import { useI18n } from "vue-i18n";
|
|
||||||
import { useBackup } from "./composables/use-backup";
|
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
import type { BackupTab } from "@halo-dev/console-shared";
|
import type { BackupTab } from "@halo-dev/console-shared";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import { markRaw, onMounted, shallowRef } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useBackup } from "./composables/use-backup";
|
||||||
|
import ListTab from "./tabs/List.vue";
|
||||||
|
import RestoreTab from "./tabs/Restore.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -37,21 +36,27 @@ const activeTab = useRouteQuery<string>("tab", tabs.value[0].id);
|
||||||
|
|
||||||
const { handleCreate } = useBackup();
|
const { handleCreate } = useBackup();
|
||||||
|
|
||||||
onMounted(() => {
|
const { pluginModules } = usePluginModuleStore();
|
||||||
const { pluginModules } = usePluginModuleStore();
|
|
||||||
|
|
||||||
pluginModules.forEach((pluginModule) => {
|
onMounted(async () => {
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["backup:tabs:create"]) {
|
try {
|
||||||
return;
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["backup:tabs:create"];
|
||||||
|
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backupTabs = await callbackFunction();
|
||||||
|
|
||||||
|
if (backupTabs) {
|
||||||
|
tabs.value = tabs.value.concat(backupTabs);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const backupTabs = extensionPoints["backup:tabs:create"]() as BackupTab[];
|
|
||||||
|
|
||||||
if (backupTabs) {
|
|
||||||
tabs.value = tabs.value.concat(backupTabs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// core libs
|
// core libs
|
||||||
import { provide, ref, computed } from "vue";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { computed, provide, ref } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
|
||||||
// libs
|
// libs
|
||||||
import { cloneDeep } from "lodash-es";
|
import { cloneDeep } from "lodash-es";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { VCard, VPageHeader, VTabbar, VAvatar } from "@halo-dev/components";
|
import { VAvatar, VCard, VPageHeader, VTabbar } from "@halo-dev/components";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { Ref } from "vue";
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
import type { Plugin, Setting, SettingForm } from "@halo-dev/api-client";
|
|
||||||
import { usePermission } from "@/utils/permission";
|
import { usePermission } from "@/utils/permission";
|
||||||
import { useI18n } from "vue-i18n";
|
import type { Plugin, Setting, SettingForm } from "@halo-dev/api-client";
|
||||||
import { useQuery } from "@tanstack/vue-query";
|
|
||||||
import type { PluginTab } from "@halo-dev/console-shared";
|
import type { PluginTab } from "@halo-dev/console-shared";
|
||||||
|
import { useQuery } from "@tanstack/vue-query";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
|
import type { Ref } from "vue";
|
||||||
import { markRaw } from "vue";
|
import { markRaw } from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import DetailTab from "./tabs/Detail.vue";
|
import DetailTab from "./tabs/Detail.vue";
|
||||||
import SettingTab from "./tabs/Setting.vue";
|
import SettingTab from "./tabs/Setting.vue";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
|
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -50,12 +50,12 @@ const { data: plugin } = useQuery({
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
onSuccess(data) {
|
async onSuccess(data) {
|
||||||
if (
|
if (
|
||||||
!data.spec.settingName ||
|
!data.spec.settingName ||
|
||||||
!currentUserHasPermission(["system:plugins:manage"])
|
!currentUserHasPermission(["system:plugins:manage"])
|
||||||
) {
|
) {
|
||||||
tabs.value = [...initialTabs.value, ...getTabsFromExtensions()];
|
tabs.value = [...initialTabs.value, ...(await getTabsFromExtensions())];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -82,7 +82,7 @@ const { data: setting } = useQuery({
|
||||||
const { forms } = data.spec;
|
const { forms } = data.spec;
|
||||||
tabs.value = [
|
tabs.value = [
|
||||||
...initialTabs.value,
|
...initialTabs.value,
|
||||||
...getTabsFromExtensions(),
|
...(await getTabsFromExtensions()),
|
||||||
...forms.map((item: SettingForm) => {
|
...forms.map((item: SettingForm) => {
|
||||||
return {
|
return {
|
||||||
id: item.group,
|
id: item.group,
|
||||||
|
@ -97,7 +97,7 @@ const { data: setting } = useQuery({
|
||||||
|
|
||||||
provide<Ref<Setting | undefined>>("setting", setting);
|
provide<Ref<Setting | undefined>>("setting", setting);
|
||||||
|
|
||||||
function getTabsFromExtensions(): PluginTab[] {
|
async function getTabsFromExtensions() {
|
||||||
const { pluginModuleMap } = usePluginModuleStore();
|
const { pluginModuleMap } = usePluginModuleStore();
|
||||||
|
|
||||||
const currentPluginModule = pluginModuleMap[route.params.name as string];
|
const currentPluginModule = pluginModuleMap[route.params.name as string];
|
||||||
|
@ -106,15 +106,14 @@ function getTabsFromExtensions(): PluginTab[] {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { extensionPoints } = currentPluginModule;
|
const callbackFunction =
|
||||||
|
currentPluginModule?.extensionPoints?.["plugin:self:tabs:create"];
|
||||||
|
|
||||||
if (!extensionPoints?.["plugin:self:tabs:create"]) {
|
if (typeof callbackFunction !== "function") {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const pluginTabs = extensionPoints[
|
const pluginTabs = await callbackFunction();
|
||||||
"plugin:self:tabs:create"
|
|
||||||
]() as PluginTab[];
|
|
||||||
|
|
||||||
return pluginTabs.filter((tab) => {
|
return pluginTabs.filter((tab) => {
|
||||||
return currentUserHasPermission(tab.permissions);
|
return currentUserHasPermission(tab.permissions);
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
|
import { usePermission } from "@/utils/permission";
|
||||||
import type { Plugin } from "@halo-dev/api-client";
|
import type { Plugin } from "@halo-dev/api-client";
|
||||||
|
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
||||||
|
import type { PluginInstallationTab } from "@halo-dev/console-shared";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
markRaw,
|
markRaw,
|
||||||
nextTick,
|
nextTick,
|
||||||
onMounted,
|
onMounted,
|
||||||
provide,
|
provide,
|
||||||
type Ref,
|
|
||||||
ref,
|
ref,
|
||||||
toRefs,
|
toRefs,
|
||||||
|
type Ref,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import LocalUpload from "./installation-tabs/LocalUpload.vue";
|
import LocalUpload from "./installation-tabs/LocalUpload.vue";
|
||||||
import RemoteDownload from "./installation-tabs/RemoteDownload.vue";
|
import RemoteDownload from "./installation-tabs/RemoteDownload.vue";
|
||||||
import type {
|
|
||||||
PluginInstallationTab,
|
|
||||||
PluginModule,
|
|
||||||
} from "@halo-dev/console-shared";
|
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
import { usePermission } from "@/utils/permission";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
|
@ -82,23 +79,28 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const { pluginModules } = usePluginModuleStore();
|
const { pluginModules } = usePluginModuleStore();
|
||||||
onMounted(() => {
|
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
onMounted(async () => {
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["plugin:installation:tabs:create"]) {
|
try {
|
||||||
return;
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["plugin:installation:tabs:create"];
|
||||||
|
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = await callbackFunction();
|
||||||
|
|
||||||
|
tabs.value.push(
|
||||||
|
...items.filter((item) => {
|
||||||
|
return currentUserHasPermission(item.permissions);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let items = extensionPoints[
|
|
||||||
"plugin:installation:tabs:create"
|
|
||||||
]() as PluginInstallationTab[];
|
|
||||||
|
|
||||||
items = items.filter((item) => {
|
|
||||||
return currentUserHasPermission(item.permissions);
|
|
||||||
});
|
|
||||||
|
|
||||||
tabs.value.push(...items);
|
|
||||||
});
|
|
||||||
|
|
||||||
tabs.value.sort((a, b) => {
|
tabs.value.sort((a, b) => {
|
||||||
return a.priority - b.priority;
|
return a.priority - b.priority;
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import UserAvatar from "@/components/user-avatar/UserAvatar.vue";
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
|
import { useUserStore } from "@/stores/user";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { usePermission } from "@/utils/permission";
|
||||||
import {
|
import {
|
||||||
VButton,
|
VButton,
|
||||||
VDropdown,
|
VDropdown,
|
||||||
VDropdownItem,
|
VDropdownItem,
|
||||||
VTabbar,
|
VTabbar,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
|
import type { UserTab } from "@halo-dev/console-shared";
|
||||||
|
import { useQuery } from "@tanstack/vue-query";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
markRaw,
|
markRaw,
|
||||||
onMounted,
|
onMounted,
|
||||||
provide,
|
provide,
|
||||||
type Ref,
|
|
||||||
ref,
|
ref,
|
||||||
toRaw,
|
toRaw,
|
||||||
|
type Ref,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
import UserEditingModal from "./components/UserEditingModal.vue";
|
import UserEditingModal from "./components/UserEditingModal.vue";
|
||||||
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.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 DetailTab from "./tabs/Detail.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 { currentUserHasPermission } = usePermission();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -62,19 +62,24 @@ const tabs = ref<UserTab[]>([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Collect user:detail:tabs:create extension points
|
// Collect user:detail:tabs:create extension points
|
||||||
onMounted(() => {
|
const { pluginModules } = usePluginModuleStore();
|
||||||
const { pluginModules } = usePluginModuleStore();
|
|
||||||
|
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
onMounted(async () => {
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["user:detail:tabs:create"]) {
|
try {
|
||||||
return;
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["user:detail:tabs:create"];
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = await callbackFunction();
|
||||||
|
|
||||||
|
tabs.value.push(...providers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const providers = extensionPoints["user:detail:tabs:create"]() as UserTab[];
|
|
||||||
|
|
||||||
tabs.value.push(...providers);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeTab = useRouteQuery<string>("tab", tabs.value[0].id, {
|
const activeTab = useRouteQuery<string>("tab", tabs.value[0].id, {
|
||||||
|
|
|
@ -1,15 +1,10 @@
|
||||||
import type { Component, Ref } from "vue";
|
|
||||||
import type { RouteRecordName, RouteRecordRaw } from "vue-router";
|
|
||||||
import type { FunctionalPage } from "../states/pages";
|
|
||||||
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
|
||||||
import type { EditorProvider, PluginTab } from "..";
|
|
||||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
|
||||||
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
|
||||||
import type { BackupTab } from "@/states/backup";
|
import type { BackupTab } from "@/states/backup";
|
||||||
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref";
|
||||||
import type { EntityFieldItem } from "@/states/entity";
|
import type { EntityFieldItem } from "@/states/entity";
|
||||||
import type { OperationItem } from "@/states/operation";
|
import type { OperationItem } from "@/states/operation";
|
||||||
|
import type { PluginInstallationTab } from "@/states/plugin-installation-tabs";
|
||||||
import type { ThemeListTab } from "@/states/theme-list-tabs";
|
import type { ThemeListTab } from "@/states/theme-list-tabs";
|
||||||
|
import type { UserProfileTab, UserTab } from "@/states/user-tab";
|
||||||
import type {
|
import type {
|
||||||
Attachment,
|
Attachment,
|
||||||
Backup,
|
Backup,
|
||||||
|
@ -17,7 +12,12 @@ import type {
|
||||||
Plugin,
|
Plugin,
|
||||||
Theme,
|
Theme,
|
||||||
} from "@halo-dev/api-client";
|
} from "@halo-dev/api-client";
|
||||||
import type { UserProfileTab, UserTab } from "@/states/user-tab";
|
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||||
|
import type { Component, Ref } from "vue";
|
||||||
|
import type { RouteRecordName, RouteRecordRaw } from "vue-router";
|
||||||
|
import type { EditorProvider, PluginTab } from "..";
|
||||||
|
import type { AttachmentSelectProvider } from "../states/attachment-selector";
|
||||||
|
import type { FunctionalPage } from "../states/pages";
|
||||||
|
|
||||||
export interface RouteRecordAppend {
|
export interface RouteRecordAppend {
|
||||||
parentName: RouteRecordName;
|
parentName: RouteRecordName;
|
||||||
|
@ -50,33 +50,29 @@ export interface ExtensionPoint {
|
||||||
|
|
||||||
"post:list-item:operation:create"?: (
|
"post:list-item:operation:create"?: (
|
||||||
post: Ref<ListedPost>
|
post: Ref<ListedPost>
|
||||||
) => OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>;
|
) => OperationItem<ListedPost>[];
|
||||||
|
|
||||||
"plugin:list-item:operation:create"?: (
|
"plugin:list-item:operation:create"?: (
|
||||||
plugin: Ref<Plugin>
|
plugin: Ref<Plugin>
|
||||||
) => OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>;
|
) => OperationItem<Plugin>[];
|
||||||
|
|
||||||
"backup:list-item:operation:create"?: (
|
"backup:list-item:operation:create"?: (
|
||||||
backup: Ref<Backup>
|
backup: Ref<Backup>
|
||||||
) => OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>;
|
) => OperationItem<Backup>[];
|
||||||
|
|
||||||
"attachment:list-item:operation:create"?: (
|
"attachment:list-item:operation:create"?: (
|
||||||
attachment: Ref<Attachment>
|
attachment: Ref<Attachment>
|
||||||
) => OperationItem<Attachment>[] | Promise<OperationItem<Attachment>[]>;
|
) => OperationItem<Attachment>[];
|
||||||
|
|
||||||
"plugin:list-item:field:create"?: (
|
"plugin:list-item:field:create"?: (plugin: Ref<Plugin>) => EntityFieldItem[];
|
||||||
plugin: Ref<Plugin>
|
|
||||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
|
||||||
|
|
||||||
"post:list-item:field:create"?: (
|
"post:list-item:field:create"?: (post: Ref<ListedPost>) => EntityFieldItem[];
|
||||||
post: Ref<ListedPost>
|
|
||||||
) => EntityFieldItem[] | Promise<EntityFieldItem[]>;
|
|
||||||
|
|
||||||
"theme:list:tabs:create"?: () => ThemeListTab[] | Promise<ThemeListTab[]>;
|
"theme:list:tabs:create"?: () => ThemeListTab[] | Promise<ThemeListTab[]>;
|
||||||
|
|
||||||
"theme:list-item:operation:create"?: (
|
"theme:list-item:operation:create"?: (
|
||||||
theme: Ref<Theme>
|
theme: Ref<Theme>
|
||||||
) => OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>;
|
) => OperationItem<Theme>[];
|
||||||
|
|
||||||
"user:detail:tabs:create"?: () => UserTab[] | Promise<UserTab[]>;
|
"user:detail:tabs:create"?: () => UserTab[] | Promise<UserTab[]>;
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,62 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import {
|
||||||
|
DecorationSet,
|
||||||
|
Editor,
|
||||||
Extension,
|
Extension,
|
||||||
RichTextEditor,
|
|
||||||
ExtensionBlockquote,
|
ExtensionBlockquote,
|
||||||
ExtensionBold,
|
ExtensionBold,
|
||||||
ExtensionBulletList,
|
ExtensionBulletList,
|
||||||
|
ExtensionClearFormat,
|
||||||
ExtensionCode,
|
ExtensionCode,
|
||||||
|
ExtensionCodeBlock,
|
||||||
|
ExtensionColor,
|
||||||
|
ExtensionColumn,
|
||||||
|
ExtensionColumns,
|
||||||
|
ExtensionCommands,
|
||||||
ExtensionDocument,
|
ExtensionDocument,
|
||||||
|
ExtensionDraggable,
|
||||||
ExtensionDropcursor,
|
ExtensionDropcursor,
|
||||||
|
ExtensionFontSize,
|
||||||
|
ExtensionFormatBrush,
|
||||||
ExtensionGapcursor,
|
ExtensionGapcursor,
|
||||||
ExtensionHardBreak,
|
ExtensionHardBreak,
|
||||||
ExtensionHeading,
|
ExtensionHeading,
|
||||||
|
ExtensionHighlight,
|
||||||
ExtensionHistory,
|
ExtensionHistory,
|
||||||
ExtensionHorizontalRule,
|
ExtensionHorizontalRule,
|
||||||
|
ExtensionIframe,
|
||||||
|
ExtensionIndent,
|
||||||
ExtensionItalic,
|
ExtensionItalic,
|
||||||
ExtensionOrderedList,
|
|
||||||
ExtensionStrike,
|
|
||||||
ExtensionText,
|
|
||||||
ExtensionTaskList,
|
|
||||||
ExtensionLink,
|
ExtensionLink,
|
||||||
ExtensionTextAlign,
|
ExtensionListKeymap,
|
||||||
ExtensionUnderline,
|
ExtensionNodeSelected,
|
||||||
ExtensionTable,
|
ExtensionOrderedList,
|
||||||
|
ExtensionPlaceholder,
|
||||||
|
ExtensionSearchAndReplace,
|
||||||
|
ExtensionStrike,
|
||||||
ExtensionSubscript,
|
ExtensionSubscript,
|
||||||
ExtensionSuperscript,
|
ExtensionSuperscript,
|
||||||
ExtensionPlaceholder,
|
ExtensionTable,
|
||||||
ExtensionHighlight,
|
ExtensionTaskList,
|
||||||
ExtensionCommands,
|
ExtensionText,
|
||||||
ExtensionIframe,
|
ExtensionTextAlign,
|
||||||
ExtensionCodeBlock,
|
|
||||||
ExtensionFontSize,
|
|
||||||
ExtensionColor,
|
|
||||||
ExtensionIndent,
|
|
||||||
lowlight,
|
|
||||||
type AnyExtension,
|
|
||||||
Editor,
|
|
||||||
ToolboxItem,
|
|
||||||
ExtensionDraggable,
|
|
||||||
ExtensionColumns,
|
|
||||||
ExtensionColumn,
|
|
||||||
ExtensionNodeSelected,
|
|
||||||
ExtensionTrailingNode,
|
ExtensionTrailingNode,
|
||||||
ToolbarItem,
|
ExtensionUnderline,
|
||||||
Plugin,
|
Plugin,
|
||||||
PluginKey,
|
PluginKey,
|
||||||
DecorationSet,
|
RichTextEditor,
|
||||||
ExtensionListKeymap,
|
ToolbarItem,
|
||||||
ExtensionSearchAndReplace,
|
ToolboxItem,
|
||||||
ExtensionClearFormat,
|
lowlight,
|
||||||
ExtensionFormatBrush,
|
type AnyExtension,
|
||||||
} from "@halo-dev/richtext-editor";
|
} from "@halo-dev/richtext-editor";
|
||||||
// ui custom extension
|
// ui custom extension
|
||||||
import {
|
import { i18n } from "@/locales";
|
||||||
UiExtensionAudio,
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
UiExtensionImage,
|
import { formatDatetime } from "@/utils/date";
|
||||||
UiExtensionUpload,
|
import { usePermission } from "@/utils/permission";
|
||||||
UiExtensionVideo,
|
import AttachmentSelectorModal from "@console/modules/contents/attachments/components/AttachmentSelectorModal.vue";
|
||||||
} from "./extensions";
|
import type { Attachment } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
IconCalendar,
|
IconCalendar,
|
||||||
IconCharacterRecognition,
|
IconCharacterRecognition,
|
||||||
|
@ -66,8 +66,23 @@ import {
|
||||||
VTabItem,
|
VTabItem,
|
||||||
VTabs,
|
VTabs,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import AttachmentSelectorModal from "@console/modules/contents/attachments/components/AttachmentSelectorModal.vue";
|
import type { AttachmentLike } from "@halo-dev/console-shared";
|
||||||
import ExtensionCharacterCount from "@tiptap/extension-character-count";
|
import ExtensionCharacterCount from "@tiptap/extension-character-count";
|
||||||
|
import { useDebounceFn, useLocalStorage } from "@vueuse/core";
|
||||||
|
import type { AxiosRequestConfig } from "axios";
|
||||||
|
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
|
||||||
|
import {
|
||||||
|
inject,
|
||||||
|
markRaw,
|
||||||
|
nextTick,
|
||||||
|
onBeforeUnmount,
|
||||||
|
onMounted,
|
||||||
|
ref,
|
||||||
|
shallowRef,
|
||||||
|
watch,
|
||||||
|
type ComputedRef,
|
||||||
|
} from "vue";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import MdiFormatHeader1 from "~icons/mdi/format-header-1";
|
import MdiFormatHeader1 from "~icons/mdi/format-header-1";
|
||||||
import MdiFormatHeader2 from "~icons/mdi/format-header-2";
|
import MdiFormatHeader2 from "~icons/mdi/format-header-2";
|
||||||
import MdiFormatHeader3 from "~icons/mdi/format-header-3";
|
import MdiFormatHeader3 from "~icons/mdi/format-header-3";
|
||||||
|
@ -75,29 +90,14 @@ import MdiFormatHeader4 from "~icons/mdi/format-header-4";
|
||||||
import MdiFormatHeader5 from "~icons/mdi/format-header-5";
|
import MdiFormatHeader5 from "~icons/mdi/format-header-5";
|
||||||
import MdiFormatHeader6 from "~icons/mdi/format-header-6";
|
import MdiFormatHeader6 from "~icons/mdi/format-header-6";
|
||||||
import RiLayoutRightLine from "~icons/ri/layout-right-line";
|
import RiLayoutRightLine from "~icons/ri/layout-right-line";
|
||||||
import {
|
|
||||||
inject,
|
|
||||||
markRaw,
|
|
||||||
ref,
|
|
||||||
watch,
|
|
||||||
onMounted,
|
|
||||||
shallowRef,
|
|
||||||
type ComputedRef,
|
|
||||||
} from "vue";
|
|
||||||
import { formatDatetime } from "@/utils/date";
|
|
||||||
import { useAttachmentSelect } from "./composables/use-attachment";
|
import { useAttachmentSelect } from "./composables/use-attachment";
|
||||||
import type { Attachment } from "@halo-dev/api-client";
|
import {
|
||||||
import { useI18n } from "vue-i18n";
|
UiExtensionAudio,
|
||||||
import { i18n } from "@/locales";
|
UiExtensionImage,
|
||||||
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
|
UiExtensionUpload,
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
UiExtensionVideo,
|
||||||
import type { AttachmentLike, PluginModule } from "@halo-dev/console-shared";
|
} from "./extensions";
|
||||||
import { useDebounceFn, useLocalStorage } from "@vueuse/core";
|
|
||||||
import { onBeforeUnmount } from "vue";
|
|
||||||
import { usePermission } from "@/utils/permission";
|
|
||||||
import type { AxiosRequestConfig } from "axios";
|
|
||||||
import { getContents } from "./utils/attachment";
|
import { getContents } from "./utils/attachment";
|
||||||
import { nextTick } from "vue";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
|
@ -189,20 +189,21 @@ const handleCloseAttachmentSelectorModal = () => {
|
||||||
attachmentOptions.value = initAttachmentOptions;
|
attachmentOptions.value = initAttachmentOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
const extensionsFromPlugins: AnyExtension[] = [];
|
const extensionsFromPlugins: AnyExtension[] = [];
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["default:editor:extension:create"]) {
|
const callbackFunction =
|
||||||
return;
|
pluginModule?.extensionPoints?.["default:editor:extension:create"];
|
||||||
|
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const extensions = extensionPoints[
|
const extensions = await callbackFunction();
|
||||||
"default:editor:extension:create"
|
|
||||||
]() as [];
|
|
||||||
|
|
||||||
extensionsFromPlugins.push(...extensions);
|
extensionsFromPlugins.push(...extensions);
|
||||||
});
|
}
|
||||||
|
|
||||||
// debounce OnUpdate
|
// debounce OnUpdate
|
||||||
const debounceOnUpdate = useDebounceFn(() => {
|
const debounceOnUpdate = useDebounceFn(() => {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
import type { EditorProvider, PluginModule } from "@halo-dev/console-shared";
|
|
||||||
import { onMounted, ref, type Ref, defineAsyncComponent, markRaw } from "vue";
|
|
||||||
import { VLoading } from "@halo-dev/components";
|
|
||||||
import Logo from "@/assets/logo.png";
|
import Logo from "@/assets/logo.png";
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
|
import { VLoading } from "@halo-dev/components";
|
||||||
|
import type { EditorProvider } from "@halo-dev/console-shared";
|
||||||
|
import { defineAsyncComponent, markRaw, onMounted, ref, type Ref } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
|
||||||
interface useEditorExtensionPointsReturn {
|
interface useEditorExtensionPointsReturn {
|
||||||
|
@ -30,17 +30,23 @@ export function useEditorExtensionPoints(): useEditorExtensionPointsReturn {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(async () => {
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
for (const pluginModule of pluginModules) {
|
||||||
const { extensionPoints } = pluginModule;
|
try {
|
||||||
if (!extensionPoints?.["editor:create"]) {
|
const callbackFunction =
|
||||||
return;
|
pluginModule?.extensionPoints?.["editor:create"];
|
||||||
|
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = await callbackFunction();
|
||||||
|
|
||||||
|
editorProviders.value.push(...providers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const providers = extensionPoints["editor:create"]() as EditorProvider[];
|
|
||||||
|
|
||||||
editorProviders.value.push(...providers);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,33 +1,33 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import UserAvatar from "@/components/user-avatar/UserAvatar.vue";
|
||||||
|
import { usePluginModuleStore } from "@/stores/plugin";
|
||||||
import { apiClient } from "@/utils/api-client";
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import type { DetailedUser } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
VButton,
|
VButton,
|
||||||
VDropdown,
|
VDropdown,
|
||||||
VDropdownItem,
|
VDropdownItem,
|
||||||
VTabbar,
|
VTabbar,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
|
import type { UserProfileTab } from "@halo-dev/console-shared";
|
||||||
|
import { useQuery } from "@tanstack/vue-query";
|
||||||
|
import { useRouteQuery } from "@vueuse/router";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
markRaw,
|
markRaw,
|
||||||
onMounted,
|
onMounted,
|
||||||
provide,
|
provide,
|
||||||
type Ref,
|
|
||||||
ref,
|
ref,
|
||||||
toRaw,
|
toRaw,
|
||||||
|
type Ref,
|
||||||
} from "vue";
|
} from "vue";
|
||||||
import type { DetailedUser } from "@halo-dev/api-client";
|
|
||||||
import ProfileEditingModal from "./components/ProfileEditingModal.vue";
|
|
||||||
import PasswordChangeModal from "./components/PasswordChangeModal.vue";
|
|
||||||
import { useQuery } from "@tanstack/vue-query";
|
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import UserAvatar from "@/components/user-avatar/UserAvatar.vue";
|
import PasswordChangeModal from "./components/PasswordChangeModal.vue";
|
||||||
|
import ProfileEditingModal from "./components/ProfileEditingModal.vue";
|
||||||
import DetailTab from "./tabs/Detail.vue";
|
import DetailTab from "./tabs/Detail.vue";
|
||||||
import PersonalAccessTokensTab from "./tabs/PersonalAccessTokens.vue";
|
|
||||||
import { useRouteQuery } from "@vueuse/router";
|
|
||||||
import NotificationPreferences from "./tabs/NotificationPreferences.vue";
|
import NotificationPreferences from "./tabs/NotificationPreferences.vue";
|
||||||
|
import PersonalAccessTokensTab from "./tabs/PersonalAccessTokens.vue";
|
||||||
import TwoFactor from "./tabs/TwoFactor.vue";
|
import TwoFactor from "./tabs/TwoFactor.vue";
|
||||||
import type { PluginModule, UserProfileTab } from "@halo-dev/console-shared";
|
|
||||||
import { usePluginModuleStore } from "@/stores/plugin";
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -76,21 +76,24 @@ const tabs = ref<UserProfileTab[]>([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Collect uc:profile:tabs:create extension points
|
// Collect uc:profile:tabs:create extension points
|
||||||
onMounted(() => {
|
const { pluginModules } = usePluginModuleStore();
|
||||||
const { pluginModules } = usePluginModuleStore();
|
|
||||||
|
|
||||||
pluginModules.forEach((pluginModule: PluginModule) => {
|
onMounted(async () => {
|
||||||
const { extensionPoints } = pluginModule;
|
for (const pluginModule of pluginModules) {
|
||||||
if (!extensionPoints?.["uc:user:profile:tabs:create"]) {
|
try {
|
||||||
return;
|
const callbackFunction =
|
||||||
|
pluginModule?.extensionPoints?.["uc:user:profile:tabs:create"];
|
||||||
|
if (typeof callbackFunction !== "function") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providers = await callbackFunction();
|
||||||
|
|
||||||
|
tabs.value.push(...providers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error processing plugin module:`, pluginModule, error);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const providers = extensionPoints[
|
|
||||||
"uc:user:profile:tabs:create"
|
|
||||||
]() as UserProfileTab[];
|
|
||||||
|
|
||||||
tabs.value.push(...providers);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const tabbarItems = computed(() => {
|
const tabbarItems = computed(() => {
|
||||||
|
|
Loading…
Reference in New Issue