Skip to content
This repository has been archived by the owner on Jan 21, 2024. It is now read-only.

feat: add delete attachment group and policy support #695

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
139 changes: 135 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,91 @@ 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;
ruibaby marked this conversation as resolved.
Show resolved Hide resolved
return apiClient.extension.storage.attachment.updatestorageHaloRunV1alpha1Attachment(
{
name: attachment.metadata.name,
attachment: attachment,
}
);
});

await Promise.all(moveToUnGroupRequests);

handleFetchGroups();
emit("reload-attachments");

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");

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 +223,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 +250,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 @@ -16,6 +19,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