mirror of https://github.com/halo-dev/halo
feat: make comment subject ref provider extensible (#4039)
#### What type of PR is this? /area console /kind feature #### What this PR does / why we need it: 让评论来源的显示支持通过插件扩展,目前如 [瞬间](https://github.com/halo-sigs/plugin-moments) 这类的插件如果使用了评论模块,那么在评论管理是无法显示具体来源的: <img width="627" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/0df354dc-ed42-4217-abbd-5bce67329e0d"> 此 PR 为 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 { Moment } from "./types"; export default definePlugin({ components: {}, extensionPoints: { "comment:subject-ref:create": () => { return [ { kind: "Moment", group: "moment.halo.run", resolve: (subject: Extension): CommentSubjectRefResult => { const moment = subject as Moment; return { label: "瞬间", title: determineMomentTitle(moment), externalUrl: `/moments/${moment.metadata.name}`, route: { name: "Moments", }, }; }, }, ]; }, }, }); ``` #### Which issue(s) this PR fixes: Fixes #3554 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note Console 端的评论来源显示支持通过插件扩展 ```pull/4114/head^2
parent
ac47942a04
commit
8c05a6d30e
|
@ -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; // 访问地址,可以设置为前台资源的访问地址
|
||||
}
|
||||
```
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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<AttachmentSelectProvider[]>;
|
||||
|
||||
"editor:create"?: () => EditorProvider[] | Promise<EditorProvider[]>;
|
||||
|
||||
"comment:subject-ref:create"?: () => CommentSubjectRefProvider[];
|
||||
}
|
||||
|
||||
export interface PluginModule {
|
||||
|
|
|
@ -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<string, (subject: Extension) => SubjectRefResult>[]
|
||||
>([
|
||||
const SubjectRefProviders = ref<CommentSubjectRefProvider[]>([
|
||||
{
|
||||
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);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in New Issue