|
|
@@ -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>
|