Add Single file upload component
This commit is contained in:
parent
c4eb67c09f
commit
3b8a129fa4
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<svg x="0px" y="0px" viewBox="0 0 511.626 511.627" xml:space="preserve" fill="currentColor">
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M392.857,292.354h-18.274c-2.669,0-4.859,0.855-6.563,2.573c-1.718,1.708-2.573,3.897-2.573,6.563v91.361 c0,12.563-4.47,23.315-13.415,32.262c-8.945,8.945-19.701,13.414-32.264,13.414H82.224c-12.562,0-23.317-4.469-32.264-13.414 c-8.945-8.946-13.417-19.698-13.417-32.262V155.31c0-12.562,4.471-23.313,13.417-32.259c8.947-8.947,19.702-13.418,32.264-13.418 h200.994c2.669,0,4.859-0.859,6.57-2.57c1.711-1.713,2.566-3.9,2.566-6.567V82.221c0-2.662-0.855-4.853-2.566-6.563 c-1.711-1.713-3.901-2.568-6.57-2.568H82.224c-22.648,0-42.016,8.042-58.102,24.125C8.042,113.297,0,132.665,0,155.313v237.542 c0,22.647,8.042,42.018,24.123,58.095c16.086,16.084,35.454,24.13,58.102,24.13h237.543c22.647,0,42.017-8.046,58.101-24.13 c16.085-16.077,24.127-35.447,24.127-58.095v-91.358c0-2.669-0.856-4.859-2.574-6.57 C397.709,293.209,395.519,292.354,392.857,292.354z"
|
||||
/>
|
||||
<path
|
||||
d="M506.199,41.971c-3.617-3.617-7.905-5.424-12.85-5.424H347.171c-4.948,0-9.233,1.807-12.847,5.424 c-3.617,3.615-5.428,7.898-5.428,12.847s1.811,9.233,5.428,12.85l50.247,50.248L198.424,304.067 c-1.906,1.903-2.856,4.093-2.856,6.563c0,2.479,0.953,4.668,2.856,6.571l32.548,32.544c1.903,1.903,4.093,2.852,6.567,2.852 s4.665-0.948,6.567-2.852l186.148-186.148l50.251,50.248c3.614,3.617,7.898,5.426,12.847,5.426s9.233-1.809,12.851-5.426 c3.617-3.616,5.424-7.898,5.424-12.847V54.818C511.626,49.866,509.813,45.586,506.199,41.971z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<svg height="426.66667pt" viewBox="0 0 426.66667 426.66667" width="426.66667pt" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="m405.332031 192h-170.664062v-170.667969c0-11.773437-9.558594-21.332031-21.335938-21.332031-11.773437 0-21.332031 9.558594-21.332031 21.332031v170.667969h-170.667969c-11.773437 0-21.332031 9.558594-21.332031 21.332031 0 11.777344 9.558594 21.335938 21.332031 21.335938h170.667969v170.664062c0 11.777344 9.558594 21.335938 21.332031 21.335938 11.777344 0 21.335938-9.558594 21.335938-21.335938v-170.664062h170.664062c11.777344 0 21.335938-9.558594 21.335938-21.335938 0-11.773437-9.558594-21.332031-21.335938-21.332031zm0 0" />
|
||||
</svg>
|
||||
</template>
|
|
@ -0,0 +1,117 @@
|
|||
<template>
|
||||
<label class="flex flex-col" :for="id">
|
||||
<span v-if="label" class="text-sm font-semibold text-gray-400">
|
||||
{{ label }}
|
||||
<span v-show="required" class="text-red-800"> *</span>
|
||||
</span>
|
||||
<div class="h-[35px] border-2 border-solid relative rounded-lg cursor-pointer flex-none border-gray-600 text-gray-300 bg-gray-700">
|
||||
<div class="flex items-center justify-center h-full" v-if="inner === null">
|
||||
<div class="relative text-sm text-gray-300 leading-none">Klicken oder Datei hierhin ziehen zum hochladen</div>
|
||||
<input class="hidden" ref="uploader" @change="upload($event.target.files)" type="file" :name="name" :id="id" :multiple="false" />
|
||||
<div class="absolute w-full h-full top-0 left-0 cursor-pointer" @drop="onDropping" @dragenter="onDragEnter" @dragover="onDragOver" @dragleave="onDragLeave"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center h-full space-x-2 px-2" v-else>
|
||||
<img :src="inner.icon" class="w-6 h-6" />
|
||||
<div v-text="inner.file_name" class="text-sm text-gray-300 leading-none grow"></div>
|
||||
<a href="#" @click.prevent="onDelete" v-tooltip="`Löschen`" class="flex justify-center items-center w-6 h-6 rounded-full bg-red-200">
|
||||
<trash-icon class="text-red-800 w-3 h-3"></trash-icon>
|
||||
</a>
|
||||
<a :href="inner.original_url" v-tooltip="`Öffnen`" class="flex justify-center items-center w-6 h-6 rounded-full bg-primary-700" target="_BLANK">
|
||||
<external-icon class="text-primary-200 w-3 h-3"></external-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {watch, ref, inject} from 'vue';
|
||||
import TrashIcon from './TrashIcon.vue';
|
||||
import ExternalIcon from './ExternalIcon.vue';
|
||||
import useReadFile from '../composables/useReadFile.js';
|
||||
import {useToast} from 'vue-toastification';
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const axios = inject('axios');
|
||||
const toast = useToast();
|
||||
|
||||
const inner = ref(null);
|
||||
const {dropping, onDragEnter, onDragOver, onDragLeave, processDrop, read} = useReadFile();
|
||||
|
||||
const props = defineProps({
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
collection: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
parentName: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
parentId: {
|
||||
required: true,
|
||||
validator: (value) => typeof value === 'number' || value === null,
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
label: {
|
||||
required: false,
|
||||
default: () => null,
|
||||
validator: (value) => value === null || typeof value === 'string',
|
||||
},
|
||||
});
|
||||
|
||||
function parseValue(v) {
|
||||
if (v === null) {
|
||||
return null;
|
||||
}
|
||||
if (v.fallback) {
|
||||
return null;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
var response = await axios.get(`/mediaupload/${props.parentName}/${props.parentId}/${props.collection}`);
|
||||
inner.value = parseValue(response.data);
|
||||
emit('update:modelValue', inner.value.id);
|
||||
}
|
||||
async function onDropping(e) {
|
||||
await upload(await processDrop(e, 1));
|
||||
}
|
||||
|
||||
async function upload(files) {
|
||||
await realUpload(await read(files[0]));
|
||||
}
|
||||
|
||||
async function realUpload(file) {
|
||||
inner.value = (
|
||||
await axios.post('/mediaupload', {
|
||||
parent: {model: props.parentName, collection_name: props.collection, id: props.parentId ? props.parentId : null},
|
||||
payload: file,
|
||||
})
|
||||
).data;
|
||||
emit('update:modelValue', inner.value);
|
||||
toast.success('Datei hochgeladen');
|
||||
}
|
||||
|
||||
async function onDelete() {
|
||||
await axios.delete(`/mediaupload/${inner.value.id}`);
|
||||
inner.value = null;
|
||||
emit('update:modelValue', null);
|
||||
toast.success('Datei entfernt');
|
||||
}
|
||||
|
||||
if (props.parentId !== null) {
|
||||
reload();
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 512 512"
|
||||
style="enable-background: new 0 0 512 512"
|
||||
xml:space="preserve"
|
||||
fill="currentColor"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M486.4,102.4h-128V25.6c0-15.36-10.24-25.6-25.6-25.6H179.2c-15.36,0-25.6,10.24-25.6,25.6v76.8h-128
|
||||
C10.24,102.4,0,112.64,0,128s10.24,25.6,25.6,25.6h460.8c15.36,0,25.6-10.24,25.6-25.6S501.76,102.4,486.4,102.4z M307.2,102.4
|
||||
H204.8V51.2h102.4V102.4z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M25.6,204.8l48.64,284.16c2.56,12.8,12.8,23.04,25.6,23.04h312.32c12.8,0,23.04-10.24,25.6-23.04L486.4,204.8H25.6z
|
||||
M153.6,460.8c-15.36,0-25.6-10.24-25.6-25.6l-25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6l25.6,153.6
|
||||
C179.2,450.56,168.96,460.8,153.6,460.8z M281.6,435.2c0,15.36-10.24,25.6-25.6,25.6s-25.6-10.24-25.6-25.6V281.6
|
||||
c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6V435.2z M384,435.2c0,15.36-10.24,25.6-25.6,25.6
|
||||
c-15.36,0-25.6-10.24-25.6-25.6l25.6-153.6c0-15.36,10.24-25.6,25.6-25.6s25.6,10.24,25.6,25.6L384,435.2z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
|
@ -0,0 +1,77 @@
|
|||
import accounting from 'accounting';
|
||||
import {ref} from 'vue';
|
||||
|
||||
accounting.settings.currency = {
|
||||
...accounting.settings.currency,
|
||||
precision: 2,
|
||||
format: '%v %s',
|
||||
decimal: ',',
|
||||
thousand: '.',
|
||||
};
|
||||
|
||||
export default function () {
|
||||
const dropping = ref(false);
|
||||
|
||||
async function read(file) {
|
||||
return new Promise((resolve) => {
|
||||
var reader = new FileReader();
|
||||
reader.onload = function () {
|
||||
var r = reader.result;
|
||||
resolve({
|
||||
content: r.substr(r.search(',') + 1),
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
});
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
function onDragEnter(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dropping.value = true;
|
||||
}
|
||||
function onDragOver(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
function onDragLeave(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dropping.value = false;
|
||||
}
|
||||
function processDrop(e, maxFiles) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
let dt = e.dataTransfer;
|
||||
let files = [...dt.files].slice(0, maxFiles ? maxFiles : dt.files.length);
|
||||
let result = [];
|
||||
for (const f in files) {
|
||||
if (typeof files[f] === 'object') {
|
||||
result.push(files[f]);
|
||||
}
|
||||
}
|
||||
dropping.value = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
function size(file) {
|
||||
if (file.size < 1000) {
|
||||
return accounting.formatMoney(file.size, {symbol: 'B'});
|
||||
}
|
||||
if (file.size < 1000000) {
|
||||
return accounting.formatMoney(file.size / 1000, {symbol: 'KB'});
|
||||
}
|
||||
if (file.size < 1000000000) {
|
||||
return accounting.formatMoney(file.size / 1000000, {symbol: 'MB'});
|
||||
}
|
||||
if (file.size < 1000000000000) {
|
||||
return accounting.formatMoney(file.size / 1000000000, {symbol: 'GB'});
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
return {dropping, onDragLeave, onDragOver, onDragEnter, processDrop, read};
|
||||
}
|
Loading…
Reference in New Issue