refactor: operation and entity field extension points (#4530)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.9.x

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

重构数据列表操作项和显示字段扩展点的类型定义和条件判断:

1. `EntityDropdownItem` 改为 `OperationItem` 以适配不同的场景,最开始仅仅是为了提供给 Entity 组件,但后面发现如主题管理列表并没有使用 Entity 组件,所以定义统一改为 Operation 。
2. 修改 `OperationItem` 和 `EntityFieldItem` 的 `visible` 字段为 `hidden`,方便渲染的时候判断,并方便调用方默认不设置值。

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

```release-note
None
```
pull/4510/head
Ryan Wang 2023-08-31 05:36:12 -05:00 committed by GitHub
parent 8eaedd6ee8
commit 1892dce64b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 128 additions and 133 deletions

View File

@ -48,6 +48,6 @@ export interface EntityFieldItem {
component: Raw<Component>; // 字段组件,可以使用 `@halo-dev/components` 中提供的 `VEntityField`,也可以自定义 component: Raw<Component>; // 字段组件,可以使用 `@halo-dev/components` 中提供的 `VEntityField`,也可以自定义
props?: Record<string, unknown>; // 组件的 props props?: Record<string, unknown>; // 组件的 props
permissions?: string[]; // 权限设置 permissions?: string[]; // 权限设置
visible?: boolean; // 是否可见 hidden?: boolean; // 是否隐藏
} }
``` ```

View File

@ -8,10 +8,10 @@
目前支持扩展的数据列表: 目前支持扩展的数据列表:
- 文章:`"post:list-item:operation:create"?: (post: Ref<ListedPost>) => | EntityDropdownItem<ListedPost>[] | Promise<EntityDropdownItem<ListedPost>[]>` - 文章:`"post:list-item:operation:create"?: (post: Ref<ListedPost>) => | OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>`
- 插件:`"plugin:list-item:operation:create"?: (plugin: Ref<Plugin>) => | EntityDropdownItem<Plugin>[] | Promise<EntityDropdownItem<Plugin>[]>` - 插件:`"plugin:list-item:operation:create"?: (plugin: Ref<Plugin>) => | OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>`
- 备份:`"backup:list-item:operation:create"?: (backup: Ref<Backup>) => | EntityDropdownItem<Backup>[] | Promise<EntityDropdownItem<Backup>[]>` - 备份:`"backup:list-item:operation:create"?: (backup: Ref<Backup>) => | OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>`
- 主题:`"theme:list-item:operation:create"?: (theme: Ref<Theme>) => | EntityDropdownItem<Theme>[] | Promise<EntityDropdownItem<Theme>[]>` - 主题:`"theme:list-item:operation:create"?: (theme: Ref<Theme>) => | OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>`
示例: 示例:
@ -32,7 +32,6 @@ export default definePlugin({
priority: 21, priority: 21,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: "导出为 Markdown 文档", label: "导出为 Markdown 文档",
visible: true,
permissions: [], permissions: [],
action: async (post: ListedPost) => { action: async (post: ListedPost) => {
const { data } = await axios.get( const { data } = await axios.get(
@ -54,17 +53,17 @@ export default definePlugin({
}); });
``` ```
`EntityDropdownItem` 类型: `OperationItem` 类型:
```ts ```ts
export interface EntityDropdownItem<T> { export interface OperationItem<T> {
priority: number; // 优先级,越小越靠前 priority: number; // 优先级,越小越靠前
component: Raw<Component>; // 菜单项组件,可以使用 `@halo-dev/components` 中提供的 `VDropdownItem`,也可以自定义 component: Raw<Component>; // 菜单项组件,可以使用 `@halo-dev/components` 中提供的 `VDropdownItem`,也可以自定义
props?: Record<string, unknown>; // 组件的 props props?: Record<string, unknown>; // 组件的 props
action?: (item?: T) => void; // 点击事件 action?: (item?: T) => void; // 点击事件
label?: string; // 菜单项名称 label?: string; // 菜单项名称
visible?: boolean; // 是否可见 hidden?: boolean; // 是否隐藏
permissions?: string[]; // 权限 permissions?: string[]; // 权限
children?: EntityDropdownItem<T>[]; // 子菜单 children?: OperationItem<T>[]; // 子菜单
} }
``` ```

View File

@ -10,3 +10,4 @@ export * from "./states/backup";
export * from "./states/plugin-installation-tabs"; export * from "./states/plugin-installation-tabs";
export * from "./states/entity"; export * from "./states/entity";
export * from "./states/theme-list-tabs"; export * from "./states/theme-list-tabs";
export * from "./states/operation";

View File

@ -1,21 +1,10 @@
import type { Component, Raw } from "vue"; import type { Component, Raw } from "vue";
export interface EntityDropdownItem<T> {
priority: number;
component: Raw<Component>;
props?: Record<string, unknown>;
action?: (item?: T) => void;
label?: string;
visible?: boolean;
permissions?: string[];
children?: EntityDropdownItem<T>[];
}
export interface EntityFieldItem { export interface EntityFieldItem {
priority: number; priority: number;
position: "start" | "end"; position: "start" | "end";
component: Raw<Component>; component: Raw<Component>;
props?: Record<string, unknown>; props?: Record<string, unknown>;
permissions?: string[]; permissions?: string[];
visible?: boolean; hidden?: boolean;
} }

View File

@ -0,0 +1,12 @@
import type { Component, Raw } from "vue";
export interface OperationItem<T> {
priority: number;
component: Raw<Component>;
props?: Record<string, unknown>;
action?: (item?: T) => void;
label?: string;
hidden?: boolean;
permissions?: string[];
children?: OperationItem<T>[];
}

View File

@ -7,7 +7,8 @@ import type { AnyExtension } from "@tiptap/vue-3";
import type { CommentSubjectRefProvider } from "@/states/comment-subject-ref"; 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 { PluginInstallationTab } from "@/states/plugin-installation-tabs";
import type { EntityDropdownItem, EntityFieldItem } from "@/states/entity"; import type { EntityFieldItem } from "@/states/entity";
import type { OperationItem } from "@/states/operation";
import type { ThemeListTab } from "@/states/theme-list-tabs"; import type { ThemeListTab } from "@/states/theme-list-tabs";
import type { Backup, ListedPost, Plugin, Theme } from "@halo-dev/api-client"; import type { Backup, ListedPost, Plugin, Theme } from "@halo-dev/api-client";
@ -42,17 +43,15 @@ export interface ExtensionPoint {
"post:list-item:operation:create"?: ( "post:list-item:operation:create"?: (
post: Ref<ListedPost> post: Ref<ListedPost>
) => ) => OperationItem<ListedPost>[] | Promise<OperationItem<ListedPost>[]>;
| EntityDropdownItem<ListedPost>[]
| Promise<EntityDropdownItem<ListedPost>[]>;
"plugin:list-item:operation:create"?: ( "plugin:list-item:operation:create"?: (
plugin: Ref<Plugin> plugin: Ref<Plugin>
) => EntityDropdownItem<Plugin>[] | Promise<EntityDropdownItem<Plugin>[]>; ) => OperationItem<Plugin>[] | Promise<OperationItem<Plugin>[]>;
"backup:list-item:operation:create"?: ( "backup:list-item:operation:create"?: (
backup: Ref<Backup> backup: Ref<Backup>
) => EntityDropdownItem<Backup>[] | Promise<EntityDropdownItem<Backup>[]>; ) => OperationItem<Backup>[] | Promise<OperationItem<Backup>[]>;
"plugin:list-item:field:create"?: ( "plugin:list-item:field:create"?: (
plugin: Ref<Plugin> plugin: Ref<Plugin>
@ -62,7 +61,7 @@ export interface ExtensionPoint {
"theme:list-item:operation:create"?: ( "theme:list-item:operation:create"?: (
theme: Ref<Theme> theme: Ref<Theme>
) => EntityDropdownItem<Theme>[] | Promise<EntityDropdownItem<Theme>[]>; ) => OperationItem<Theme>[] | Promise<OperationItem<Theme>[]>;
} }
export interface PluginModule { export interface PluginModule {

View File

@ -1,5 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { EntityFieldItem } from "packages/shared/dist"; import { usePermission } from "@/utils/permission";
import type { EntityFieldItem } from "@halo-dev/console-shared";
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -7,6 +8,8 @@ withDefaults(
}>(), }>(),
{} {}
); );
const { currentUserHasPermission } = usePermission();
</script> </script>
<template> <template>
@ -17,7 +20,7 @@ withDefaults(
<component <component
:is="field.component" :is="field.component"
v-bind="field.props" v-bind="field.props"
v-if="field.visible" v-if="!field.hidden && currentUserHasPermission(field.permissions)"
/> />
</template> </template>
</template> </template>

View File

@ -1,10 +1,13 @@
<script setup lang="ts" generic="T"> <script setup lang="ts" generic="T">
import type { EntityDropdownItem } from "@halo-dev/console-shared"; import type { OperationItem } from "@halo-dev/console-shared";
import { VDropdown } from "@halo-dev/components"; import { VDropdown } from "@halo-dev/components";
import { usePermission } from "@/utils/permission";
const { currentUserHasPermission } = usePermission();
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
dropdownItems: EntityDropdownItem<T>[]; dropdownItems: OperationItem<T>[];
item?: T; item?: T;
}>(), }>(),
{ {
@ -12,7 +15,7 @@ const props = withDefaults(
} }
); );
function action(dropdownItem: EntityDropdownItem<T>) { function action(dropdownItem: OperationItem<T>) {
if (!dropdownItem.action) { if (!dropdownItem.action) {
return; return;
} }
@ -22,11 +25,15 @@ function action(dropdownItem: EntityDropdownItem<T>) {
<template> <template>
<template v-for="(dropdownItem, index) in dropdownItems"> <template v-for="(dropdownItem, index) in dropdownItems">
<template v-if="dropdownItem.visible"> <template
v-if="
!dropdownItem.hidden &&
currentUserHasPermission(dropdownItem.permissions)
"
>
<VDropdown <VDropdown
v-if="dropdownItem.children?.length" v-if="dropdownItem.children?.length"
:key="`dropdown-children-items-${index}`" :key="`dropdown-children-items-${index}`"
v-permission="dropdownItem.permissions"
:triggers="['click']" :triggers="['click']"
> >
<component <component
@ -40,10 +47,12 @@ function action(dropdownItem: EntityDropdownItem<T>) {
<template v-for="(childItem, childIndex) in dropdownItem.children"> <template v-for="(childItem, childIndex) in dropdownItem.children">
<component <component
:is="childItem.component" :is="childItem.component"
v-if="childItem.visible" v-if="
!childItem.hidden &&
currentUserHasPermission(childItem.permissions)
"
v-bind="childItem.props" v-bind="childItem.props"
:key="`dropdown-child-item-${childIndex}`" :key="`dropdown-child-item-${childIndex}`"
v-permission="childItem.permissions"
@click="action(childItem)" @click="action(childItem)"
> >
{{ childItem.label }} {{ childItem.label }}
@ -56,7 +65,6 @@ function action(dropdownItem: EntityDropdownItem<T>) {
v-else v-else
v-bind="dropdownItem.props" v-bind="dropdownItem.props"
:key="`dropdown-item-${index}`" :key="`dropdown-item-${index}`"
v-permission="dropdownItem.permissions"
@click="action(dropdownItem)" @click="action(dropdownItem)"
> >
{{ dropdownItem.label }} {{ dropdownItem.label }}

View File

@ -1,62 +1,17 @@
import { usePluginModuleStore } from "@/stores/plugin"; import { usePluginModuleStore } from "@/stores/plugin";
import { usePermission } from "@/utils/permission"; import type { EntityFieldItem, PluginModule } from "@halo-dev/console-shared";
import type {
EntityDropdownItem,
EntityFieldItem,
PluginModule,
} from "@halo-dev/console-shared";
import { onMounted, ref, type ComputedRef, type Ref, computed } from "vue"; import { onMounted, ref, type ComputedRef, type Ref, computed } from "vue";
export function useEntityDropdownItemExtensionPoint<T>(
extensionPointName: string,
entity: Ref<T>,
presets: ComputedRef<EntityDropdownItem<T>[]>
) {
const { pluginModules } = usePluginModuleStore();
const itemsFromPlugins = ref<EntityDropdownItem<T>[]>([]);
onMounted(() => {
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.[extensionPointName]) {
return;
}
const items = extensionPoints[extensionPointName](
entity
) as EntityDropdownItem<T>[];
itemsFromPlugins.value.push(...items);
});
});
const dropdownItems = computed(() => {
return [...presets.value, ...itemsFromPlugins.value].sort((a, b) => {
return a.priority - b.priority;
});
});
return { dropdownItems };
}
export function useEntityFieldItemExtensionPoint<T>( export function useEntityFieldItemExtensionPoint<T>(
extensionPointName: string, extensionPointName: string,
entity: Ref<T>, entity: Ref<T>,
presets: ComputedRef<EntityFieldItem[]> presets: ComputedRef<EntityFieldItem[]>
) { ) {
const { pluginModules } = usePluginModuleStore(); const { pluginModules } = usePluginModuleStore();
const { currentUserHasPermission } = usePermission();
const itemsFromPlugins = ref<EntityFieldItem[]>([]); const itemsFromPlugins = ref<EntityFieldItem[]>([]);
const allItems = computed(() => { const allItems = computed(() => {
return [...presets.value, ...itemsFromPlugins.value].map((item) => { return [...presets.value, ...itemsFromPlugins.value];
return {
...item,
visible:
item.visible !== false && currentUserHasPermission(item.permissions),
};
});
}); });
onMounted(() => { onMounted(() => {

View File

@ -0,0 +1,36 @@
import { usePluginModuleStore } from "@/stores/plugin";
import type { OperationItem, PluginModule } from "@halo-dev/console-shared";
import { onMounted, ref, type ComputedRef, type Ref, computed } from "vue";
export function useOperationItemExtensionPoint<T>(
extensionPointName: string,
entity: Ref<T>,
presets: ComputedRef<OperationItem<T>[]>
) {
const { pluginModules } = usePluginModuleStore();
const itemsFromPlugins = ref<OperationItem<T>[]>([]);
onMounted(() => {
pluginModules.forEach((pluginModule: PluginModule) => {
const { extensionPoints } = pluginModule;
if (!extensionPoints?.[extensionPointName]) {
return;
}
const items = extensionPoints[extensionPointName](
entity
) as OperationItem<T>[];
itemsFromPlugins.value.push(...items);
});
});
const operationItems = computed(() => {
return [...presets.value, ...itemsFromPlugins.value].sort((a, b) => {
return a.priority - b.priority;
});
});
return { operationItems };
}

View File

@ -27,9 +27,10 @@ import { ref } from "vue";
import { computed } from "vue"; import { computed } from "vue";
import { markRaw } from "vue"; import { markRaw } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useEntityDropdownItemExtensionPoint } from "@/composables/use-entity-extension-points"; import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue"; import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
import { toRefs } from "vue"; import { toRefs } from "vue";
import type { OperationItem } from "packages/shared/dist";
const { currentUserHasPermission } = usePermission(); const { currentUserHasPermission } = usePermission();
const { t } = useI18n(); const { t } = useI18n();
@ -122,15 +123,14 @@ const handleDelete = async () => {
}); });
}; };
const { dropdownItems } = useEntityDropdownItemExtensionPoint<ListedPost>( const { operationItems } = useOperationItemExtensionPoint<ListedPost>(
"post:list-item:operation:create", "post:list-item:operation:create",
post, post,
computed(() => [ computed((): OperationItem<ListedPost>[] => [
{ {
priority: 10, priority: 10,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: t("core.common.buttons.edit"), label: t("core.common.buttons.edit"),
visible: true,
permissions: [], permissions: [],
action: () => { action: () => {
router.push({ router.push({
@ -143,7 +143,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<ListedPost>(
priority: 20, priority: 20,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: t("core.common.buttons.setting"), label: t("core.common.buttons.setting"),
visible: true,
permissions: [], permissions: [],
action: () => { action: () => {
emit("open-setting-modal", props.post.post); emit("open-setting-modal", props.post.post);
@ -152,7 +151,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<ListedPost>(
{ {
priority: 30, priority: 30,
component: markRaw(VDropdownDivider), component: markRaw(VDropdownDivider),
visible: true,
}, },
{ {
priority: 40, priority: 40,
@ -161,7 +159,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<ListedPost>(
type: "danger", type: "danger",
}, },
label: t("core.common.buttons.delete"), label: t("core.common.buttons.delete"),
visible: true,
permissions: [], permissions: [],
action: handleDelete, action: handleDelete,
}, },
@ -327,7 +324,7 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<ListedPost>(
v-if="currentUserHasPermission(['system:posts:manage'])" v-if="currentUserHasPermission(['system:posts:manage'])"
#dropdownItems #dropdownItems
> >
<EntityDropdownItems :dropdown-items="dropdownItems" :item="post" /> <EntityDropdownItems :dropdown-items="operationItems" :item="post" />
</template> </template>
</VEntity> </VEntity>
</template> </template>

View File

@ -17,11 +17,12 @@ import { useThemeLifeCycle } from "../composables/use-theme";
import { usePermission } from "@/utils/permission"; import { usePermission } from "@/utils/permission";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useQueryClient } from "@tanstack/vue-query"; import { useQueryClient } from "@tanstack/vue-query";
import { useEntityDropdownItemExtensionPoint } from "@/composables/use-entity-extension-points"; import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import { markRaw } from "vue"; import { markRaw } from "vue";
import { defineComponent } from "vue"; import { defineComponent } from "vue";
import UninstallOperationItem from "./operation/UninstallOperationItem.vue"; import UninstallOperationItem from "./operation/UninstallOperationItem.vue";
import { computed } from "vue"; import { computed } from "vue";
import type { OperationItem } from "packages/shared/dist";
const { currentUserHasPermission } = usePermission(); const { currentUserHasPermission } = usePermission();
const { t } = useI18n(); const { t } = useI18n();
@ -83,10 +84,10 @@ const handleCreateTheme = async () => {
} }
}; };
const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>( const { operationItems } = useOperationItemExtensionPoint<Theme>(
"theme:list-item:operation:create", "theme:list-item:operation:create",
theme, theme,
computed(() => [ computed((): OperationItem<Theme>[] => [
{ {
priority: 10, priority: 10,
component: markRaw(VButton), component: markRaw(VButton),
@ -95,9 +96,8 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>(
}, },
action: () => handleActiveTheme(true), action: () => handleActiveTheme(true),
label: t("core.common.buttons.activate"), label: t("core.common.buttons.activate"),
visible: hidden: isActivated.value,
!isActivated.value && permissions: ["system:themes:manage"],
currentUserHasPermission(["system:themes:manage"]),
}, },
{ {
priority: 20, priority: 20,
@ -121,7 +121,7 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>(
template: `<VButton size="sm"><IconMore /></VButton>`, template: `<VButton size="sm"><IconMore /></VButton>`,
}) })
), ),
visible: currentUserHasPermission(["system:themes:manage"]), permissions: ["system:themes:manage"],
children: [ children: [
{ {
priority: 10, priority: 10,
@ -238,12 +238,13 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>(
</div> </div>
<div> <div>
<VSpace v-if="installed"> <VSpace v-if="installed">
<template v-for="(item, index) in dropdownItems" :key="index"> <template v-for="(item, index) in operationItems" :key="index">
<template v-if="!item.children?.length"> <template v-if="!item.children?.length">
<component <component
:is="item.component" :is="item.component"
v-if="item.visible !== false" v-if="
v-permission="item.permissions" !item.hidden && currentUserHasPermission(item.permissions)
"
v-bind="item.props" v-bind="item.props"
@click="item.action?.(theme)" @click="item.action?.(theme)"
> >
@ -252,8 +253,9 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>(
</template> </template>
<template v-else> <template v-else>
<VDropdown <VDropdown
v-if="item.visible !== false" v-if="
v-permission="item.permissions" !item.hidden && currentUserHasPermission(item.permissions)
"
> >
<component <component
:is="item.component" :is="item.component"
@ -269,8 +271,10 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Theme>(
> >
<component <component
:is="childItem.component" :is="childItem.component"
v-if="childItem.visible !== false" v-if="
v-permission="childItem.permissions" !childItem.hidden &&
currentUserHasPermission(childItem.permissions)
"
v-bind="childItem.props" v-bind="childItem.props"
@click="childItem.action?.(theme)" @click="childItem.action?.(theme)"
> >

View File

@ -15,9 +15,10 @@ import { apiClient } from "@/utils/api-client";
import { useQueryClient } from "@tanstack/vue-query"; import { useQueryClient } from "@tanstack/vue-query";
import prettyBytes from "pretty-bytes"; import prettyBytes from "pretty-bytes";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useEntityDropdownItemExtensionPoint } from "@/composables/use-entity-extension-points"; import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue"; import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
import { toRefs } from "vue"; import { toRefs } from "vue";
import type { OperationItem } from "packages/shared/dist";
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { t } = useI18n(); const { t } = useI18n();
@ -100,15 +101,15 @@ function handleDelete() {
}); });
} }
const { dropdownItems } = useEntityDropdownItemExtensionPoint<Backup>( const { operationItems } = useOperationItemExtensionPoint<Backup>(
"backup:list-item:operation:create", "backup:list-item:operation:create",
backup, backup,
computed(() => [ computed((): OperationItem<Backup>[] => [
{ {
priority: 10, priority: 10,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: t("core.common.buttons.download"), label: t("core.common.buttons.download"),
visible: props.backup.status?.phase === "SUCCEEDED", hidden: props.backup.status?.phase !== "SUCCEEDED",
permissions: [], permissions: [],
action: () => handleDownload(), action: () => handleDownload(),
}, },
@ -119,7 +120,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Backup>(
type: "danger", type: "danger",
}, },
label: t("core.common.buttons.delete"), label: t("core.common.buttons.delete"),
visible: true,
action: () => handleDelete(), action: () => handleDelete(),
}, },
]) ])
@ -187,7 +187,7 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Backup>(
</VEntityField> </VEntityField>
</template> </template>
<template #dropdownItems> <template #dropdownItems>
<EntityDropdownItems :dropdown-items="dropdownItems" :item="backup" /> <EntityDropdownItems :dropdown-items="operationItems" :item="backup" />
</template> </template>
</VEntity> </VEntity>
</template> </template>

View File

@ -16,10 +16,8 @@ import { apiClient } from "@/utils/api-client";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import type { Ref } from "vue"; import type { Ref } from "vue";
import { ref } from "vue"; import { ref } from "vue";
import { import { useEntityFieldItemExtensionPoint } from "@/composables/use-entity-extension-points";
useEntityDropdownItemExtensionPoint, import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
useEntityFieldItemExtensionPoint,
} from "@/composables/use-entity-extension-points";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue"; import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
import EntityFieldItems from "@/components/entity-fields/EntityFieldItems.vue"; import EntityFieldItems from "@/components/entity-fields/EntityFieldItems.vue";
@ -28,6 +26,7 @@ import StatusDotField from "@/components/entity-fields/StatusDotField.vue";
import AuthorField from "./entity-fields/AuthorField.vue"; import AuthorField from "./entity-fields/AuthorField.vue";
import SwitchField from "./entity-fields/SwitchField.vue"; import SwitchField from "./entity-fields/SwitchField.vue";
import { computed } from "vue"; import { computed } from "vue";
import type { EntityFieldItem, OperationItem } from "packages/shared/dist";
const { currentUserHasPermission } = usePermission(); const { currentUserHasPermission } = usePermission();
const { t } = useI18n(); const { t } = useI18n();
@ -76,15 +75,14 @@ const handleResetSettingConfig = async () => {
}); });
}; };
const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>( const { operationItems } = useOperationItemExtensionPoint<Plugin>(
"plugin:list-item:operation:create", "plugin:list-item:operation:create",
plugin, plugin,
computed(() => [ computed((): OperationItem<Plugin>[] => [
{ {
priority: 10, priority: 10,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: t("core.common.buttons.detail"), label: t("core.common.buttons.detail"),
visible: true,
permissions: [], permissions: [],
action: () => { action: () => {
router.push({ router.push({
@ -97,7 +95,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
priority: 20, priority: 20,
component: markRaw(VDropdownItem), component: markRaw(VDropdownItem),
label: t("core.common.buttons.upgrade"), label: t("core.common.buttons.upgrade"),
visible: true,
permissions: [], permissions: [],
action: () => { action: () => {
emit("open-upgrade-modal", props.plugin); emit("open-upgrade-modal", props.plugin);
@ -106,7 +103,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
{ {
priority: 30, priority: 30,
component: markRaw(VDropdownDivider), component: markRaw(VDropdownDivider),
visible: true,
}, },
{ {
priority: 40, priority: 40,
@ -115,7 +111,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
type: "danger", type: "danger",
}, },
label: t("core.common.buttons.uninstall"), label: t("core.common.buttons.uninstall"),
visible: true,
children: [ children: [
{ {
priority: 10, priority: 10,
@ -124,7 +119,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
type: "danger", type: "danger",
}, },
label: t("core.common.buttons.uninstall"), label: t("core.common.buttons.uninstall"),
visible: true,
action: () => uninstall(), action: () => uninstall(),
}, },
{ {
@ -134,7 +128,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
type: "danger", type: "danger",
}, },
label: t("core.plugin.operations.uninstall_and_delete_config.button"), label: t("core.plugin.operations.uninstall_and_delete_config.button"),
visible: true,
action: () => uninstall(true), action: () => uninstall(true),
}, },
], ],
@ -146,7 +139,6 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
type: "danger", type: "danger",
}, },
label: t("core.common.buttons.reset"), label: t("core.common.buttons.reset"),
visible: true,
action: () => { action: () => {
handleResetSettingConfig(); handleResetSettingConfig();
}, },
@ -157,7 +149,7 @@ const { dropdownItems } = useEntityDropdownItemExtensionPoint<Plugin>(
const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>( const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
"plugin:list-item:field:create", "plugin:list-item:field:create",
plugin, plugin,
computed(() => [ computed((): EntityFieldItem[] => [
{ {
position: "start", position: "start",
priority: 10, priority: 10,
@ -188,7 +180,7 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
state: "error", state: "error",
animate: true, animate: true,
}, },
visible: props.plugin.status?.phase === "FAILED", hidden: props.plugin.status?.phase !== "FAILED",
}, },
{ {
position: "end", position: "end",
@ -199,7 +191,7 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
state: "warning", state: "warning",
animate: true, animate: true,
}, },
visible: !!props.plugin.metadata.deletionTimestamp, hidden: !props.plugin.metadata.deletionTimestamp,
}, },
{ {
position: "end", position: "end",
@ -208,7 +200,7 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
props: { props: {
plugin: props.plugin, plugin: props.plugin,
}, },
visible: !!props.plugin.spec.author, hidden: !props.plugin.spec.author,
}, },
{ {
position: "end", position: "end",
@ -225,7 +217,7 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
props: { props: {
description: formatDatetime(props.plugin.metadata.creationTimestamp), description: formatDatetime(props.plugin.metadata.creationTimestamp),
}, },
visible: !!props.plugin.metadata.creationTimestamp, hidden: !props.plugin.metadata.creationTimestamp,
}, },
{ {
position: "end", position: "end",
@ -263,7 +255,7 @@ const { startFields, endFields } = useEntityFieldItemExtensionPoint<Plugin>(
v-if="currentUserHasPermission(['system:plugins:manage'])" v-if="currentUserHasPermission(['system:plugins:manage'])"
#dropdownItems #dropdownItems
> >
<EntityDropdownItems :dropdown-items="dropdownItems" :item="plugin" /> <EntityDropdownItems :dropdown-items="operationItems" :item="plugin" />
</template> </template>
</VEntity> </VEntity>
</template> </template>