upload file form WIP

This commit is contained in:
William Buezas 2024-01-30 21:54:14 -03:00
parent 7e1fced429
commit 3cfef87efb
5 changed files with 311 additions and 46 deletions

6
package-lock.json generated
View File

@ -12,6 +12,7 @@
"@quasar/extras": "^1.16.4", "@quasar/extras": "^1.16.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"chromium": "^3.0.3", "chromium": "^3.0.3",
"croppie": "^2.6.5",
"pinia": "^2.1.3", "pinia": "^2.1.3",
"quasar": "^2.12.0", "quasar": "^2.12.0",
"validator": "^13.9.0", "validator": "^13.9.0",
@ -3169,6 +3170,11 @@
"node": ">= 10" "node": ">= 10"
} }
}, },
"node_modules/croppie": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/croppie/-/croppie-2.6.5.tgz",
"integrity": "sha512-IlChnVUGG5T3w2gRZIaQgBtlvyuYnlUWs2YZIXXR3H9KrlO1PtBT3j+ykxvy9eZIWhk+V5SpBmhCQz5UXKrEKQ=="
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",

View File

@ -19,6 +19,7 @@
"@quasar/extras": "^1.16.4", "@quasar/extras": "^1.16.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"chromium": "^3.0.3", "chromium": "^3.0.3",
"croppie": "^2.6.5",
"pinia": "^2.1.3", "pinia": "^2.1.3",
"quasar": "^2.12.0", "quasar": "^2.12.0",
"validator": "^13.9.0", "validator": "^13.9.0",

View File

@ -1,22 +1,38 @@
<script setup> <script setup>
import { reactive, ref } from 'vue'; import { reactive, computed, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue'; import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
import VnRow from 'components/ui/VnRow.vue'; import VnRow from 'components/ui/VnRow.vue';
import FormModelPopup from './FormModelPopup.vue'; import VnInput from 'src/components/common/VnInput.vue';
const emit = defineEmits(['onDataSaved']); import Croppie from 'croppie/croppie';
import 'croppie/croppie.css';
import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
const props = defineProps({}); const props = defineProps({});
const { t } = useI18n(); const { t } = useI18n();
const { notify } = useNotify();
const editFormData = reactive({ const editPhotoFormData = reactive({
uploadMethod: 'computer', uploadMethod: 'computer',
file: null, file: null,
quantity: null, url: null,
viewportType: {
code: 'normal',
description: t('Normal'),
viewport: {
width: 400,
height: 400,
},
output: {
width: 1200,
height: 1200,
},
},
}); });
const options = [ const options = [
@ -24,59 +40,295 @@ const options = [
{ label: t('Import from external URL'), value: 'URL' }, { label: t('Import from external URL'), value: 'URL' },
]; ];
const onDataSaved = (data) => { const viewportTypes = [
emit('onDataSaved', data); {
code: 'normal',
description: t('Normal'),
viewport: {
width: 400,
height: 400,
},
output: {
width: 1200,
height: 1200,
},
},
{
code: 'panoramic',
description: t('Panoramic'),
viewport: {
width: 675,
height: 450,
},
output: {
width: 1350,
height: 900,
},
},
{
code: 'vertical',
description: t('Vertical'),
viewport: {
width: 306.66,
height: 533.33,
},
output: {
width: 460,
height: 800,
},
},
];
const allowedContentTypes = ref('');
const photoContainerRef = ref(null);
const editor = ref(null);
const displayEditor = () => {
const viewportType = editPhotoFormData.viewportType;
const viewport = viewportType.viewport;
const boundaryWidth = viewport.width + 200;
const boundaryHeight = viewport.height + 200;
if (editor.value) editor.value.destroy();
editor.value = new Croppie(photoContainerRef.value, {
viewport: { width: viewport.width, height: viewport.height },
boundary: { width: boundaryWidth, height: boundaryHeight },
enableOrientation: true,
showZoomer: true,
});
};
const viewportSelection = computed({
get() {
return editPhotoFormData.viewportType;
},
set(val) {
editPhotoFormData.viewportType = val;
const hasFile = editPhotoFormData.file || editPhotoFormData.url;
if (!val || !hasFile) return;
let file;
if (editPhotoFormData.uploadMethod == 'computer') file = editPhotoFormData.file;
else if (editPhotoFormData.uploadMethod == 'URL') file = editPhotoFormData.url;
updatePhotoPreview(file);
},
});
const updatePhotoPreview = (value) => {
if (value) {
displayEditor();
if (editPhotoFormData.uploadMethod == 'computer') {
editPhotoFormData.file = value;
const reader = new FileReader();
reader.onload = (e) => editor.value.bind({ url: e.target.result });
reader.readAsDataURL(value);
} else if (editPhotoFormData.uploadMethod == 'URL') {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = value;
img.onload = () => editor.value.bind({ url: value });
img.onerror = () => {
notify(
t("This photo provider doesn't allow remote downloads"),
'negative'
);
};
}
}
};
const rotateLeft = () => {
editor.value.rotate(90);
};
const rotateRight = () => {
editor.value.rotate(-90);
};
const onUploadAccept = () => {
try {
if (!this.newPhoto.files) throw new Error(`Select an image`);
const options = {
type: 'blob',
};
return this.editor
.result(options)
.then((blob) => (this.newPhoto.blob = blob))
.then(() => this.makeRequest());
} catch (e) {
this.vnApp.showError(this.$t(e.message));
return false;
}
};
const makeRequest = async () => {
// const options = {
// method: 'POST',
// url: `Images/upload`,
// params: this.newPhoto,
// headers: { 'Content-Type': undefined },
// timeout: this.canceler.promise,
// transformRequest: ([file]) => {
// const formData = new FormData();
// const now = Date.vnNew();
// const timestamp = now.getTime();
// const fileName = `${file.name}_${timestamp}`;
// formData.append('blob', this.newPhoto.blob, fileName);
// return formData;
// },
// data: this.newPhoto.files,
// };
await axios.post('Images/upload');
this.$http(options)
.then(() => this.vnApp.showSuccess(this.$t('Data saved!')))
.then(() => this.emit('response'))
.finally(() => (this.canceler = null));
}; };
</script> </script>
<template> <template>
<!-- <FetchData <FetchData
url="Warehouses" ref="allowTypesRef"
@on-fetch="(data) => (warehousesOptions = data)" url="ImageContainers/allowedContentTypes"
@on-fetch="(data) => (allowedContentTypes = data.join(', '))"
auto-load auto-load
/> --> />
<FormModelPopup <QForm class="all-pointer-events">
url-create="Items/regularize" <QCard class="q-pa-lg">
model="Items" <span ref="closeButton" class="close-icon" v-close-popup>
:title="t('Edit photo')" <QIcon name="close" size="sm" />
:form-initial-data="editFormData" </span>
@on-data-saved="onDataSaved($event)" <h1 class="title">{{ t('Edit photo') }}</h1>
> <div class="row q-gutter-lg">
<template #form-inputs="{}"> <div
<VnRow class="row q-gutter-md q-mb-md"> v-show="editPhotoFormData.file || editPhotoFormData.url"
<div class="col"> class="row q-gutter-lg items-center"
<QOptionGroup >
:options="options" <QIcon
type="radio" name="rotate_left"
v-model="editFormData.uploadMethod" size="sm"
/> color="primary"
</div> class="cursor-pointer"
</VnRow> @click="rotateLeft()"
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
:label="t('entry.buys.file')"
:multiple="false"
v-model="editFormData.file"
@update:model-value="onFileChange($event)"
class="required"
> >
<template #append> <!-- <QTooltip class="no-pointer-events">
<QIcon name="vn:attach" class="cursor-pointer"> {{ t('Rotate left') }}
<QTooltip>{{ t('Select a file') }}</QTooltip> </QTooltip> -->
</QIcon> </QIcon>
</template> <div>
</QFile> <div ref="photoContainerRef" />
</div>
<QIcon
name="rotate_right"
size="sm"
color="primary"
class="cursor-pointer"
@click="rotateRight()"
>
<!-- <QTooltip class="no-pointer-events">
{{ t('Rotate right') }}
</QTooltip> -->
</QIcon>
</div> </div>
</VnRow>
<pre>{{ editFormData }}</pre> <div class="column">
</template> <VnRow class="row q-gutter-md q-mb-md">
</FormModelPopup> <div class="col">
<QOptionGroup
:options="options"
type="radio"
v-model="editPhotoFormData.uploadMethod"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
v-if="editPhotoFormData.uploadMethod === 'computer'"
:label="t('File')"
:multiple="false"
@update:model-value="updatePhotoPreview($event)"
:accept="allowedContentTypes"
class="required"
>
<template #append>
<QIcon
name="vn:attach"
class="cursor-pointer q-mr-sm"
>
<QTooltip>{{ t('Select a file') }}</QTooltip>
</QIcon>
<QIcon name="info" class="cursor-pointer">
<QTooltip>{{
t(
'components.editPictureForm.allowedFilesText',
{
allowedContentTypes:
allowedContentTypes,
}
)
}}</QTooltip>
</QIcon>
</template>
</QFile>
<VnInput
v-if="editPhotoFormData.uploadMethod === 'URL'"
v-model="editPhotoFormData.url"
@update:model-value="updatePhotoPreview($event)"
placeholder="https://"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Orientation')"
:options="viewportTypes"
hide-selected
option-label="description"
v-model="viewportSelection"
/>
</div>
</VnRow>
</div>
</div>
</QCard>
</QForm>
</template> </template>
<style lang="scss" scoped>
.title {
font-size: 17px;
font-weight: bold;
line-height: 20px;
}
.close-icon {
position: absolute;
top: 20px;
right: 20px;
cursor: pointer;
}
</style>
<i18n> <i18n>
es: es:
Edit photo: Editar foto Edit photo: Editar foto
Select from computer: Seleccionar desde ordenador Select from computer: Seleccionar desde ordenador
Import from external URL: Importar desde URL externa Import from external URL: Importar desde URL externa
Vertical: Vertical
Normal: Normal
Panoramic: Panorámica
Orientation: Orientación
File: Fichero
This photo provider doesn't allow remote downloads: Este proveedor de fotos no permite descargas remotas
Rotate left: Girar a la izquierda
Rotate right: Girar a la derecha
</i18n> </i18n>

View File

@ -1102,5 +1102,8 @@ export default {
addToPinned: 'Add to pinned', addToPinned: 'Add to pinned',
removeFromPinned: 'Remove from pinned', removeFromPinned: 'Remove from pinned',
}, },
editPictureForm: {
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
},
}, },
}; };

View File

@ -1102,5 +1102,8 @@ export default {
addToPinned: 'Añadir a fijados', addToPinned: 'Añadir a fijados',
removeFromPinned: 'Eliminar de fijados', removeFromPinned: 'Eliminar de fijados',
}, },
editPictureForm: {
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
},
}, },
}; };