AvatarImageComponent.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. <template>
  2. <div
  3. class="avatar-wrapper relative-position"
  4. :class="{ 'drag-over': isDragOver }"
  5. @dragover.prevent="isDragOver = true"
  6. @dragleave="isDragOver = false"
  7. @drop.prevent="onDrop"
  8. >
  9. <div
  10. v-if="!imageUrl"
  11. class="full-width full-height flex flex-center column gap-xs no-image-state"
  12. @click="openChangeDialog"
  13. >
  14. <q-icon name="add_photo_alternate" size="32px" color="grey-5" />
  15. <span class="text-grey-6" style="font-size: 12px">adicione uma imagem</span>
  16. </div>
  17. <template v-else>
  18. <img :src="imageUrl" class="avatar-image" />
  19. <div class="actions-overlay absolute row no-wrap" style="top: 6px; right: 6px; gap: 4px">
  20. <q-btn
  21. round
  22. unelevated
  23. size="xs"
  24. icon="edit"
  25. color="grey-8"
  26. text-color="white"
  27. @click.stop="openChangeDialog"
  28. >
  29. <q-tooltip>Trocar imagem</q-tooltip>
  30. </q-btn>
  31. <q-btn
  32. round
  33. unelevated
  34. size="xs"
  35. icon="delete"
  36. color="negative"
  37. text-color="white"
  38. @click.stop="removeImage"
  39. >
  40. <q-tooltip>Remover imagem</q-tooltip>
  41. </q-btn>
  42. </div>
  43. </template>
  44. </div>
  45. </template>
  46. <script setup>
  47. import { ref } from "vue";
  48. import { useQuasar } from "quasar";
  49. import ChangeImageDialog from "src/components/shared/ChangeImageDialog.vue";
  50. const emit = defineEmits(["update:file"]);
  51. const $q = useQuasar();
  52. const imageUrl = ref(null);
  53. const isDragOver = ref(false);
  54. function openChangeDialog() {
  55. $q.dialog({ component: ChangeImageDialog }).onOk(({ file, previewUrl }) => {
  56. imageUrl.value = previewUrl;
  57. emit("update:file", file);
  58. });
  59. }
  60. function removeImage() {
  61. imageUrl.value = null;
  62. emit("update:file", null);
  63. }
  64. function onDrop(event) {
  65. isDragOver.value = false;
  66. const file = event.dataTransfer.files[0];
  67. if (file && file.type.startsWith("image/")) {
  68. const reader = new FileReader();
  69. reader.onload = (e) => {
  70. imageUrl.value = e.target.result;
  71. };
  72. reader.readAsDataURL(file);
  73. emit("update:file", file);
  74. }
  75. }
  76. </script>
  77. <style scoped>
  78. .avatar-wrapper {
  79. width: 174px;
  80. height: 149px;
  81. border-radius: 8px;
  82. border: 2px dashed #ccc;
  83. overflow: hidden;
  84. background-color: #f5f5f5;
  85. cursor: pointer;
  86. transition: border-color 0.2s, background-color 0.2s;
  87. }
  88. .avatar-wrapper:hover,
  89. .avatar-wrapper.drag-over {
  90. border-color: #ff8340;
  91. background-color: #fff5ef;
  92. }
  93. .no-image-state {
  94. height: 100%;
  95. }
  96. .avatar-image {
  97. width: 100%;
  98. height: 100%;
  99. object-fit: cover;
  100. display: block;
  101. }
  102. .actions-overlay {
  103. opacity: 0;
  104. transition: opacity 0.2s;
  105. }
  106. .avatar-wrapper:hover .actions-overlay {
  107. opacity: 1;
  108. }
  109. </style>