Skip to content

Commit

Permalink
feat: add delete attachment group and policy support (halo-dev/consol…
Browse files Browse the repository at this point in the history
…e#695)

#### What type of PR is this?

/kind feature
/milestone 2.0

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

支持删除附件分组和存储策略。

删除策略的逻辑为:删除前会根据策略查询附件,如果有附件,则无法删除,否则可以删除。

删除附件的逻辑为:

1. 选择`删除并将附件移动至未分组`时,会在前端批量调用更新附件的接口,将所有附件的 `groupRef` 置空。
2. 选择`删除并同时删除附件`时,会在前端批量调用删除附件接口。

#### Which issue(s) this PR fixes:

Fixes halo-dev#2706

#### Special notes for your reviewer:

/cc @halo-dev/sig-halo-console 

测试方式:

1. 需要执行 `pnpm build:packages`
2. 创建若干存储策略,并在部分存储策略中上传附件,再对存储策略做删除处理,需要满足以下情况:
    1. 已包含附件的策略会提示不允许删除。
    2. 未包含附件的策略可以删除
3. 创建若干分组,并在部分分组中上传附件,再对分组做删除处理,需要满足以下情况:
    1. 选择`删除并将附件移动至未分组`时,检查分组是否被删除,且里面的附件是否已经被移动到未分组。
    2. 选择`删除并同时删除附件`时,检查分组是否被删除,且里面的附件是否被删除。


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

<!--
如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。
否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change),
Release Note 需要以 `action required` 开头。
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```release-note
支持删除附件分组和存储策略。
```
  • Loading branch information
ruibaby committed Nov 18, 2022
1 parent 7887648 commit 2061649
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 57 deletions.
2 changes: 1 addition & 1 deletion packages/components/src/components/button/Button.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ function handleClick() {
}
.btn-danger {
background: #d71d1d;
background-color: #d71d1d !important;
@apply text-white;
}
Expand Down
1 change: 1 addition & 0 deletions src/modules/contents/attachments/AttachmentList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ onMounted(() => {
v-model:selected-group="selectedGroup"
@select="onGroupChange"
@update="handleFetchGroups"
@reload-attachments="handleFetchAttachments"
/>
</div>

Expand Down
141 changes: 137 additions & 4 deletions src/modules/contents/attachments/components/AttachmentGroupList.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
<script lang="ts" setup>
// core libs
import { onMounted, ref } from "vue";
import { onMounted, ref, watch } from "vue";
// components
import { IconAddCircle, IconMore, VButton, VSpace } from "@halo-dev/components";
import {
Dialog,
IconAddCircle,
IconMore,
Toast,
VButton,
VSpace,
VStatusDot,
} from "@halo-dev/components";
import AttachmentGroupEditingModal from "./AttachmentGroupEditingModal.vue";
// types
import type { Group } from "@halo-dev/api-client";
import { useRouteQuery } from "@vueuse/router";
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
import { apiClient } from "@/utils/api-client";
const props = withDefaults(
defineProps<{
Expand All @@ -27,6 +36,7 @@ const emit = defineEmits<{
(event: "update:selectedGroup", group: Group): void;
(event: "select", group: Group): void;
(event: "update"): void;
(event: "reload-attachments"): void;
}>();
const defaultGroups: Group[] = [
Expand Down Expand Up @@ -79,6 +89,93 @@ const onEditingModalClose = () => {
handleFetchGroups();
};
const handleDelete = (group: Group) => {
Dialog.warning({
title: "是否确认删除该分组?",
description:
"此操作将删除分组,并将分组下的附件移动至未分组,此操作无法恢复。",
confirmType: "danger",
onConfirm: async () => {
// TODO: 后续将修改为在后端进行批量操作处理
const { data } = await apiClient.attachment.searchAttachments({
group: group.metadata.name,
page: 0,
size: 0,
});
await apiClient.extension.storage.group.deletestorageHaloRunV1alpha1Group(
{ name: group.metadata.name }
);
// move attachments to none group
const moveToUnGroupRequests = data.items.map((attachment) => {
attachment.spec.groupRef = undefined;
return apiClient.extension.storage.attachment.updatestorageHaloRunV1alpha1Attachment(
{
name: attachment.metadata.name,
attachment: attachment,
}
);
});
await Promise.all(moveToUnGroupRequests);
handleFetchGroups();
emit("reload-attachments");
emit("update");
Toast.success(`删除成功,${data.total} 个附件已移动至未分组`);
},
});
};
const handleDeleteWithAttachments = (group: Group) => {
Dialog.warning({
title: "是否确认删除该分组?",
description: "此操作将删除分组以及分组下的所有附件,此操作无法恢复。",
confirmType: "danger",
onConfirm: async () => {
// TODO: 后续将修改为在后端进行批量操作处理
const { data } = await apiClient.attachment.searchAttachments({
group: group.metadata.name,
page: 0,
size: 0,
});
await apiClient.extension.storage.group.deletestorageHaloRunV1alpha1Group(
{ name: group.metadata.name }
);
const deleteAttachmentRequests = data.items.map((attachment) => {
return apiClient.extension.storage.attachment.deletestorageHaloRunV1alpha1Attachment(
{ name: attachment.metadata.name }
);
});
await Promise.all(deleteAttachmentRequests);
handleFetchGroups();
emit("reload-attachments");
emit("update");
Toast.success(`删除成功,${data.total} 个附件已被同时删除`);
},
});
};
watch(
() => groups.value.length,
() => {
const groupIndex = groups.value.findIndex(
(group) => group.metadata.name === routeQuery.value
);
if (groupIndex < 0) {
handleSelectGroup(defaultGroups[0]);
}
}
);
onMounted(async () => {
await handleFetchGroups();
Expand Down Expand Up @@ -128,10 +225,16 @@ onMounted(async () => {
class="flex cursor-pointer items-center rounded-base bg-gray-100 p-2 text-gray-500 transition-all hover:bg-gray-200 hover:text-gray-900 hover:shadow-sm"
@click="handleSelectGroup(group)"
>
<div class="flex flex-1 items-center truncate">
<div class="flex flex-1 items-center gap-2 truncate">
<span class="truncate text-sm">
{{ group.spec.displayName }}
</span>
<VStatusDot
v-if="group.metadata.deletionTimestamp"
v-tooltip="`删除中`"
state="warning"
animate
/>
</div>
<FloatingDropdown
v-if="!readonly"
Expand All @@ -149,7 +252,37 @@ onMounted(async () => {
>
重命名
</VButton>
<VButton v-close-popper block type="danger"> 删除</VButton>
<FloatingDropdown
class="w-full"
placement="right"
:triggers="['click']"
>
<VButton block type="danger">删除</VButton>
<template #popper>
<div class="w-52 p-2">
<VSpace class="w-full" direction="column">
<VButton
v-close-popper.all
block
type="danger"
size="sm"
@click="handleDelete(group)"
>
删除并将附件移动至未分组
</VButton>
<VButton
v-close-popper.all
block
type="danger"
size="sm"
@click="handleDeleteWithAttachments(group)"
>
删除并同时删除附件
</VButton>
</VSpace>
</div>
</template>
</FloatingDropdown>
</VSpace>
</div>
</template>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<script lang="ts" setup>
import {
IconAddCircle,
IconMore,
VButton,
VModal,
VSpace,
VEmpty,
Dialog,
VEntity,
VEntityField,
VStatusDot,
} from "@halo-dev/components";
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
import { ref, watch } from "vue";
Expand All @@ -15,6 +18,7 @@ import {
useFetchAttachmentPolicy,
useFetchAttachmentPolicyTemplate,
} from "../composables/use-attachment-policy";
import { apiClient } from "@/utils/api-client";
const props = withDefaults(
defineProps<{
Expand Down Expand Up @@ -73,6 +77,31 @@ const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
policyEditingModal.value = true;
};
const handleDelete = async (policy: Policy) => {
const { data } = await apiClient.attachment.searchAttachments({
policy: policy.metadata.name,
});
if (data.total > 0) {
Dialog.warning({
title: "删除失败",
description: "该策略下存在附件,无法删除。",
});
return;
}
Dialog.warning({
title: "确定删除该策略吗?",
description: "当前策略下没有已上传的附件。",
onConfirm: async () => {
await apiClient.extension.storage.policy.deletestorageHaloRunV1alpha1Policy(
{ name: policy.metadata.name }
);
handleFetchPolicies();
},
});
};
const onEditingModalClose = () => {
selectedPolicy.value = undefined;
handleFetchPolicies();
Expand Down Expand Up @@ -162,57 +191,46 @@ watch(
role="list"
>
<li v-for="(policy, index) in policies" :key="index">
<div
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
>
<div class="relative flex flex-row items-center">
<div class="flex-1">
<div class="flex flex-col sm:flex-row">
<span
class="mr-0 truncate text-sm font-medium text-gray-900 sm:mr-2"
>
{{ policy.spec.displayName }}
</span>
</div>
<div class="mt-1 flex">
<span class="text-xs text-gray-500">
{{ policy.spec.templateRef?.name }}
</span>
</div>
</div>
<div class="flex">
<div
class="inline-flex flex-col items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<time class="text-sm tabular-nums text-gray-500">
<VEntity>
<template #start>
<VEntityField
:title="policy.spec.displayName"
:description="policy.spec.templateRef?.name"
></VEntityField>
</template>
<template #end>
<VEntityField v-if="policy.metadata.deletionTimestamp">
<template #description>
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
</template>
</VEntityField>
<VEntityField>
<template #description>
<span class="truncate text-xs tabular-nums text-gray-500">
{{ formatDatetime(policy.metadata.creationTimestamp) }}
</time>
<span class="cursor-pointer">
<FloatingDropdown>
<IconMore />
<template #popper>
<div class="w-48 p-2">
<VSpace class="w-full" direction="column">
<VButton
v-close-popper
block
type="secondary"
@click="handleOpenEditingModal(policy)"
>
编辑
</VButton>
<VButton v-close-popper block type="danger">
删除
</VButton>
</VSpace>
</div>
</template>
</FloatingDropdown>
</span>
</div>
</div>
</div>
</div>
</template>
</VEntityField>
</template>
<template #dropdownItems>
<VButton
v-close-popper
block
type="secondary"
@click="handleOpenEditingModal(policy)"
>
编辑
</VButton>
<VButton
v-close-popper
block
type="danger"
@click="handleDelete(policy)"
>
删除
</VButton>
</template>
</VEntity>
</li>
</ul>
<template #footer>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { onMounted, ref, type Ref } from "vue";
import { onMounted, onUnmounted, ref, type Ref } from "vue";
import type { Group } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";

Expand All @@ -15,13 +15,26 @@ export function useFetchAttachmentGroup(options?: {

const groups = ref<Group[]>([] as Group[]);
const loading = ref<boolean>(false);
const refreshInterval = ref();

const handleFetchGroups = async () => {
try {
clearInterval(refreshInterval.value);

loading.value = true;
const { data } =
await apiClient.extension.storage.group.liststorageHaloRunV1alpha1Group();
groups.value = data.items;

const deletedGroups = groups.value.filter(
(group) => !!group.metadata.deletionTimestamp
);

if (deletedGroups.length) {
refreshInterval.value = setInterval(() => {
handleFetchGroups();
}, 1000);
}
} catch (e) {
console.error("Failed to fetch attachment groups", e);
} finally {
Expand All @@ -33,6 +46,10 @@ export function useFetchAttachmentGroup(options?: {
fetchOnMounted && handleFetchGroups();
});

onUnmounted(() => {
clearInterval(refreshInterval.value);
});

return {
groups,
loading,
Expand Down

0 comments on commit 2061649

Please sign in to comment.