Files
FEDEO/frontend/components/DocumentUploadModal.vue
florianfederspiel 7c644c941a
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 15s
Build and Push Docker Images / build-frontend (push) Successful in 57s
fix #141
fix #142
2026-03-22 22:10:41 +01:00

160 lines
4.8 KiB
Vue

<script setup>
const props = defineProps({
fileData: {
type: Object,
default: () => ({
type: null
})
}
})
const emit = defineEmits(["uploadFinished"])
const modal = useModal()
const uploadInProgress = ref(false)
const availableFiletypes = ref([])
const localFileData = reactive({
...props.fileData
})
// 1. State für die Dateien und die Dropzone Referenz
const selectedFiles = ref([])
const dropZoneRef = ref(null)
// 2. Setup der Dropzone
const onDrop = (files) => {
// Wenn Dateien gedroppt werden, speichern wir sie
// files ist hier meist ein Array, wir stellen sicher, dass es passt
selectedFiles.value = files || []
}
const { isOverDropZone } = useDropZone(dropZoneRef, {
onDrop,
// Verhindert, dass der Browser das Bild einfach öffnet
preventDefaultForDrop: true,
})
// 3. Handler für den klassischen Datei-Input Klick
const onFileInputChange = (e) => {
if (e.target.files) {
selectedFiles.value = Array.from(e.target.files)
}
}
const setup = async () => {
availableFiletypes.value = await useEntities("filetags").select()
}
setup()
const uploadFiles = async () => {
// Validierung: Keine Dateien ausgewählt
if (!selectedFiles.value || selectedFiles.value.length === 0) {
alert("Bitte wählen Sie zuerst Dateien aus.") // Oder eine schönere Toast Notification
return
}
uploadInProgress.value = true;
const { typeEnabled, ...fileData } = localFileData
await useFiles().uploadFiles(fileData, selectedFiles.value, [], true)
uploadInProgress.value = false;
emit("uploadFinished")
modal.close()
}
// Helper Funktion um Dateinamen anzuzeigen (da das Input Feld leer bleibt beim Droppen)
const fileNames = computed(() => {
if (!selectedFiles.value.length) return ''
return selectedFiles.value.map(f => f.name).join(', ')
})
</script>
<template>
<UModal>
<template #content>
<div ref="dropZoneRef" class="relative h-full flex flex-col">
<div
v-if="isOverDropZone"
class="absolute inset-0 z-50 flex items-center justify-center rounded-lg border-2 border-dashed border-primary-500 bg-primary-500/10 backdrop-blur-sm transition-all"
>
<span class="rounded bg-white/80 px-4 py-2 text-xl font-bold text-primary-600 shadow-sm">
Dateien hier ablegen
</span>
</div>
<UCard :ui="{ body: { base: 'flex-1' }, ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Datei hochladen
</h3>
<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
class="-my-1"
@click="modal.close()"
:disabled="uploadInProgress"
/>
</div>
</template>
<UFormField
label="Datei:"
:help="selectedFiles.length > 0 ? `${selectedFiles.length} Datei(en) ausgewählt` : 'Ziehen Sie Dateien hierher oder klicken Sie'"
>
<UInput
v-if="selectedFiles.length === 0"
type="file"
id="fileUploadInput"
multiple
accept="image/jpeg, image/png, image/gif, application/pdf"
@change="onFileInputChange"
/>
<div v-if="selectedFiles.length > 0" class="mt-2 text-sm text-gray-500">
Ausgewählt: <span class="font-medium text-gray-700 dark:text-gray-300">{{ fileNames }}</span>
</div>
</UFormField>
<UFormField
label="Typ:"
class="mt-3"
>
<USelectMenu
:items="availableFiletypes"
v-model="localFileData.type"
value-key="id"
label-key="name"
:search-input="{ placeholder: 'Suchen...' }"
:filter-fields="['name']"
:disabled="!localFileData.typeEnabled"
class="w-full"
>
<template #default>
<span v-if="availableFiletypes.find(x => x.id === localFileData.type)">{{ availableFiletypes.find(x => x.id === localFileData.type).name }}</span>
<span v-else>Kein Typ ausgewählt</span>
</template>
</USelectMenu>
</UFormField>
<template #footer>
<UButton
@click="uploadFiles"
:loading="uploadInProgress"
:disabled="uploadInProgress || selectedFiles.length === 0"
>Hochladen</UButton>
</template>
</UCard>
</div>
</template>
</UModal>
</template>
<style scoped>
</style>