108 lines
2.3 KiB
Vue
108 lines
2.3 KiB
Vue
<template>
|
|
<div
|
|
class="upload-zone"
|
|
:class="{ 'upload-zone--active': isDragging }"
|
|
@dragenter.prevent="isDragging = true"
|
|
@dragleave.prevent="isDragging = false"
|
|
@dragover.prevent
|
|
@drop.prevent="handleDrop"
|
|
>
|
|
<input
|
|
ref="fileInput"
|
|
type="file"
|
|
multiple
|
|
accept="image/*,audio/*,.pdf"
|
|
class="hidden"
|
|
@change="handleFiles"
|
|
/>
|
|
|
|
<div v-if="uploading" class="upload-progress">
|
|
<div class="i-lucide-loader-2 h-6 w-6 animate-spin text-primary" />
|
|
<span>Upload en cours...</span>
|
|
</div>
|
|
|
|
<div v-else class="upload-content" @click="fileInput?.click()">
|
|
<div class="i-lucide-upload h-8 w-8 text-white/30 mb-2" />
|
|
<p class="text-sm text-white/50">Glissez des fichiers ici ou cliquez pour sélectionner</p>
|
|
<p class="text-xs text-white/30 mt-1">Images, audio, PDF</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const emit = defineEmits<{
|
|
uploaded: [files: string[]]
|
|
}>()
|
|
|
|
const fileInput = ref<HTMLInputElement>()
|
|
const isDragging = ref(false)
|
|
const uploading = ref(false)
|
|
|
|
function handleDrop(e: DragEvent) {
|
|
isDragging.value = false
|
|
const files = e.dataTransfer?.files
|
|
if (files) upload(files)
|
|
}
|
|
|
|
function handleFiles(e: Event) {
|
|
const target = e.target as HTMLInputElement
|
|
if (target.files) upload(target.files)
|
|
}
|
|
|
|
async function upload(files: FileList) {
|
|
uploading.value = true
|
|
try {
|
|
const formData = new FormData()
|
|
for (const file of files) {
|
|
formData.append('file', file)
|
|
}
|
|
|
|
const result = await $fetch<{ files: string[] }>('/api/admin/media/upload', {
|
|
method: 'POST',
|
|
body: formData,
|
|
})
|
|
|
|
emit('uploaded', result.files)
|
|
}
|
|
finally {
|
|
uploading.value = false
|
|
if (fileInput.value) fileInput.value.value = ''
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.upload-zone {
|
|
border: 2px dashed hsl(20 8% 22%);
|
|
border-radius: 0.75rem;
|
|
padding: 2rem;
|
|
text-align: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.upload-zone:hover {
|
|
border-color: hsl(12 76% 48% / 0.4);
|
|
}
|
|
|
|
.upload-zone--active {
|
|
border-color: hsl(12 76% 48%);
|
|
background: hsl(12 76% 48% / 0.05);
|
|
}
|
|
|
|
.upload-progress {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: hsl(20 8% 55%);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.upload-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
</style>
|