56 lines
1.8 KiB
Vue
56 lines
1.8 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import { ref } from 'vue'
|
||
|
|
import { api, type Attachment } from '../api'
|
||
|
|
|
||
|
|
const props = defineProps<{ attachments: Attachment[]; taskId: string }>()
|
||
|
|
const emit = defineEmits<{ deleted: [] }>()
|
||
|
|
|
||
|
|
const deletingId = ref<number | null>(null)
|
||
|
|
|
||
|
|
async function remove(id: number) {
|
||
|
|
deletingId.value = id
|
||
|
|
try {
|
||
|
|
await api.deleteAttachment(props.taskId, id)
|
||
|
|
emit('deleted')
|
||
|
|
} catch {
|
||
|
|
// silently ignore — parent will reload
|
||
|
|
} finally {
|
||
|
|
deletingId.value = null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatSize(bytes: number): string {
|
||
|
|
if (bytes < 1024) return `${bytes}B`
|
||
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`
|
||
|
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div v-if="attachments.length" class="flex flex-wrap gap-3 mb-3">
|
||
|
|
<div
|
||
|
|
v-for="att in attachments"
|
||
|
|
:key="att.id"
|
||
|
|
class="relative group border border-gray-700 rounded-lg overflow-hidden bg-gray-900 w-28"
|
||
|
|
>
|
||
|
|
<a :href="api.attachmentUrl(att.id)" target="_blank" rel="noopener">
|
||
|
|
<img
|
||
|
|
:src="api.attachmentUrl(att.id)"
|
||
|
|
:alt="att.filename"
|
||
|
|
class="w-28 h-20 object-cover block"
|
||
|
|
/>
|
||
|
|
</a>
|
||
|
|
<div class="px-1.5 py-1">
|
||
|
|
<p class="text-[10px] text-gray-400 truncate" :title="att.filename">{{ att.filename }}</p>
|
||
|
|
<p class="text-[10px] text-gray-600">{{ formatSize(att.size) }}</p>
|
||
|
|
</div>
|
||
|
|
<button
|
||
|
|
@click="remove(att.id)"
|
||
|
|
:disabled="deletingId === att.id"
|
||
|
|
class="absolute top-1 right-1 w-5 h-5 rounded-full bg-red-900/80 text-red-400 text-xs leading-none opacity-0 group-hover:opacity-100 transition-opacity disabled:opacity-50 flex items-center justify-center"
|
||
|
|
title="Удалить"
|
||
|
|
>✕</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|