frontend/src/views/DetailsView/DetailsViewImages.vue

181 lines
4.1 KiB
Vue
Raw Normal View History

2025-10-18 14:10:21 +03:00
<script setup>
import { onMounted, ref } from 'vue'
import { NModal, NUpload, useMessage } from 'naive-ui'
import BoxIcon from '@/components/icons/BoxIcon.vue'
import { useMerchImagesApi } from '@/api/merchImages.js'
const { uploadImage, getImageUrl, deleteImage } = useMerchImagesApi()
const message = useMessage()
const props = defineProps({
merchUuid: { type: String, required: true },
})
const showModal = ref(false)
const previewImageUrl = ref('')
const fileList = ref([])
function handlePreview(file) {
if (file.file) {
previewImageUrl.value = URL.createObjectURL(file.file)
} else if (file.url) {
previewImageUrl.value = file.url
}
showModal.value = true
}
function onModalClose() {
if (previewImageUrl.value && previewImageUrl.value.startsWith('blob:')) {
URL.revokeObjectURL(previewImageUrl.value)
}
previewImageUrl.value = ''
}
function beforeUpload({ fileList: newFileList }) {
return newFileList.length <= 1
}
async function handleUpload({ fileList: newFileList }) {
const file = newFileList[newFileList.length - 1]
try {
await uploadImage(props.merchUuid, file.file)
const { imgUrl } = await getImageUrl(props.merchUuid, 'full')
message.success('Image uploaded successfully.')
fileList.value = [
{
name: file.name,
url: imgUrl,
status: 'finished',
},
]
} catch (error) {
message.error('Upload error: ' + (error.message || 'Unknown error.'))
}
}
onMounted(async () => {
try {
const { imgUrl } = await getImageUrl(props.merchUuid, 'full')
fileList.value = [
{
name: 'full.jpg',
url: imgUrl,
status: 'finished',
},
]
} catch (error) {
fileList.value = []
if (!error.message?.includes('404')) {
console.error('Error getting image: ', error)
}
}
})
const showConfirmDelete = ref(false)
const deleteImageHandler = async () => {
showConfirmDelete.value = true
return false //prevents instant "delete image" in n-upload before confirm
}
const confirmDeleteImage = async () => {
try {
await deleteImage(props.merchUuid)
fileList.value = []
message.success('Image deleted successfully.')
} catch (error) {
message.error('Image delete error: ' + (error.message || 'Unknown error.'))
} finally {
showConfirmDelete.value = false
}
}
const cancelDelete = () => {
showConfirmDelete.value = false
}
</script>
<template>
<div>
<n-upload
v-model:file-list="fileList"
:default-upload="false"
list-type="image-card"
:max="1"
:before-upload="beforeUpload"
@before-preview="handlePreview"
@change="handleUpload"
@remove="deleteImageHandler"
>
<div class="upload-trigger" v-if="fileList.length === 0">
<BoxIcon class="upload-icon" />
<span class="upload-text">Click to Upload</span>
</div>
</n-upload>
<n-modal
v-model:show="showModal"
preset="card"
style="width: 600px"
title="Preview"
@after-leave="onModalClose"
>
<img
:src="previewImageUrl"
style="width: 100%; max-height: 600px; object-fit: contain"
alt="Preview"
/>
</n-modal>
</div>
<n-modal v-model:show="showConfirmDelete" preset="dialog" title="Confirmation">
<template #default>
<p>Confirm delete image</p>
</template>
<template #action>
<n-button @click="confirmDeleteImage" type="error">Delete</n-button>
<n-button @click="cancelDelete">Cancel</n-button>
</template>
</n-modal>
</template>
<style scoped>
.upload-trigger {
width: 300px;
max-height: 600px;
min-height: 180px;
border: 1px dashed #ccc;
border-radius: 8px;
background-color: #fafafa;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition:
background-color 0.2s ease,
border-color 0.2s ease;
}
.upload-trigger:hover {
border-color: #18a058;
background-color: #f0fdf4;
}
.upload-icon {
width: 150px;
height: 150px;
opacity: 0.6;
}
.upload-text {
margin-top: 8px;
font-size: 14px;
color: #555;
}
</style>