|
|
@@ -6,6 +6,7 @@ Assistant de création d'image
|
|
|
<template>
|
|
|
<div class="input-image" >
|
|
|
<UiImage
|
|
|
+ ref="imageElement"
|
|
|
:image-id="modelValue"
|
|
|
:default-image="defaultImage"
|
|
|
:width="width"
|
|
|
@@ -59,6 +60,7 @@ Assistant de création d'image
|
|
|
{{$t('upload_image')}}
|
|
|
</button>
|
|
|
</div>
|
|
|
+ <span class="max-size-label">{{ $t('max_size_4_mb') }}</span>
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
@@ -86,6 +88,8 @@ import ApiResource from "~/models/ApiResource";
|
|
|
import {useImageManager} from "~/composables/data/useImageManager";
|
|
|
import { Cropper } from 'vue-advanced-cropper'
|
|
|
import 'vue-advanced-cropper/dist/style.css';
|
|
|
+import {FILE_FOLDER, FILE_STATUS, FILE_TYPE, FILE_VISIBILITY, TYPE_ALERT} from "~/types/enum/enums";
|
|
|
+import {usePageStore} from "~/stores/page";
|
|
|
|
|
|
const props = defineProps({
|
|
|
/**
|
|
|
@@ -136,14 +140,16 @@ const props = defineProps({
|
|
|
|
|
|
const { em } = useEntityManager()
|
|
|
const { imageManager } = useImageManager()
|
|
|
+const pageStore = usePageStore()
|
|
|
|
|
|
-const emit = defineEmits(['update', 'reload', 'reset', 'close'])
|
|
|
+const emit = defineEmits(['update:modelValue', 'update:image', 'reset'])
|
|
|
|
|
|
/**
|
|
|
* Références à des composants
|
|
|
*/
|
|
|
const fileInput: Ref<null | any> = ref(null)
|
|
|
const cropper: Ref<any> = ref(null)
|
|
|
+const imageElement: Ref<any> = ref(null)
|
|
|
|
|
|
/**
|
|
|
* L'objet File contenant les informations de l'image
|
|
|
@@ -175,6 +181,8 @@ const uploadedImage: Ref<AnyJson> = ref({
|
|
|
name: null
|
|
|
})
|
|
|
|
|
|
+const MAX_FILE_SIZE = 4 * 1024 * 1024
|
|
|
+
|
|
|
/**
|
|
|
* Coordonnées du cropper
|
|
|
*/
|
|
|
@@ -202,82 +210,85 @@ const defaultSize = (params: any): { width: number, height: number } | null => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const openModal = () => {
|
|
|
- if (!file.value) {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- if (file.value.config) {
|
|
|
- const config = JSON.parse(file.value.config)
|
|
|
- coordinates.value.left = config.x
|
|
|
- coordinates.value.top = config.y
|
|
|
- coordinates.value.height = config.height
|
|
|
- coordinates.value.width = config.width
|
|
|
- }
|
|
|
-
|
|
|
- uploadedImage.value.name = file.value.name
|
|
|
- uploadedImage.value.id = file.value.id
|
|
|
- uploadedImage.value.src = image.value
|
|
|
-
|
|
|
+/**
|
|
|
+ * Affiche la modale d'upload / modification de l'image
|
|
|
+ */
|
|
|
+const openModal = async () => {
|
|
|
showModal.value = true
|
|
|
+ pending.value = true
|
|
|
|
|
|
- console.log(defaultSize.value)
|
|
|
-}
|
|
|
-
|
|
|
+ if (props.modelValue !== null) {
|
|
|
+ // Un objet File existe déjà: on le récupère
|
|
|
+ file.value = await em.fetch(File, props.modelValue)
|
|
|
+ image.value = await imageManager.get(props.modelValue)
|
|
|
+
|
|
|
+ if (file.value.config) {
|
|
|
+ const config = JSON.parse(file.value.config)
|
|
|
+ coordinates.value.left = config.x
|
|
|
+ coordinates.value.top = config.y
|
|
|
+ coordinates.value.height = config.height
|
|
|
+ coordinates.value.width = config.width
|
|
|
+ }
|
|
|
|
|
|
+ uploadedImage.value.name = file.value.name
|
|
|
+ uploadedImage.value.id = file.value.id
|
|
|
+ uploadedImage.value.src = image.value
|
|
|
|
|
|
-/**
|
|
|
- * Charge le File et l'image correspondante.
|
|
|
- * Si le File possède une configuration, l'applique à l'image.
|
|
|
- *
|
|
|
- * @param id
|
|
|
- */
|
|
|
-const loadFile = async (id: number | string) => {
|
|
|
- pending.value = true
|
|
|
- file.value = await em.fetch(File, id as number)
|
|
|
- image.value = await imageManager.get(id as number)
|
|
|
+ } else {
|
|
|
+ // Nouveau File
|
|
|
+ file.value = em.newInstance(File)
|
|
|
+ }
|
|
|
|
|
|
pending.value = false
|
|
|
}
|
|
|
|
|
|
-if (props.modelValue !== null) {
|
|
|
- loadFile(props.modelValue)
|
|
|
+/**
|
|
|
+ * Réinitialise l'image sélectionnée
|
|
|
+ */
|
|
|
+const reset = () => {
|
|
|
+ uploadedImage.value.src = null
|
|
|
+ uploadedImage.value.file = null
|
|
|
+ uploadedImage.value.name = null
|
|
|
+ uploadedImage.value.id = null
|
|
|
+ URL.revokeObjectURL(uploadedImage.value.src)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Upload une image depuis le poste client
|
|
|
* @param event
|
|
|
*/
|
|
|
-const uploadImage = (event:any) => {
|
|
|
+const uploadImage = (event: any) => {
|
|
|
const { files } = event.target
|
|
|
|
|
|
if (!files || !files[0]) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ const uploadedFile = files[0]
|
|
|
+
|
|
|
+ if (uploadedFile.size > MAX_FILE_SIZE) {
|
|
|
+ pageStore.alerts.push({type: TYPE_ALERT.ALERT, messages: ['file_too_large'] })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
reset()
|
|
|
+
|
|
|
uploadedImage.value.name = files[0].name
|
|
|
uploadedImage.value.src = URL.createObjectURL(files[0])
|
|
|
uploadedImage.value.file = files[0]
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * Lorsque le cropper change de position / taille, on met à jour les coordonnées
|
|
|
- * @param config
|
|
|
- */
|
|
|
-const onCropperChange = ({ coordinates: config } : any) => {
|
|
|
- coordinates.value = config;
|
|
|
+ coordinates.value.top = 0
|
|
|
+ coordinates.value.left = 0
|
|
|
+ coordinates.value.height = uploadedImage.value.height
|
|
|
+ coordinates.value.width = uploadedImage.value.width
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Réinitialise l'image sélectionnée
|
|
|
+ * Lorsque le cropper change de position / taille, on met à jour les coordonnées
|
|
|
+ * @param newCoordinates
|
|
|
*/
|
|
|
-const reset = () => {
|
|
|
- uploadedImage.value.src = null
|
|
|
- uploadedImage.value.file = null
|
|
|
- uploadedImage.value.name = null
|
|
|
- uploadedImage.value.id = null
|
|
|
- URL.revokeObjectURL(uploadedImage.value.src)
|
|
|
+const onCropperChange = ({ coordinates: newCoordinates } : any) => {
|
|
|
+ coordinates.value = newCoordinates;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -286,7 +297,18 @@ const reset = () => {
|
|
|
const cancel = () => {
|
|
|
reset()
|
|
|
showModal.value = false
|
|
|
- emit('close')
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Construit la nouvelle config du fichier à partir des réglages actuels
|
|
|
+ */
|
|
|
+const updateFileConfig = () => {
|
|
|
+ file.value!!.config = JSON.stringify({
|
|
|
+ x: coordinates.value.left,
|
|
|
+ y: coordinates.value.top,
|
|
|
+ height: coordinates.value.height,
|
|
|
+ width: coordinates.value.width
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -300,19 +322,12 @@ const saveNewImage = async () => {
|
|
|
// On créé l'objet File à sauvegarder
|
|
|
file.value.name = uploadedImage.value.name
|
|
|
file.value.imgFieldName = props.field
|
|
|
+ file.value.visibility = FILE_VISIBILITY.EVERYBODY
|
|
|
+ file.value.folder = FILE_FOLDER.IMAGES
|
|
|
+ file.value.status = FILE_STATUS.READY
|
|
|
+ file.value.type = FILE_TYPE.UPLOADED
|
|
|
|
|
|
- // Construit la nouvelle config du fichier
|
|
|
- file.value!!.config = JSON.stringify({
|
|
|
- x: coordinates.value.left,
|
|
|
- y: coordinates.value.top,
|
|
|
- height: coordinates.value.height,
|
|
|
- width: coordinates.value.width
|
|
|
- })
|
|
|
-
|
|
|
- // TODO: utiliser des enums?
|
|
|
- file.value.visibility = 'EVERYBODY'
|
|
|
- file.value.folder = 'IMAGES'
|
|
|
- file.value.status = 'READY'
|
|
|
+ updateFileConfig()
|
|
|
|
|
|
if (props.ownerId) {
|
|
|
// TODO: revoir
|
|
|
@@ -321,9 +336,10 @@ const saveNewImage = async () => {
|
|
|
|
|
|
// TODO: A revoir, on doit pouvoir persister l'image aussi
|
|
|
const returnedFile = await em.persist(File, file.value)
|
|
|
+ // await imageManager.persist(file.value, uploadedImage.src)
|
|
|
|
|
|
//On émet un évent afin de mettre à jour le formulaire de départ
|
|
|
- emit('update', returnedFile.data['@id'])
|
|
|
+ emit('update:modelValue', returnedFile.id)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -334,20 +350,9 @@ const saveExistingImage = async () => {
|
|
|
throw new Error('No File object defined')
|
|
|
}
|
|
|
|
|
|
- file.value.id = uploadedImage.value.id
|
|
|
-
|
|
|
- // Construit la nouvelle config du fichier
|
|
|
- file.value!!.config = JSON.stringify({
|
|
|
- x: coordinates.value.left,
|
|
|
- y: coordinates.value.top,
|
|
|
- height: coordinates.value.height,
|
|
|
- width: coordinates.value.width
|
|
|
- })
|
|
|
+ updateFileConfig()
|
|
|
|
|
|
await em.persist(File, file.value) // TODO: à revoir
|
|
|
-
|
|
|
- // On émet un évent afin de mettre à jour le formulaire de départ
|
|
|
- emit('reload')
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -358,6 +363,7 @@ const save = async () => {
|
|
|
if (!file.value) {
|
|
|
throw new Error('No File object defined')
|
|
|
}
|
|
|
+ pageStore.loading = true
|
|
|
|
|
|
if (uploadedImage.value.src && uploadedImage.value.src !== image.value) {
|
|
|
// Une nouvelle image a été uploadée
|
|
|
@@ -367,8 +373,13 @@ const save = async () => {
|
|
|
await saveExistingImage()
|
|
|
} else {
|
|
|
// On a reset l'image
|
|
|
- emit('reset')
|
|
|
+ emit('update:modelValue', null)
|
|
|
}
|
|
|
+
|
|
|
+ imageElement.value.refresh()
|
|
|
+
|
|
|
+ showModal.value = false
|
|
|
+ pageStore.loading = false
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -382,10 +393,11 @@ onUnmounted(() => {
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
- .vue-advanced-cropper__stretcher{
|
|
|
+ :deep(.vue-advanced-cropper__stretcher) {
|
|
|
height: auto !important;
|
|
|
width: auto !important;
|
|
|
}
|
|
|
+
|
|
|
.loading{
|
|
|
height: 300px;
|
|
|
}
|
|
|
@@ -442,4 +454,13 @@ onUnmounted(() => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .max-size-label {
|
|
|
+ display: block;
|
|
|
+ width: 100%;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 13px;
|
|
|
+ color: rgb(var(--v-theme-on-neutral-soft));
|
|
|
+ margin-top: 6px;
|
|
|
+ }
|
|
|
</style>
|