65 lines
2 KiB
Vue
65 lines
2 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { api } from '../api'
|
|
|
|
const { t } = useI18n()
|
|
|
|
const props = defineProps<{ taskId: string }>()
|
|
const emit = defineEmits<{ uploaded: [] }>()
|
|
|
|
const dragging = ref(false)
|
|
const uploading = ref(false)
|
|
const error = ref('')
|
|
const fileInput = ref<HTMLInputElement | null>(null)
|
|
|
|
async function upload(file: File) {
|
|
if (!file.type.startsWith('image/')) {
|
|
error.value = t('attachments.images_only')
|
|
return
|
|
}
|
|
uploading.value = true
|
|
error.value = ''
|
|
try {
|
|
await api.uploadAttachment(props.taskId, file)
|
|
emit('uploaded')
|
|
} catch (e: any) {
|
|
error.value = e.message
|
|
} finally {
|
|
uploading.value = false
|
|
}
|
|
}
|
|
|
|
function onFileChange(event: Event) {
|
|
const input = event.target as HTMLInputElement
|
|
if (input.files?.[0]) upload(input.files[0])
|
|
input.value = ''
|
|
}
|
|
|
|
function onDrop(event: DragEvent) {
|
|
dragging.value = false
|
|
const file = event.dataTransfer?.files[0]
|
|
if (file) upload(file)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="border-2 border-dashed rounded-lg p-3 text-center transition-colors cursor-pointer select-none"
|
|
:class="dragging ? 'border-blue-500 bg-blue-950/20' : 'border-gray-700 hover:border-gray-500'"
|
|
@dragover.prevent="dragging = true"
|
|
@dragleave="dragging = false"
|
|
@drop.prevent="onDrop"
|
|
@click="fileInput?.click()"
|
|
>
|
|
<input ref="fileInput" type="file" accept="image/*" class="hidden" @change="onFileChange" />
|
|
<div v-if="uploading" class="flex items-center justify-center gap-2 text-xs text-blue-400">
|
|
<span class="inline-block w-3 h-3 border-2 border-blue-400 border-t-transparent rounded-full animate-spin"></span>
|
|
{{ t('attachments.uploading') }}
|
|
</div>
|
|
<div v-else class="text-xs text-gray-500">
|
|
{{ t('attachments.drop_hint') }} <span class="text-blue-400">{{ t('attachments.click_to_select') }}</span>
|
|
</div>
|
|
<p v-if="error" class="text-red-400 text-xs mt-1">{{ error }}</p>
|
|
</div>
|
|
</template>
|