mirror of https://github.com/halo-dev/halo-admin
refactor: attachment upload component (#784)
#### What type of PR is this? /kind improvement #### What this PR does / why we need it: 优化上传附件的组件和附件库选择组件。 1. 附件上传支持缓存选择的分组和策略。 2. 附件上传支持选择分组。 3. 移除附件选择组件的上传 tab,改为和附件库管理中一样的上传组件。 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2828 #### Screenshots: <img width="722" alt="image" src="https://user-images.githubusercontent.com/21301288/208612167-c7082be4-0fb8-4caa-b246-d15bac525e86.png"> #### Special notes for your reviewer: 测试方式: 1. 测试在附件管理中上传附件的功能是否正常。 2. 测试在附件选择组件中上传附件的功能是否正常。 #### Does this PR introduce a user-facing change? ```release-note 优化 Console 端上传附件的功能,支持缓存选择的分组和策略。 ```pull/799/head
parent
b29a72d7a5
commit
023831cdd4
|
@ -298,7 +298,6 @@ onMounted(() => {
|
||||||
</AttachmentDetailModal>
|
</AttachmentDetailModal>
|
||||||
<AttachmentUploadModal
|
<AttachmentUploadModal
|
||||||
v-model:visible="uploadVisible"
|
v-model:visible="uploadVisible"
|
||||||
:group="selectedGroup"
|
|
||||||
@close="onUploadModalClose"
|
@close="onUploadModalClose"
|
||||||
/>
|
/>
|
||||||
<AttachmentPoliciesModal v-model:visible="policyVisible" />
|
<AttachmentPoliciesModal v-model:visible="policyVisible" />
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
import { VButton, VModal, VTabbar } from "@halo-dev/components";
|
||||||
import { ref, markRaw, onMounted } from "vue";
|
import { ref, markRaw, onMounted } from "vue";
|
||||||
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
|
import CoreSelectorProvider from "./selector-providers/CoreSelectorProvider.vue";
|
||||||
import UploadSelectorProvider from "./selector-providers/UploadSelectorProvider.vue";
|
|
||||||
import type {
|
import type {
|
||||||
AttachmentLike,
|
AttachmentLike,
|
||||||
AttachmentSelectProvider,
|
AttachmentSelectProvider,
|
||||||
|
@ -33,11 +32,6 @@ const attachmentSelectProviders = ref<AttachmentSelectProvider[]>([
|
||||||
label: "附件库",
|
label: "附件库",
|
||||||
component: markRaw(CoreSelectorProvider),
|
component: markRaw(CoreSelectorProvider),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "upload",
|
|
||||||
label: "上传",
|
|
||||||
component: markRaw(UploadSelectorProvider),
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// resolve plugin extension points
|
// resolve plugin extension points
|
||||||
|
|
|
@ -1,63 +1,72 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { VModal, IconAddCircle, VAlert } from "@halo-dev/components";
|
import { VModal, IconAddCircle, VAlert } from "@halo-dev/components";
|
||||||
import UppyUpload from "@/components/upload/UppyUpload.vue";
|
import UppyUpload from "@/components/upload/UppyUpload.vue";
|
||||||
import { computed, ref, watch, watchEffect } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import type { Policy, Group, PolicyTemplate } from "@halo-dev/api-client";
|
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
|
||||||
import {
|
import {
|
||||||
useFetchAttachmentPolicy,
|
useFetchAttachmentPolicy,
|
||||||
useFetchAttachmentPolicyTemplate,
|
useFetchAttachmentPolicyTemplate,
|
||||||
} from "../composables/use-attachment-policy";
|
} from "../composables/use-attachment-policy";
|
||||||
|
import { useFetchAttachmentGroup } from "../composables/use-attachment-group";
|
||||||
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
import AttachmentPolicyEditingModal from "./AttachmentPolicyEditingModal.vue";
|
||||||
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
group?: Group;
|
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
visible: false,
|
visible: false,
|
||||||
group: undefined,
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const groupName = computed(() => {
|
|
||||||
if (props.group?.metadata.name === "ungrouped") {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return props.group?.metadata.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(event: "update:visible", visible: boolean): void;
|
(event: "update:visible", visible: boolean): void;
|
||||||
(event: "close"): void;
|
(event: "close"): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const { groups, handleFetchGroups } = useFetchAttachmentGroup({
|
||||||
|
fetchOnMounted: false,
|
||||||
|
});
|
||||||
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy({
|
const { policies, handleFetchPolicies } = useFetchAttachmentPolicy({
|
||||||
fetchOnMounted: false,
|
fetchOnMounted: false,
|
||||||
});
|
});
|
||||||
const { policyTemplates, handleFetchPolicyTemplates } =
|
const { policyTemplates, handleFetchPolicyTemplates } =
|
||||||
useFetchAttachmentPolicyTemplate();
|
useFetchAttachmentPolicyTemplate();
|
||||||
|
|
||||||
const selectedPolicy = ref<Policy>();
|
const selectedGroupName = useLocalStorage("attachment-upload-group", "");
|
||||||
|
const selectedPolicyName = useLocalStorage("attachment-upload-policy", "");
|
||||||
const policyToCreate = ref<Policy>();
|
const policyToCreate = ref<Policy>();
|
||||||
const uploadVisible = ref(false);
|
const uploadVisible = ref(false);
|
||||||
const policyEditingModal = ref(false);
|
const policyEditingModal = ref(false);
|
||||||
|
|
||||||
const modalTitle = computed(() => {
|
watch(
|
||||||
if (
|
() => groups.value,
|
||||||
props.group?.metadata.name &&
|
() => {
|
||||||
props.group?.metadata.name !== "ungrouped"
|
if (selectedGroupName.value === "") return;
|
||||||
) {
|
|
||||||
return `上传附件到分组:${props.group.spec.displayName}`;
|
|
||||||
}
|
|
||||||
return "上传附件到未分组";
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
const group = groups.value.find(
|
||||||
if (policies.value.length) {
|
(group) => group.metadata.name === selectedGroupName.value
|
||||||
selectedPolicy.value = policies.value[0];
|
);
|
||||||
|
if (!group) {
|
||||||
|
selectedGroupName.value =
|
||||||
|
groups.value.length > 0 ? groups.value[0].metadata.name : "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => policies.value,
|
||||||
|
() => {
|
||||||
|
const policy = policies.value.find(
|
||||||
|
(policy) => policy.metadata.name === selectedPolicyName.value
|
||||||
|
);
|
||||||
|
if (!policy) {
|
||||||
|
selectedPolicyName.value =
|
||||||
|
policies.value.length > 0 ? policies.value[0].metadata.name : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
const handleOpenCreateNewPolicyModal = (policyTemplate: PolicyTemplate) => {
|
||||||
policyToCreate.value = {
|
policyToCreate.value = {
|
||||||
|
@ -93,6 +102,7 @@ watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(newValue) => {
|
(newValue) => {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
|
handleFetchGroups();
|
||||||
handleFetchPolicies();
|
handleFetchPolicies();
|
||||||
handleFetchPolicyTemplates();
|
handleFetchPolicyTemplates();
|
||||||
uploadVisible.value = true;
|
uploadVisible.value = true;
|
||||||
|
@ -110,12 +120,36 @@ watch(
|
||||||
:body-class="['!p-0']"
|
:body-class="['!p-0']"
|
||||||
:visible="visible"
|
:visible="visible"
|
||||||
:width="650"
|
:width="650"
|
||||||
:title="modalTitle"
|
title="上传附件"
|
||||||
@update:visible="onVisibleChange"
|
@update:visible="onVisibleChange"
|
||||||
>
|
>
|
||||||
<div class="w-full p-4">
|
<div class="w-full p-4">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<span class="text-sm text-gray-900">选择存储策略:</span>
|
<span class="text-sm text-gray-800">选择分组:</span>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
||||||
|
<div
|
||||||
|
v-for="(group, index) in [
|
||||||
|
{ metadata: { name: '' }, spec: { displayName: '未分组' } },
|
||||||
|
...groups,
|
||||||
|
]"
|
||||||
|
:key="index"
|
||||||
|
:class="{
|
||||||
|
'!bg-gray-200 !text-gray-900':
|
||||||
|
group.metadata.name === selectedGroupName,
|
||||||
|
}"
|
||||||
|
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="selectedGroupName = group.metadata.name"
|
||||||
|
>
|
||||||
|
<div class="flex flex-1 items-center gap-2 truncate">
|
||||||
|
<span class="truncate text-sm">
|
||||||
|
{{ group.spec.displayName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<span class="text-sm text-gray-800">选择存储策略:</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
<div class="mb-3 grid grid-cols-2 gap-x-2 gap-y-3 sm:grid-cols-4">
|
||||||
<div
|
<div
|
||||||
|
@ -123,10 +157,10 @@ watch(
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="{
|
:class="{
|
||||||
'!bg-gray-200 !text-gray-900':
|
'!bg-gray-200 !text-gray-900':
|
||||||
selectedPolicy?.metadata.name === policy.metadata.name,
|
selectedPolicyName === policy.metadata.name,
|
||||||
}"
|
}"
|
||||||
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"
|
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="selectedPolicy = policy"
|
@click="selectedPolicyName = policy.metadata.name"
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 flex-col items-start truncate">
|
<div class="flex flex-1 flex-col items-start truncate">
|
||||||
<span class="truncate text-sm">
|
<span class="truncate text-sm">
|
||||||
|
@ -176,13 +210,13 @@ watch(
|
||||||
<UppyUpload
|
<UppyUpload
|
||||||
v-if="uploadVisible"
|
v-if="uploadVisible"
|
||||||
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
||||||
:disabled="!selectedPolicy"
|
:disabled="!selectedPolicyName"
|
||||||
:meta="{
|
:meta="{
|
||||||
policyName: selectedPolicy?.metadata.name as string,
|
policyName: selectedPolicyName,
|
||||||
groupName: groupName
|
groupName: selectedGroupName,
|
||||||
}"
|
}"
|
||||||
:allowed-meta-fields="['policyName', 'groupName']"
|
:allowed-meta-fields="['policyName', 'groupName']"
|
||||||
:note="selectedPolicy ? '' : '请先选择存储策略'"
|
:note="selectedPolicyName ? '' : '请先选择存储策略'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</VModal>
|
</VModal>
|
||||||
|
|
|
@ -78,6 +78,14 @@ await handleFetchAttachments();
|
||||||
readonly
|
readonly
|
||||||
@select="onGroupChange"
|
@select="onGroupChange"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="attachments.total > 0" class="mb-5">
|
||||||
|
<VButton @click="uploadVisible = true">
|
||||||
|
<template #icon>
|
||||||
|
<IconUpload class="h-full w-full" />
|
||||||
|
</template>
|
||||||
|
上传
|
||||||
|
</VButton>
|
||||||
|
</div>
|
||||||
<VEmpty
|
<VEmpty
|
||||||
v-if="!attachments.total && !loading"
|
v-if="!attachments.total && !loading"
|
||||||
message="当前没有附件,你可以尝试刷新或者上传附件"
|
message="当前没有附件,你可以尝试刷新或者上传附件"
|
||||||
|
@ -86,7 +94,7 @@ await handleFetchAttachments();
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton @click="handleFetchAttachments">刷新</VButton>
|
<VButton @click="handleFetchAttachments">刷新</VButton>
|
||||||
<VButton type="secondary" @click="emit('change-provider', 'upload')">
|
<VButton type="secondary" @click="uploadVisible = true">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<IconUpload class="h-full w-full" />
|
<IconUpload class="h-full w-full" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,231 +0,0 @@
|
||||||
<script lang="ts" setup>
|
|
||||||
import {
|
|
||||||
VEmpty,
|
|
||||||
IconCheckboxFill,
|
|
||||||
VCard,
|
|
||||||
IconDeleteBin,
|
|
||||||
Dialog,
|
|
||||||
Toast,
|
|
||||||
} from "@halo-dev/components";
|
|
||||||
|
|
||||||
import type { AttachmentLike } from "@halo-dev/console-shared";
|
|
||||||
import { apiClient } from "@/utils/api-client";
|
|
||||||
import LazyImage from "@/components/image/LazyImage.vue";
|
|
||||||
import type { Attachment } from "@halo-dev/api-client";
|
|
||||||
import UppyUpload from "@/components/upload/UppyUpload.vue";
|
|
||||||
import AttachmentFileTypeIcon from "../AttachmentFileTypeIcon.vue";
|
|
||||||
import { computed, ref, watchEffect } from "vue";
|
|
||||||
import { isImage } from "@/utils/image";
|
|
||||||
import { useFetchAttachmentPolicy } from "../../composables/use-attachment-policy";
|
|
||||||
import { useFetchAttachmentGroup } from "../../composables/use-attachment-group";
|
|
||||||
import type { SuccessResponse } from "@uppy/core";
|
|
||||||
|
|
||||||
withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
selected: AttachmentLike[];
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
selected: () => [],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const emit = defineEmits<{
|
|
||||||
(event: "update:selected", attachments: AttachmentLike[]): void;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const { policies } = useFetchAttachmentPolicy({ fetchOnMounted: true });
|
|
||||||
const policyMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: "选择存储策略",
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
...policies.value.map((policy) => {
|
|
||||||
return {
|
|
||||||
label: policy.spec.displayName,
|
|
||||||
value: policy.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
const selectedPolicy = ref("");
|
|
||||||
|
|
||||||
const { groups } = useFetchAttachmentGroup({ fetchOnMounted: true });
|
|
||||||
const groupMap = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: "选择分组",
|
|
||||||
value: "",
|
|
||||||
},
|
|
||||||
...groups.value.map((group) => {
|
|
||||||
return {
|
|
||||||
label: group.spec.displayName,
|
|
||||||
value: group.metadata.name,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
const selectedGroup = ref("");
|
|
||||||
|
|
||||||
const attachments = ref<Set<Attachment>>(new Set<Attachment>());
|
|
||||||
const selectedAttachments = ref<Set<Attachment>>(new Set<Attachment>());
|
|
||||||
|
|
||||||
const onUploaded = async (response: SuccessResponse) => {
|
|
||||||
const attachment = response.body as Attachment;
|
|
||||||
|
|
||||||
const { data } =
|
|
||||||
await apiClient.extension.storage.attachment.getstorageHaloRunV1alpha1Attachment(
|
|
||||||
{
|
|
||||||
name: attachment.metadata.name,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
attachments.value.add(data);
|
|
||||||
selectedAttachments.value.add(data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelect = async (attachment: Attachment | undefined) => {
|
|
||||||
if (!attachment) return;
|
|
||||||
if (selectedAttachments.value.has(attachment)) {
|
|
||||||
selectedAttachments.value.delete(attachment);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedAttachments.value.add(attachment);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDelete = async (attachment: Attachment) => {
|
|
||||||
Dialog.warning({
|
|
||||||
title: "确定要删除当前的附件吗?",
|
|
||||||
confirmType: "danger",
|
|
||||||
onConfirm: async () => {
|
|
||||||
try {
|
|
||||||
await apiClient.extension.storage.attachment.deletestorageHaloRunV1alpha1Attachment(
|
|
||||||
{
|
|
||||||
name: attachment.metadata.name,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
attachments.value.delete(attachment);
|
|
||||||
selectedAttachments.value.delete(attachment);
|
|
||||||
|
|
||||||
Toast.success("删除成功");
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Failed to delete attachment", e);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
emit("update:selected", Array.from(selectedAttachments.value));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="flex h-full flex-col gap-4 sm:flex-row">
|
|
||||||
<div class="h-full w-full space-y-4 overflow-auto sm:w-96">
|
|
||||||
<FormKit type="form">
|
|
||||||
<FormKit
|
|
||||||
v-model="selectedPolicy"
|
|
||||||
type="select"
|
|
||||||
:options="policyMap"
|
|
||||||
label="存储策略"
|
|
||||||
></FormKit>
|
|
||||||
<FormKit
|
|
||||||
v-model="selectedGroup"
|
|
||||||
:options="groupMap"
|
|
||||||
type="select"
|
|
||||||
label="分组"
|
|
||||||
></FormKit>
|
|
||||||
</FormKit>
|
|
||||||
<UppyUpload
|
|
||||||
v-if="selectedPolicy"
|
|
||||||
endpoint="/apis/api.console.halo.run/v1alpha1/attachments/upload"
|
|
||||||
:disabled="!selectedPolicy"
|
|
||||||
:meta="{
|
|
||||||
policyName: selectedPolicy,
|
|
||||||
groupName: selectedGroup,
|
|
||||||
}"
|
|
||||||
:allowed-meta-fields="['policyName', 'groupName']"
|
|
||||||
:note="selectedPolicy ? '' : '请先选择存储策略'"
|
|
||||||
@uploaded="onUploaded"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="h-full flex-1 overflow-auto">
|
|
||||||
<VEmpty
|
|
||||||
v-if="!attachments.size"
|
|
||||||
message="当前没有上传的文件,你可以点击左侧区域上传文件"
|
|
||||||
title="当前没有上传的文件"
|
|
||||||
>
|
|
||||||
</VEmpty>
|
|
||||||
<div
|
|
||||||
v-else
|
|
||||||
class="grid grid-cols-3 gap-x-2 gap-y-3 p-0.5 sm:grid-cols-3 md:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-6"
|
|
||||||
role="list"
|
|
||||||
>
|
|
||||||
<VCard
|
|
||||||
v-for="(attachment, index) in Array.from(attachments)"
|
|
||||||
:key="index"
|
|
||||||
:body-class="['!p-0']"
|
|
||||||
:class="{
|
|
||||||
'ring-1 ring-primary': selectedAttachments.has(attachment),
|
|
||||||
}"
|
|
||||||
class="hover:shadow"
|
|
||||||
@click="handleSelect(attachment)"
|
|
||||||
>
|
|
||||||
<div class="group relative bg-white">
|
|
||||||
<div
|
|
||||||
class="aspect-w-10 aspect-h-8 block h-full w-full cursor-pointer overflow-hidden bg-gray-100"
|
|
||||||
>
|
|
||||||
<LazyImage
|
|
||||||
v-if="isImage(attachment.spec.mediaType)"
|
|
||||||
:key="attachment.metadata.name"
|
|
||||||
:alt="attachment.spec.displayName"
|
|
||||||
:src="attachment.status?.permalink"
|
|
||||||
classes="pointer-events-none object-cover group-hover:opacity-75"
|
|
||||||
>
|
|
||||||
<template #loading>
|
|
||||||
<div
|
|
||||||
class="flex h-full items-center justify-center object-cover"
|
|
||||||
>
|
|
||||||
<span class="text-xs text-gray-400">加载中...</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #error>
|
|
||||||
<div
|
|
||||||
class="flex h-full items-center justify-center object-cover"
|
|
||||||
>
|
|
||||||
<span class="text-xs text-red-400">加载异常</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</LazyImage>
|
|
||||||
<AttachmentFileTypeIcon
|
|
||||||
v-else
|
|
||||||
:file-name="attachment.spec.displayName"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
class="pointer-events-none block truncate px-2 py-1 text-center text-xs font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
{{ attachment.spec.displayName }}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div
|
|
||||||
:class="{ '!flex': selectedAttachments.has(attachment) }"
|
|
||||||
class="absolute top-0 left-0 hidden h-1/3 w-full justify-end bg-gradient-to-b from-gray-300 to-transparent ease-in-out group-hover:flex"
|
|
||||||
>
|
|
||||||
<IconDeleteBin
|
|
||||||
class="mt-1 mr-1 hidden h-5 w-5 cursor-pointer text-red-400 transition-all hover:text-red-600 group-hover:block"
|
|
||||||
@click.stop="handleDelete(attachment)"
|
|
||||||
/>
|
|
||||||
<IconCheckboxFill
|
|
||||||
:class="{
|
|
||||||
'!text-primary': selectedAttachments.has(attachment),
|
|
||||||
}"
|
|
||||||
class="mt-1 mr-1 h-5 w-5 cursor-pointer text-white transition-all hover:text-primary"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</VCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
Loading…
Reference in New Issue