v4
This commit is contained in:
127
src/components/pnImageSelector.vue
Normal file
127
src/components/pnImageSelector.vue
Normal 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>
|
||||
Reference in New Issue
Block a user