Image.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <!--
  2. Composant Image permettant de charger et d'afficher une image stockée sur les serveurs Opentalent à partir de son id.
  3. Permet d'afficher une image par défaut si l'image demandée n'est pas disponible ou invalide.
  4. Si la propriété 'upload' est à 'true', propose aussi un input pour uploader une nouvelle image.
  5. -->
  6. <template>
  7. <main>
  8. <div class="image-wrapper" :style="{width: width + 'px'}">
  9. <v-img
  10. :src="imageSrc"
  11. :lazy-src="defaultImagePath"
  12. :height="height"
  13. :width="width"
  14. aspect-ratio="1"
  15. >
  16. <template #placeholder>
  17. <v-row
  18. class="fill-height ma-0"
  19. align="center"
  20. justify="center"
  21. v-if="pending"
  22. >
  23. <v-progress-circular
  24. :indeterminate="true"
  25. color="neutral"
  26. />
  27. </v-row>
  28. </template>
  29. </v-img>
  30. <div>
  31. <div v-if="upload" class="click-action hover" @click="openUpload=true"><v-icon>mdi-upload</v-icon></div>
  32. <UiInputImage
  33. v-if="openUpload"
  34. @close="openUpload=false"
  35. :existingImageId="id"
  36. :field="field"
  37. :ownerId="ownerId"
  38. @update="$emit('update', $event, field); openUpload=false"
  39. @reload="onReload"
  40. @reset="reset"
  41. ></UiInputImage>
  42. </div>
  43. </div>
  44. </main>
  45. </template>
  46. <script setup lang="ts">
  47. import {ref} from "@vue/reactivity";
  48. import type {Ref} from "@vue/reactivity";
  49. import {useImageFetch} from "~/composables/data/useImageFetch";
  50. import {onUnmounted, watch} from "@vue/runtime-core";
  51. import type {WatchStopHandle} from "@vue/runtime-core";
  52. import {useImageManager} from "~/composables/data/useImageManager";
  53. import ImageManager from "~/services/data/imageManager";
  54. const props = defineProps({
  55. id: {
  56. type: Number,
  57. required: false,
  58. default: null
  59. },
  60. defaultImage: {
  61. type: String,
  62. required: false
  63. },
  64. height: {
  65. type: Number,
  66. required: false
  67. },
  68. width: {
  69. type: Number,
  70. required: false
  71. },
  72. field: {
  73. type: String,
  74. required: false
  75. },
  76. upload: {
  77. type: Boolean,
  78. required: false,
  79. default: false
  80. },
  81. ownerId:{
  82. type: Number,
  83. required: false
  84. }
  85. })
  86. const { imageManager } = useImageManager()
  87. const { fetch } = useImageFetch()
  88. const defaultImagePath = props.defaultImage ?? ImageManager.defaultImage
  89. const openUpload: Ref<Boolean> = ref(false)
  90. const { data: imageSrc, pending, refresh } = fetch(props.id ?? null, defaultImagePath, props.height, props.width)
  91. const unwatch: WatchStopHandle = watch(() => props.id, async (newValue, oldValue) => {
  92. await refresh()
  93. })
  94. const onReload = async () => {
  95. await refresh()
  96. openUpload.value = false
  97. }
  98. /**
  99. * Quand on souhaite faire un reset de l'image
  100. */
  101. const reset = () => {
  102. imageSrc.value = defaultImagePath
  103. openUpload.value = false
  104. }
  105. /**
  106. * Lorsqu'on démonte le component, on supprime le watcher
  107. */
  108. onUnmounted(() => {
  109. unwatch()
  110. })
  111. </script>
  112. <style lang="scss">
  113. div.image-wrapper {
  114. display: block;
  115. position: relative;
  116. img {
  117. max-width: 100%;
  118. }
  119. .click-action {
  120. position: absolute;
  121. top:0;
  122. left:0;
  123. width: 100%;
  124. height: 100%;
  125. background: transparent;
  126. opacity: 0;
  127. transition: all .2s;
  128. &:hover {
  129. opacity: 1;
  130. background: rgb(var(--v-theme-neutral-strong));
  131. cursor: pointer;
  132. }
  133. i {
  134. color: rgb(var(--v-theme-on-neutral-strong));
  135. position: absolute;
  136. top: 50%;
  137. left: 50%;
  138. transform: translate(-50% , -50%);
  139. font-size: 50px;
  140. z-index: 1;
  141. opacity: 1;
  142. &:hover{
  143. color: rgb(var(--v-theme-primary-alt));
  144. }
  145. }
  146. }
  147. }
  148. </style>