PronounsPage/components/FileUploader.vue

103 lines
2.8 KiB
Vue

<template>
<div>
<input
:id="id"
type="file"
:name="name + (multiple ? '[]' : '')"
:multiple="multiple"
:disabled="uploading"
:accept="mime"
@change="filesChange($event.target.name, $event.target.files)"
>
<label
:for="id"
:class="['uploader-container', form ? 'form-control p-2' : classes, drag ? 'drag' : '']"
@dragover="drag = true"
@dragleave="drag = false"
>
<p v-if="errorMessage" class="text-danger mb-0">
<Icon v="exclamation-circle" />
<T>{{ errorMessage }}</T>
</p>
<p v-else-if="uploading" class="mb-0">
<Spinner />
</p>
<p v-else class="mb-0">
<slot>
<Icon v="upload" />
<T>images.upload.instructionShort</T>
</slot>
</p>
</label>
</div>
</template>
<script>
export default {
props: {
url: { required: true },
multiple: { type: Boolean },
mime: { default: '*/*' },
name: { default: 'files' },
form: { type: Boolean },
classes: { default: 'btn btn-outline-primary btn-sm' },
},
data() {
return {
uploading: false,
drag: false,
errorMessage: '',
id: `upload-${this.name}`,
};
},
methods: {
async filesChange(fieldName, fileList) {
if (!fileList.length) {
return;
}
this.drag = false;
const formData = new FormData();
for (const file of fileList) {
formData.append(fieldName, file, file.name);
}
await this.save(formData);
},
async save(formData) {
this.uploading = true;
this.errorMessage = '';
try {
const ids = await $fetch(this.url, {
method: 'POST',
body: formData,
headers: {
'Content-Type': 'multipart/form-data',
},
});
this.$emit('uploaded', ids);
} catch (e) {
this.errorMessage = e?.response?.data?.error || 'error.invalidImage';
}
this.uploading = false;
},
},
};
</script>
<style lang="scss" scoped>
@import "../assets/variables";
.uploader-container {
position: relative;
cursor: pointer;
&.form-control {
&:hover, &.drag {
background: lighten($primary, 50%);
}
}
}
input[type="file"] {
display: none;
}
</style>