mirror of https://github.com/halo-dev/halo
feat: make comment and reply list item operations extendable (#6438)
#### What type of PR is this? /area console /kind feature /milestone 2.18.x #### What this PR does / why we need it: 评论和回复管理列表项的操作按钮支持被插件扩展。 ![image](https://github.com/user-attachments/assets/20174eda-ec46-4ab2-b1d9-c27e4aaa6cba) ![image](https://github.com/user-attachments/assets/ac44c221-71f5-4077-8116-a92245c22697) #### Which issue(s) this PR fixes: Fixes #6392 #### Special notes for your reviewer: 需要评论和回复的关于列表的已有功能是否正常。 如果需要测试扩展点是否有效,可以使用此插件测试:[plugin-starter-1.0.0-SNAPSHOT.jar.zip](https://github.com/user-attachments/files/16482348/plugin-starter-1.0.0-SNAPSHOT.jar.zip) ``` export default definePlugin({ components: {}, routes: [], extensionPoints: { "comment:list-item:operation:create": (comment: Ref<ListedComment>) => { return [ { priority: 21, component: markRaw(VDropdownItem), label: "测试评论菜单", visible: true, permissions: [], action: () => { console.log(comment) }, }, ]; }, "reply:list-item:operation:create": (reply: Ref<ListedReply>) => { return [ { priority: 11, component: markRaw(VDropdownItem), label: "测试回复菜单", visible: true, permissions: [], action: () => { console.log(reply) }, }, ]; }, }, }); ``` #### Does this PR introduce a user-facing change? ```release-note Console 评论和回复管理列表项的操作按钮支持被插件扩展。 ```pull/6442/head
parent
684c3b045b
commit
4a4f8b655d
|
@ -29,12 +29,23 @@ import {
|
||||||
import type {
|
import type {
|
||||||
CommentSubjectRefProvider,
|
CommentSubjectRefProvider,
|
||||||
CommentSubjectRefResult,
|
CommentSubjectRefResult,
|
||||||
|
OperationItem,
|
||||||
} from "@halo-dev/console-shared";
|
} from "@halo-dev/console-shared";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
|
||||||
import { computed, onMounted, provide, ref, type Ref } from "vue";
|
import {
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
provide,
|
||||||
|
ref,
|
||||||
|
type Ref,
|
||||||
|
toRefs,
|
||||||
|
markRaw,
|
||||||
|
} from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
||||||
import ReplyListItem from "./ReplyListItem.vue";
|
import ReplyListItem from "./ReplyListItem.vue";
|
||||||
|
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
|
||||||
|
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
|
||||||
|
|
||||||
const { currentUserHasPermission } = usePermission();
|
const { currentUserHasPermission } = usePermission();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -50,6 +61,8 @@ const props = withDefaults(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { comment } = toRefs(props);
|
||||||
|
|
||||||
const hoveredReply = ref<ListedReply>();
|
const hoveredReply = ref<ListedReply>();
|
||||||
const showReplies = ref(false);
|
const showReplies = ref(false);
|
||||||
const replyModal = ref(false);
|
const replyModal = ref(false);
|
||||||
|
@ -293,6 +306,35 @@ const subjectRefResult = computed(() => {
|
||||||
}
|
}
|
||||||
return subjectRef.resolve(subject);
|
return subjectRef.resolve(subject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { operationItems } = useOperationItemExtensionPoint<ListedComment>(
|
||||||
|
"comment:list-item:operation:create",
|
||||||
|
comment,
|
||||||
|
computed((): OperationItem<ListedComment>[] => [
|
||||||
|
{
|
||||||
|
priority: 0,
|
||||||
|
component: markRaw(VDropdownItem),
|
||||||
|
label: t("core.comment.operations.approve_comment_in_batch.button"),
|
||||||
|
action: handleApprove,
|
||||||
|
hidden: props.comment?.comment.spec.approved,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
priority: 10,
|
||||||
|
component: markRaw(VDropdownItem),
|
||||||
|
label: t("core.comment.operations.approve_applies_in_batch.button"),
|
||||||
|
action: handleApproveReplyInBatch,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
priority: 20,
|
||||||
|
component: markRaw(VDropdownItem),
|
||||||
|
props: {
|
||||||
|
type: "danger",
|
||||||
|
},
|
||||||
|
label: t("core.common.buttons.delete"),
|
||||||
|
action: handleDelete,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -419,18 +461,7 @@ const subjectRefResult = computed(() => {
|
||||||
v-if="currentUserHasPermission(['system:comments:manage'])"
|
v-if="currentUserHasPermission(['system:comments:manage'])"
|
||||||
#dropdownItems
|
#dropdownItems
|
||||||
>
|
>
|
||||||
<VDropdownItem
|
<EntityDropdownItems :dropdown-items="operationItems" :item="comment" />
|
||||||
v-if="!comment?.comment.spec.approved"
|
|
||||||
@click="handleApprove"
|
|
||||||
>
|
|
||||||
{{ $t("core.comment.operations.approve_comment_in_batch.button") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<VDropdownItem @click="handleApproveReplyInBatch">
|
|
||||||
{{ $t("core.comment.operations.approve_applies_in_batch.button") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<VDropdownItem type="danger" @click="handleDelete">
|
|
||||||
{{ $t("core.common.buttons.delete") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="showReplies" #footer>
|
<template v-if="showReplies" #footer>
|
||||||
|
|
|
@ -13,10 +13,13 @@ import {
|
||||||
VStatusDot,
|
VStatusDot,
|
||||||
VTag,
|
VTag,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
|
import type { OperationItem } from "@halo-dev/console-shared";
|
||||||
import { useQueryClient } from "@tanstack/vue-query";
|
import { useQueryClient } from "@tanstack/vue-query";
|
||||||
import { computed, inject, ref, type Ref } from "vue";
|
import { computed, inject, ref, type Ref, toRefs, markRaw } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
||||||
|
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
|
||||||
|
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
@ -33,6 +36,8 @@ const props = withDefaults(
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { reply } = toRefs(props);
|
||||||
|
|
||||||
const quoteReply = computed(() => {
|
const quoteReply = computed(() => {
|
||||||
const { quoteReply: replyName } = props.reply.reply.spec;
|
const { quoteReply: replyName } = props.reply.reply.spec;
|
||||||
|
|
||||||
|
@ -119,6 +124,31 @@ function onReplyCreationModalClose() {
|
||||||
});
|
});
|
||||||
replyModal.value = false;
|
replyModal.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { operationItems } = useOperationItemExtensionPoint<ListedReply>(
|
||||||
|
"reply:list-item:operation:create",
|
||||||
|
reply,
|
||||||
|
computed((): OperationItem<ListedReply>[] => [
|
||||||
|
{
|
||||||
|
priority: 0,
|
||||||
|
component: markRaw(VDropdownItem),
|
||||||
|
label: t("core.comment.operations.approve_reply.button"),
|
||||||
|
permissions: ["system:comments:manage"],
|
||||||
|
action: handleApprove,
|
||||||
|
hidden: props.reply?.reply.spec.approved,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
priority: 10,
|
||||||
|
component: markRaw(VDropdownItem),
|
||||||
|
props: {
|
||||||
|
type: "danger",
|
||||||
|
},
|
||||||
|
label: t("core.common.buttons.delete"),
|
||||||
|
permissions: ["system:comments:manage"],
|
||||||
|
action: handleDelete,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -214,20 +244,7 @@ function onReplyCreationModalClose() {
|
||||||
</VEntityField>
|
</VEntityField>
|
||||||
</template>
|
</template>
|
||||||
<template #dropdownItems>
|
<template #dropdownItems>
|
||||||
<VDropdownItem
|
<EntityDropdownItems :dropdown-items="operationItems" :item="reply" />
|
||||||
v-if="!reply?.reply.spec.approved"
|
|
||||||
v-permission="['system:comments:manage']"
|
|
||||||
@click="handleApprove"
|
|
||||||
>
|
|
||||||
{{ $t("core.comment.operations.approve_reply.button") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
<VDropdownItem
|
|
||||||
v-permission="['system:comments:manage']"
|
|
||||||
type="danger"
|
|
||||||
@click="handleDelete"
|
|
||||||
>
|
|
||||||
{{ $t("core.common.buttons.delete") }}
|
|
||||||
</VDropdownItem>
|
|
||||||
</template>
|
</template>
|
||||||
</VEntity>
|
</VEntity>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
目前支持扩展的数据列表:
|
目前支持扩展的数据列表:
|
||||||
|
|
||||||
- 文章:`"post:list-item:operation:create"?: (post: Ref<ListedPost>) => | OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>`
|
- 文章:`"post:list-item:operation:create"?: (post: Ref<ListedPost>) => | OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>`
|
||||||
|
- 评论:`"comment:list-item:operation:create"?: (comment: Ref<ListedComment>) => | OperationItem<ListedComment>[] | Promise<OperationItem<ListedComment>[]>`
|
||||||
|
- 回复:`"reply:list-item:operation:create"?: (reply: Ref<ListedReply>) => | OperationItem<ListedReply>[] | Promise<OperationItem<ListedReply>[]>`
|
||||||
- 插件:`"plugin:list-item:operation:create"?: (plugin: Ref<Plugin>) => | OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>`
|
- 插件:`"plugin:list-item:operation:create"?: (plugin: Ref<Plugin>) => | OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>`
|
||||||
- 备份:`"backup:list-item:operation:create"?: (backup: Ref<Backup>) => | OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>`
|
- 备份:`"backup:list-item:operation:create"?: (backup: Ref<Backup>) => | OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>`
|
||||||
- 主题:`"theme:list-item:operation:create"?: (theme: Ref<Theme>) => | OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>`
|
- 主题:`"theme:list-item:operation:create"?: (theme: Ref<Theme>) => | OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>`
|
||||||
|
|
|
@ -11,6 +11,8 @@ import type {
|
||||||
ListedPost,
|
ListedPost,
|
||||||
Plugin,
|
Plugin,
|
||||||
Theme,
|
Theme,
|
||||||
|
ListedComment,
|
||||||
|
ListedReply,
|
||||||
} from "@halo-dev/api-client";
|
} from "@halo-dev/api-client";
|
||||||
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
import type { AnyExtension } from "@halo-dev/richtext-editor";
|
||||||
import type { Component, Ref } from "vue";
|
import type { Component, Ref } from "vue";
|
||||||
|
@ -52,6 +54,14 @@ export interface ExtensionPoint {
|
||||||
post: Ref<ListedPost>
|
post: Ref<ListedPost>
|
||||||
) => OperationItem<ListedPost>[];
|
) => OperationItem<ListedPost>[];
|
||||||
|
|
||||||
|
"comment:list-item:operation:create"?: (
|
||||||
|
comment: Ref<ListedComment>
|
||||||
|
) => OperationItem<ListedComment>[];
|
||||||
|
|
||||||
|
"reply:list-item:operation:create"?: (
|
||||||
|
reply: Ref<ListedReply>
|
||||||
|
) => OperationItem<ListedReply>[];
|
||||||
|
|
||||||
"plugin:list-item:operation:create"?: (
|
"plugin:list-item:operation:create"?: (
|
||||||
plugin: Ref<Plugin>
|
plugin: Ref<Plugin>
|
||||||
) => OperationItem<Plugin>[];
|
) => OperationItem<Plugin>[];
|
||||||
|
|
Loading…
Reference in New Issue