update frontend ui, i18n, filters, and deps
This commit is contained in:
94
frontend/components/FileUploader.vue
Normal file
94
frontend/components/FileUploader.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user