diff --git a/console/docs/extension-points/comment-subject-ref.md b/console/docs/extension-points/comment-subject-ref.md new file mode 100644 index 000000000..5c3e4674f --- /dev/null +++ b/console/docs/extension-points/comment-subject-ref.md @@ -0,0 +1,59 @@ +# 评论来源显示拓展点 + +在 Console 中,评论管理列表的评论来源默认仅支持显示来自文章和页面的评论,如果其他插件中的业务模块也使用了评论,那么就可以通过该拓展点来扩展评论来源的显示。 + +## 定义方式 + +假设以文章为例: + +```ts +import { definePlugin } from "@halo-dev/console-shared"; +import type { CommentSubjectRefResult } from "@halo-dev/console-shared"; +import type { Extension } from "@halo-dev/api-client"; +import type { Post } from "./types"; + +export default definePlugin({ + components: {}, + extensionPoints: { + "comment:subject-ref:create": () => { + return [ + { + kind: "Post", + group: "post.halo.run", + resolve: (subject: Extension): CommentSubjectRefResult => { + const post = subject as Post; + return { + label: "文章", + title: post.spec.title, + externalUrl: post.status.permalink, + route: { + name: "PostEditor", + params: { + name: post.metadata.name + } + }, + }; + }, + }, + ]; + }, + }, +}); +``` + +类型定义如下: + +```ts +type CommentSubjectRefProvider = { + kind: string; // 自定义模型的类型 + group: string; // 自定义模型的分组 + resolve: (subject: Extension) => CommentSubjectRefResult; +} + +interface CommentSubjectRefResult { + label: string; // 来源名称(类型) + title: string; // 来源标题 + route?: RouteLocationRaw; // Console 的路由,可以设置为来源的详情或者编辑页面 + externalUrl?: string; // 访问地址,可以设置为前台资源的访问地址 +} +``` diff --git a/console/packages/shared/src/index.ts b/console/packages/shared/src/index.ts index 9f18df67a..5f5917436 100644 --- a/console/packages/shared/src/index.ts +++ b/console/packages/shared/src/index.ts @@ -4,3 +4,4 @@ export * from "./core/plugins"; export * from "./states/pages"; export * from "./states/attachment-selector"; export * from "./states/editor"; +export * from "./states/comment-subject-ref"; diff --git a/console/packages/shared/src/states/comment-subject-ref.ts b/console/packages/shared/src/states/comment-subject-ref.ts new file mode 100644 index 000000000..4e8a33183 --- /dev/null +++ b/console/packages/shared/src/states/comment-subject-ref.ts @@ -0,0 +1,15 @@ +import type { Extension } from "@halo-dev/api-client"; +import type { RouteLocationRaw } from "vue-router"; + +export interface CommentSubjectRefResult { + label: string; + title: string; + route?: RouteLocationRaw; + externalUrl?: string; +} + +export type CommentSubjectRefProvider = { + kind: string; + group: string; + resolve: (subject: Extension) => CommentSubjectRefResult; +}; diff --git a/console/packages/shared/src/types/plugin.ts b/console/packages/shared/src/types/plugin.ts index a9898da75..2ea41584a 100644 --- a/console/packages/shared/src/types/plugin.ts +++ b/console/packages/shared/src/types/plugin.ts @@ -3,6 +3,7 @@ import type { RouteRecordRaw, RouteRecordName } from "vue-router"; import type { FunctionalPage } from "../states/pages"; import type { AttachmentSelectProvider } from "../states/attachment-selector"; import type { EditorProvider } from ".."; +import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; export interface RouteRecordAppend { parentName: RouteRecordName; @@ -18,6 +19,8 @@ export interface ExtensionPoint { | Promise; "editor:create"?: () => EditorProvider[] | Promise; + + "comment:subject-ref:create"?: () => CommentSubjectRefProvider[]; } export interface PluginModule { diff --git a/console/src/modules/contents/comments/components/CommentListItem.vue b/console/src/modules/contents/comments/components/CommentListItem.vue index 869e68ae3..9caa6c37a 100644 --- a/console/src/modules/contents/comments/components/CommentListItem.vue +++ b/console/src/modules/contents/comments/components/CommentListItem.vue @@ -24,14 +24,18 @@ import type { SinglePage, } from "@halo-dev/api-client"; import { formatDatetime } from "@/utils/date"; -import { computed, provide, ref, type Ref } from "vue"; +import { computed, provide, ref, onMounted, type Ref } from "vue"; import ReplyListItem from "./ReplyListItem.vue"; import { apiClient } from "@/utils/api-client"; -import type { RouteLocationRaw } from "vue-router"; import cloneDeep from "lodash.clonedeep"; import { usePermission } from "@/utils/permission"; import { useQuery } from "@tanstack/vue-query"; import { useI18n } from "vue-i18n"; +import { usePluginModuleStore, type PluginModule } from "@/stores/plugin"; +import type { + CommentSubjectRefProvider, + CommentSubjectRefResult, +} from "packages/shared/dist"; const { currentUserHasPermission } = usePermission(); const { t } = useI18n(); @@ -195,18 +199,11 @@ const onReplyCreationModalClose = () => { }; // Subject ref processing -interface SubjectRefResult { - label: string; - title: string; - route?: RouteLocationRaw; - externalUrl?: string; -} - -const SubjectRefProvider = ref< - Record SubjectRefResult>[] ->([ +const SubjectRefProviders = ref([ { - Post: (subject: Extension): SubjectRefResult => { + kind: "Post", + group: "content.halo.run", + resolve: (subject: Extension): CommentSubjectRefResult => { const post = subject as Post; return { label: t("core.comment.subject_refs.post"), @@ -222,7 +219,9 @@ const SubjectRefProvider = ref< }, }, { - SinglePage: (subject: Extension): SubjectRefResult => { + kind: "SinglePage", + group: "content.halo.run", + resolve: (subject: Extension): CommentSubjectRefResult => { const singlePage = subject as SinglePage; return { label: t("core.comment.subject_refs.page"), @@ -239,6 +238,27 @@ const SubjectRefProvider = ref< }, ]); +onMounted(() => { + const { pluginModules } = usePluginModuleStore(); + + pluginModules.forEach((pluginModule: PluginModule) => { + const { extensionPoints } = pluginModule; + if (!extensionPoints?.["comment:subject-ref:create"]) { + return; + } + + const providers = extensionPoints[ + "comment:subject-ref:create" + ]() as CommentSubjectRefProvider[]; + + if (providers) { + providers.forEach((provider) => { + SubjectRefProviders.value.push(provider); + }); + } + }); +}); + const subjectRefResult = computed(() => { const { subject } = props.comment; if (!subject) { @@ -247,8 +267,10 @@ const subjectRefResult = computed(() => { title: t("core.comment.subject_refs.unknown"), }; } - const subjectRef = SubjectRefProvider.value.find((provider) => - Object.keys(provider).includes(subject.kind) + const subjectRef = SubjectRefProviders.value.find( + (provider) => + provider.kind === subject.kind && + subject.apiVersion.startsWith(provider.group) ); if (!subjectRef) { return { @@ -256,7 +278,7 @@ const subjectRefResult = computed(() => { title: t("core.comment.subject_refs.unknown"), }; } - return subjectRef[subject.kind](subject); + return subjectRef.resolve(subject); });