Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

Commit

Permalink
refactor: plugin extension points of console plugin (#738)
Browse files Browse the repository at this point in the history
#### What type of PR is this?

/kind improvement
/milestone 2.0

#### What this PR does / why we need it:

重构 Console 端插件扩展点的定义方式。现在需要如下定义:

```ts
  extensionPoints: {
    "page:functional:create": () => {
      return [
        {
          name: "链接",
          url: "/links",
          path: "/pages/functional/links",
          permissions: ["plugin:links:view"],
        },
      ];
    },
  },
```

#### Special notes for your reviewer:

可以用以下插件进行测试:

- https://github.com/halo-sigs/plugin-links
- https://github.com/halo-sigs/plugin-unsplash

#### Does this PR introduce a user-facing change?


```release-note
None
```
  • Loading branch information
ruibaby authored Nov 30, 2022
1 parent 7e2a285 commit 8b24cee
Show file tree
Hide file tree
Showing 20 changed files with 95 additions and 106 deletions.
4 changes: 2 additions & 2 deletions packages/shared/src/core/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Plugin } from "../types/plugin";
import type { PluginModule } from "../types/plugin";

export function definePlugin(plugin: Plugin): Plugin {
export function definePlugin(plugin: PluginModule): PluginModule {
return plugin;
}
6 changes: 1 addition & 5 deletions packages/shared/src/states/attachment-selector.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import type { Attachment } from "@halo-dev/api-client";
import type { Component } from "vue";

export interface AttachmentSelectorPublicState {
providers: AttachmentProvider[];
}

export type AttachmentLike =
| Attachment
| string
Expand All @@ -13,7 +9,7 @@ export type AttachmentLike =
type: string;
};

export interface AttachmentProvider {
export interface AttachmentSelectProvider {
id: string;
label: string;
component: Component | string;
Expand Down
6 changes: 1 addition & 5 deletions packages/shared/src/states/pages.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
export interface PagesPublicState {
functionalPages: FunctionalPagesState[];
}

export interface FunctionalPagesState {
export interface FunctionalPage {
name: string;
path: string;
url?: string;
Expand Down
26 changes: 12 additions & 14 deletions packages/shared/src/types/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import type { Component, Ref } from "vue";
import type { Component } from "vue";
import type { RouteRecordRaw, RouteRecordName } from "vue-router";
import type { PagesPublicState } from "../states/pages";
import type { AttachmentSelectorPublicState } from "../states/attachment-selector";

export type ExtensionPointName = "PAGES" | "POSTS" | "ATTACHMENT_SELECTOR";

export type ExtensionPointState =
| PagesPublicState
| AttachmentSelectorPublicState;
import type { FunctionalPage } from "../states/pages";
import type { AttachmentSelectProvider } from "../states/attachment-selector";

export interface RouteRecordAppend {
parentName: RouteRecordName;
route: RouteRecordRaw;
}

export interface Plugin {
name: string;
export interface ExtensionPoint {
"page:functional:create"?: () => FunctionalPage[] | Promise<FunctionalPage[]>;

"attachment:selector:create"?: () =>
| AttachmentSelectProvider[]
| Promise<AttachmentSelectProvider[]>;
}

export interface PluginModule {
/**
* These components will be registered when plugin is activated.
*/
Expand All @@ -34,7 +34,5 @@ export interface Plugin {

routes?: RouteRecordRaw[] | RouteRecordAppend[];

extensionPoints?: {
[key in ExtensionPointName]?: (state: Ref<ExtensionPointState>) => void;
};
extensionPoints?: ExtensionPoint;
}
23 changes: 0 additions & 23 deletions src/composables/usePlugins.ts

This file was deleted.

14 changes: 5 additions & 9 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { DirectiveBinding } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import type { Plugin, RouteRecordAppend } from "@halo-dev/console-shared";
import type { PluginModule, RouteRecordAppend } from "@halo-dev/console-shared";
import { Toast } from "@halo-dev/components";
import { apiClient } from "@/utils/api-client";
// setup
Expand All @@ -12,7 +12,7 @@ import { setupComponents } from "./setup/setupComponents";
// core modules
import { coreModules } from "./modules";
import { useScriptTag } from "@vueuse/core";
import { usePluginStore } from "@/stores/plugin";
import { usePluginModuleStore } from "@/stores/plugin";
import { hasPermission } from "@/utils/permission";
import { useRoleStore } from "@/stores/role";
import type { RouteRecordRaw } from "vue-router";
Expand All @@ -26,7 +26,7 @@ setupComponents(app);

app.use(createPinia());

function registerModule(pluginModule: Plugin, core: boolean) {
function registerModule(pluginModule: PluginModule, core: boolean) {
if (pluginModule.components) {
Object.keys(pluginModule.components).forEach((key) => {
const component = pluginModule.components?.[key];
Expand All @@ -38,7 +38,6 @@ function registerModule(pluginModule: Plugin, core: boolean) {

if (pluginModule.routes) {
if (!Array.isArray(pluginModule.routes)) {
console.error(`${pluginModule.name}: Plugin routes must be an array`);
return;
}

Expand Down Expand Up @@ -86,7 +85,7 @@ function loadCoreModules() {
});
}

const pluginStore = usePluginStore();
const pluginModuleStore = usePluginModuleStore();

function loadStyle(href: string) {
return new Promise(function (resolve, reject) {
Expand Down Expand Up @@ -144,9 +143,8 @@ async function loadPluginModules() {
const pluginModule = window[plugin.metadata.name];

if (pluginModule) {
// @ts-ignore
plugin.spec.module = pluginModule;
registerModule(pluginModule, false);
pluginModuleStore.registerPluginModule(pluginModule);
}
} catch (e) {
const message = `${plugin.metadata.name}: 加载插件入口文件失败`;
Expand All @@ -164,8 +162,6 @@ async function loadPluginModules() {
pluginErrorMessages.push(message);
}
}

pluginStore.registerPlugin(plugin);
}

if (pluginErrorMessages.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script lang="ts" setup>
import { VButton, VModal, VTabbar } from "@halo-dev/components";
import { ref, markRaw } from "vue";
import { ref, markRaw, onMounted } from "vue";
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
import UploadSelectorProvider from "./selector-providers/UploadSelectorProvider.vue";
import type {
AttachmentLike,
AttachmentSelectorPublicState,
AttachmentSelectProvider,
PluginModule,
} from "@halo-dev/console-shared";
import { useExtensionPointsState } from "@/composables/usePlugins";
import { usePluginModuleStore } from "@/stores/plugin";
withDefaults(
defineProps<{
Expand All @@ -26,24 +27,42 @@ const emit = defineEmits<{
const selected = ref<AttachmentLike[]>([] as AttachmentLike[]);
const attachmentSelectorPublicState = ref<AttachmentSelectorPublicState>({
providers: [
{
id: "core",
label: "附件库",
component: markRaw(CoreSelectorProvider),
},
{
id: "upload",
label: "上传",
component: markRaw(UploadSelectorProvider),
},
],
});
const attachmentSelectProviders = ref<AttachmentSelectProvider[]>([
{
id: "core",
label: "附件库",
component: markRaw(CoreSelectorProvider),
},
{
id: "upload",
label: "上传",
component: markRaw(UploadSelectorProvider),
},
]);
// resolve plugin extension points
const { pluginModules } = usePluginModuleStore();
useExtensionPointsState("ATTACHMENT_SELECTOR", attachmentSelectorPublicState);
onMounted(() => {
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["attachment:selector:create"]) {
return;
}
const providers = extensionPoints[
"attachment:selector:create"
]() as AttachmentSelectProvider[];
if (providers) {
providers.forEach((provider) => {
attachmentSelectProviders.value.push(provider);
});
}
});
});
const activeId = ref(attachmentSelectorPublicState.value.providers[0].id);
const activeId = ref(attachmentSelectProviders.value[0].id);
const onVisibleChange = (visible: boolean) => {
emit("update:visible", visible);
Expand All @@ -53,7 +72,7 @@ const onVisibleChange = (visible: boolean) => {
};
const onChangeProvider = (providerId: string) => {
const provider = attachmentSelectorPublicState.value.providers.find(
const provider = attachmentSelectProviders.value.find(
(provider) => provider.id === providerId
);
Expand All @@ -80,14 +99,14 @@ const handleConfirm = () => {
>
<VTabbar
v-model:active-id="activeId"
:items="attachmentSelectorPublicState.providers"
:items="attachmentSelectProviders"
class="w-full"
type="outline"
></VTabbar>

<div v-if="visible" class="mt-2">
<template
v-for="(provider, index) in attachmentSelectorPublicState.providers"
v-for="(provider, index) in attachmentSelectProviders"
:key="index"
>
<Suspense>
Expand Down
1 change: 0 additions & 1 deletion src/modules/contents/attachments/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { IconFolder } from "@halo-dev/components";
import { markRaw } from "vue";

export default definePlugin({
name: "attachmentModule",
components: {
AttachmentSelectorModal,
},
Expand Down
1 change: 0 additions & 1 deletion src/modules/contents/comments/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import CommentStatsWidget from "./widgets/CommentStatsWidget.vue";
import { markRaw } from "vue";

export default definePlugin({
name: "commentModule",
components: {
CommentStatsWidget,
},
Expand Down
36 changes: 27 additions & 9 deletions src/modules/contents/pages/FunctionalPageList.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref } from "vue";
import { onMounted, ref } from "vue";
import {
VEmpty,
VSpace,
Expand All @@ -8,19 +8,37 @@ import {
VEntity,
VEntityField,
} from "@halo-dev/components";
import type { PagesPublicState } from "@halo-dev/console-shared";
import { useExtensionPointsState } from "@/composables/usePlugins";
import type { FunctionalPage, PluginModule } from "@halo-dev/console-shared";
import { usePluginModuleStore } from "@/stores/plugin";
const pagesPublicState = ref<PagesPublicState>({
functionalPages: [],
});
const functionalPages = ref<FunctionalPage[]>([] as FunctionalPage[]);
// resolve plugin extension points
const { pluginModules } = usePluginModuleStore();
onMounted(() => {
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.["page:functional:create"]) {
return;
}
useExtensionPointsState("PAGES", pagesPublicState);
const pages = extensionPoints[
"page:functional:create"
]() as FunctionalPage[];
if (pages) {
pages.forEach((page) => {
functionalPages.value.push(page);
});
}
});
});
</script>

<template>
<VEmpty
v-if="!pagesPublicState.functionalPages.length"
v-if="!functionalPages.length"
message="当前没有功能页面,功能页面通常由各个插件提供,你可以尝试安装新插件以获得支持"
title="当前没有功能页面"
>
Expand All @@ -41,7 +59,7 @@ useExtensionPointsState("PAGES", pagesPublicState);
role="list"
>
<li
v-for="(page, index) in pagesPublicState.functionalPages"
v-for="(page, index) in functionalPages"
:key="index"
v-permission="page.permissions"
>
Expand Down
1 change: 0 additions & 1 deletion src/modules/contents/pages/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { IconPages } from "@halo-dev/components";
import { markRaw } from "vue";

export default definePlugin({
name: "pageModule",
components: {
SinglePageStatsWidget,
},
Expand Down
1 change: 0 additions & 1 deletion src/modules/contents/posts/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import RecentPublishedWidget from "./widgets/RecentPublishedWidget.vue";
import { markRaw } from "vue";

export default definePlugin({
name: "postModule",
components: {
PostStatsWidget,
RecentPublishedWidget,
Expand Down
1 change: 0 additions & 1 deletion src/modules/dashboard/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import ViewsStatsWidget from "./widgets/ViewsStatsWidget.vue";
import { markRaw } from "vue";

export default definePlugin({
name: "dashboardModule",
components: {
QuickLinkWidget,
ViewsStatsWidget,
Expand Down
1 change: 0 additions & 1 deletion src/modules/interface/menus/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { IconListSettings } from "@halo-dev/components";
import { markRaw } from "vue";

export default definePlugin({
name: "menuModule",
components: {},
routes: [
{
Expand Down
1 change: 0 additions & 1 deletion src/modules/interface/themes/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { IconPalette } from "@halo-dev/components";
import { markRaw } from "vue";

export default definePlugin({
name: "themeModule",
components: {},
routes: [
{
Expand Down
1 change: 0 additions & 1 deletion src/modules/system/plugins/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { IconPlug } from "@halo-dev/components";
import { markRaw } from "vue";

export default definePlugin({
name: "pluginModule",
components: {},
routes: [
{
Expand Down
Loading

1 comment on commit 8b24cee

@vercel
Copy link

@vercel vercel bot commented on 8b24cee Nov 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

ui – ./

halo-admin-ui.vercel.app
ui-git-main-halo-dev.vercel.app
ui-halo-dev.vercel.app

Please sign in to comment.