upload file form WIP
This commit is contained in:
parent
7e1fced429
commit
3cfef87efb
|
@ -12,6 +12,7 @@
|
|||
"@quasar/extras": "^1.16.4",
|
||||
"axios": "^1.4.0",
|
||||
"chromium": "^3.0.3",
|
||||
"croppie": "^2.6.5",
|
||||
"pinia": "^2.1.3",
|
||||
"quasar": "^2.12.0",
|
||||
"validator": "^13.9.0",
|
||||
|
@ -3169,6 +3170,11 @@
|
|||
"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": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
"@quasar/extras": "^1.16.4",
|
||||
"axios": "^1.4.0",
|
||||
"chromium": "^3.0.3",
|
||||
"croppie": "^2.6.5",
|
||||
"pinia": "^2.1.3",
|
||||
"quasar": "^2.12.0",
|
||||
"validator": "^13.9.0",
|
||||
|
|
|
@ -1,22 +1,38 @@
|
|||
<script setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { reactive, computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
|
||||
import FetchData from 'components/FetchData.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 { t } = useI18n();
|
||||
const { notify } = useNotify();
|
||||
|
||||
const editFormData = reactive({
|
||||
const editPhotoFormData = reactive({
|
||||
uploadMethod: 'computer',
|
||||
file: null,
|
||||
quantity: null,
|
||||
url: null,
|
||||
viewportType: {
|
||||
code: 'normal',
|
||||
description: t('Normal'),
|
||||
viewport: {
|
||||
width: 400,
|
||||
height: 400,
|
||||
},
|
||||
output: {
|
||||
width: 1200,
|
||||
height: 1200,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const options = [
|
||||
|
@ -24,59 +40,295 @@ const options = [
|
|||
{ label: t('Import from external URL'), value: 'URL' },
|
||||
];
|
||||
|
||||
const onDataSaved = (data) => {
|
||||
emit('onDataSaved', data);
|
||||
const viewportTypes = [
|
||||
{
|
||||
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>
|
||||
|
||||
<template>
|
||||
<!-- <FetchData
|
||||
url="Warehouses"
|
||||
@on-fetch="(data) => (warehousesOptions = data)"
|
||||
<FetchData
|
||||
ref="allowTypesRef"
|
||||
url="ImageContainers/allowedContentTypes"
|
||||
@on-fetch="(data) => (allowedContentTypes = data.join(', '))"
|
||||
auto-load
|
||||
/> -->
|
||||
<FormModelPopup
|
||||
url-create="Items/regularize"
|
||||
model="Items"
|
||||
:title="t('Edit photo')"
|
||||
:form-initial-data="editFormData"
|
||||
@on-data-saved="onDataSaved($event)"
|
||||
/>
|
||||
<QForm class="all-pointer-events">
|
||||
<QCard class="q-pa-lg">
|
||||
<span ref="closeButton" class="close-icon" v-close-popup>
|
||||
<QIcon name="close" size="sm" />
|
||||
</span>
|
||||
<h1 class="title">{{ t('Edit photo') }}</h1>
|
||||
<div class="row q-gutter-lg">
|
||||
<div
|
||||
v-show="editPhotoFormData.file || editPhotoFormData.url"
|
||||
class="row q-gutter-lg items-center"
|
||||
>
|
||||
<template #form-inputs="{}">
|
||||
<QIcon
|
||||
name="rotate_left"
|
||||
size="sm"
|
||||
color="primary"
|
||||
class="cursor-pointer"
|
||||
@click="rotateLeft()"
|
||||
>
|
||||
<!-- <QTooltip class="no-pointer-events">
|
||||
{{ t('Rotate left') }}
|
||||
</QTooltip> -->
|
||||
</QIcon>
|
||||
<div>
|
||||
<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 class="column">
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QOptionGroup
|
||||
:options="options"
|
||||
type="radio"
|
||||
v-model="editFormData.uploadMethod"
|
||||
v-model="editPhotoFormData.uploadMethod"
|
||||
/>
|
||||
</div>
|
||||
</VnRow>
|
||||
<VnRow class="row q-gutter-md q-mb-md">
|
||||
<div class="col">
|
||||
<QFile
|
||||
:label="t('entry.buys.file')"
|
||||
v-if="editPhotoFormData.uploadMethod === 'computer'"
|
||||
:label="t('File')"
|
||||
:multiple="false"
|
||||
v-model="editFormData.file"
|
||||
@update:model-value="onFileChange($event)"
|
||||
@update:model-value="updatePhotoPreview($event)"
|
||||
:accept="allowedContentTypes"
|
||||
class="required"
|
||||
>
|
||||
<template #append>
|
||||
<QIcon name="vn:attach" class="cursor-pointer">
|
||||
<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>
|
||||
<pre>{{ editFormData }}</pre>
|
||||
</template>
|
||||
</FormModelPopup>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
es:
|
||||
Edit photo: Editar foto
|
||||
Select from computer: Seleccionar desde ordenador
|
||||
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>
|
||||
|
|
|
@ -1102,5 +1102,8 @@ export default {
|
|||
addToPinned: 'Add to pinned',
|
||||
removeFromPinned: 'Remove from pinned',
|
||||
},
|
||||
editPictureForm: {
|
||||
allowedFilesText: 'Allowed file types: { allowedContentTypes }',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1102,5 +1102,8 @@ export default {
|
|||
addToPinned: 'Añadir a fijados',
|
||||
removeFromPinned: 'Eliminar de fijados',
|
||||
},
|
||||
editPictureForm: {
|
||||
allowedFilesText: 'Tipos de archivo permitidos: { allowedContentTypes }',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue