95 lines
2.4 KiB
Vue
95 lines
2.4 KiB
Vue
<template>
|
|
<div>
|
|
<div
|
|
class="card"
|
|
:style="dropStyle"
|
|
@dragenter.prevent="onDragEnter"
|
|
@dragover.prevent
|
|
@dragleave.prevent="onDragLeave"
|
|
@drop.prevent="onDrop"
|
|
>
|
|
<p>{{ label || t('fileUploader.label') }}</p>
|
|
<input :disabled="disabled" type="file" multiple @change="onFilesSelected" />
|
|
<button class="card" type="button" :disabled="disabled" @click="emitUpload">
|
|
{{ buttonText || t('actions.upload') }}
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="previews.length" class="grid" style="margin-top: 12px;">
|
|
<div v-for="preview in previews" :key="preview.name" class="card">
|
|
<img v-if="preview.url" :src="preview.url" :alt="preview.name" style="max-width: 100%;" />
|
|
<p>{{ preview.name }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const { t } = useI18n()
|
|
|
|
const props = defineProps({
|
|
disabled: { type: Boolean, default: false },
|
|
buttonText: { type: String, default: '' },
|
|
label: { type: String, default: '' }
|
|
})
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'upload', files: FileList): void
|
|
}>()
|
|
|
|
const files = ref<FileList | null>(null)
|
|
const isDragging = ref(false)
|
|
const previews = ref<{ name: string; url?: string }[]>([])
|
|
|
|
const dropStyle = computed(() => ({
|
|
border: isDragging.value ? '2px dashed #c46b2d' : '1px dashed #d9c9b2',
|
|
padding: '16px',
|
|
cursor: props.disabled ? 'not-allowed' : 'pointer',
|
|
opacity: props.disabled ? '0.6' : '1'
|
|
}))
|
|
|
|
const onFilesSelected = (event: Event) => {
|
|
const target = event.target as HTMLInputElement
|
|
files.value = target.files
|
|
buildPreviews(target.files)
|
|
}
|
|
|
|
const emitUpload = () => {
|
|
if (!files.value || files.value.length === 0) {
|
|
return
|
|
}
|
|
emit('upload', files.value)
|
|
}
|
|
|
|
const onDragEnter = () => {
|
|
if (props.disabled) return
|
|
isDragging.value = true
|
|
}
|
|
|
|
const onDragLeave = () => {
|
|
isDragging.value = false
|
|
}
|
|
|
|
const onDrop = (event: DragEvent) => {
|
|
if (props.disabled) return
|
|
isDragging.value = false
|
|
if (event.dataTransfer?.files) {
|
|
files.value = event.dataTransfer.files
|
|
buildPreviews(event.dataTransfer.files)
|
|
}
|
|
}
|
|
|
|
const buildPreviews = (fileList: FileList | null) => {
|
|
previews.value = []
|
|
if (!fileList) return
|
|
Array.from(fileList).forEach((file) => {
|
|
if (file.type.startsWith('image/')) {
|
|
const url = URL.createObjectURL(file)
|
|
previews.value.push({ name: file.name, url })
|
|
} else {
|
|
previews.value.push({ name: file.name })
|
|
}
|
|
})
|
|
}
|
|
</script>
|