This commit is contained in:
2025-04-30 13:11:35 +03:00
parent c8f3c9801f
commit cda54b1e95
60 changed files with 1054 additions and 651 deletions

View File

@@ -0,0 +1,127 @@
<template>
<div>
<div
class="relative-position"
:style="{
width: sizePx,
height: sizePx,
display: 'block'
}"
>
<q-file
ref="imgFileSelector"
v-model="imageFile"
:style="{ display: 'none' }"
@update:model-value="handleUpload()"
:filter="checkImgType"
accept="image/*"
/>
<q-icon
v-if="modelValue === '' || modelValue === undefined"
name="mdi-camera-plus-outline"
class="absolute-full fit text-grey-4"
:style="{ fontSize: String(iconsize) + 'px'}"
@click = "imgFileSelectorClick"
/>
<q-img
v-else
fit="cover"
:src="modelValue"
:style="{
height: sizePx,
maxWidth: sizePx,
borderRadius: avatar ? String(size/2) + 'px' : 'var(--top-raduis)',
}"
@click="showDialog = true"
/>
</div>
<q-dialog v-model="showDialog">
<q-card class="w100 relative-position" style="height: auto;">
<q-img :src="modelValue"/>
<div
class="flex row items-center jutsify-center q-pb-sm"
style="bottom: 0; position: absolute; left: 50%; transform: translate(-50%, 0%);">
<q-btn
v-for="btn in menuBtns"
:key="btn.name"
:icon="btn.icon"
@click="btn.f"
class="q-mx-xs bg-white"
round flat
style="opacity: 0.8"
color="primary"
/>
</div>
</q-card>
</q-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, Ref, computed } from 'vue' // eslint-disable-line
import { QFile } from 'quasar'
const modelValue = defineModel<string>()
const props = defineProps<{
size?: number
iconsize?: number
avatar?: boolean
}>()
const imageFile = ref(null) // file-from selector
const imgFileSelector= ref() as Ref<QFile> // input file DOM
const size = ref<number>(props.size ? props.size : 100)
const iconsize = ref<number>(props.iconsize ? props.iconsize : 75)
const showDialog = ref<boolean>(false)
const menuBtns = [
{ name: 'change', icon: 'mdi-swap-horizontal', f: imgFileSelectorClick },
{ name: 'delete', icon: 'mdi-delete-outline', f: deleteImage },
{ name: 'close', icon: 'mdi-close', f: () => showDialog.value = false }
]
const sizePx = computed(() => {
return String(size.value) + 'px'
})
async function handleUpload () {
if (imageFile.value) {
const img = await imgToBase64(imageFile.value)
modelValue.value = typeof img === 'string' ? img : ''
}
}
function imgFileSelectorClick () {
imgFileSelector.value.pickFiles()
}
function deleteImage () {
showDialog.value = false
imageFile.value = null
modelValue.value = ''
}
function imgToBase64(file: File): Promise<string | ArrayBuffer | null> {
const reader: FileReader = new FileReader()
reader.readAsDataURL(file)
return new Promise((resolve, reject) => {
reader.onerror = () => {
reader.abort()
reject(new Error('Something went wrong'))
}
reader.onload = () => {
resolve(reader.result)
}
})
}
function checkImgType(files: File[]): File[] {
return files.filter((file: File) => file.type === 'image/x-png' || file.type === 'image/jpeg' || file.type === 'image/webp' )
}
</script>
<style scope>
</style>