Просмотр исходного кода

feat(shared-component): cria componente de imagem

ebagabee 3 недель назад
Родитель
Сommit
6526410051
1 измененных файлов с 134 добавлено и 0 удалено
  1. 134 0
      src/components/shared/AvatarImageComponent.vue

+ 134 - 0
src/components/shared/AvatarImageComponent.vue

@@ -0,0 +1,134 @@
+<template>
+  <div
+    class="avatar-wrapper relative-position"
+    :class="{ 'drag-over': isDragOver }"
+    @dragover.prevent="isDragOver = true"
+    @dragleave="isDragOver = false"
+    @drop.prevent="onDrop"
+  >
+    <div
+      v-if="!imageUrl"
+      class="full-width full-height flex flex-center column gap-xs no-image-state"
+      @click="triggerFilePicker"
+    >
+      <q-icon name="add_photo_alternate" size="32px" color="grey-5" />
+      <span class="text-grey-6" style="font-size: 12px">adicione uma imagem</span>
+    </div>
+
+    <template v-else>
+      <img :src="imageUrl" class="avatar-image" />
+
+      <div class="actions-overlay absolute row no-wrap" style="top: 6px; right: 6px; gap: 4px">
+        <q-btn
+          round
+          unelevated
+          size="xs"
+          icon="edit"
+          color="grey-8"
+          text-color="white"
+          @click.stop="triggerFilePicker"
+        >
+          <q-tooltip>Trocar imagem</q-tooltip>
+        </q-btn>
+        <q-btn
+          round
+          unelevated
+          size="xs"
+          icon="delete"
+          color="negative"
+          text-color="white"
+          @click.stop="removeImage"
+        >
+          <q-tooltip>Remover imagem</q-tooltip>
+        </q-btn>
+      </div>
+    </template>
+
+    <q-file
+      ref="filePickerRef"
+      v-model="fileModel"
+      accept="image/*"
+      style="display: none"
+      @update:model-value="onFileSelected"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+
+const emit = defineEmits(["update:file"]);
+
+const filePickerRef = ref(null);
+const fileModel = ref(null);
+const imageUrl = ref(null);
+const isDragOver = ref(false);
+
+function triggerFilePicker() {
+  filePickerRef.value.pickFiles();
+}
+
+function onFileSelected(file) {
+  if (!file) return;
+  const reader = new FileReader();
+  reader.onload = (e) => {
+    imageUrl.value = e.target.result;
+  };
+  reader.readAsDataURL(file);
+  emit("update:file", file);
+}
+
+function removeImage() {
+  imageUrl.value = null;
+  fileModel.value = null;
+  emit("update:file", null);
+}
+
+function onDrop(event) {
+  isDragOver.value = false;
+  const file = event.dataTransfer.files[0];
+  if (file && file.type.startsWith("image/")) {
+    fileModel.value = file;
+    onFileSelected(file);
+  }
+}
+</script>
+
+<style scoped>
+.avatar-wrapper {
+  width: 174px;
+  height: 149px;
+  border-radius: 8px;
+  border: 2px dashed #ccc;
+  overflow: hidden;
+  background-color: #f5f5f5;
+  cursor: pointer;
+  transition: border-color 0.2s, background-color 0.2s;
+}
+
+.avatar-wrapper:hover,
+.avatar-wrapper.drag-over {
+  border-color: #ff8340;
+  background-color: #fff5ef;
+}
+
+.no-image-state {
+  height: 100%;
+}
+
+.avatar-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  display: block;
+}
+
+.actions-overlay {
+  opacity: 0;
+  transition: opacity 0.2s;
+}
+
+.avatar-wrapper:hover .actions-overlay {
+  opacity: 1;
+}
+</style>