details view image component refactor
All checks were successful
/ Make image (push) Successful in 39s

This commit is contained in:
nquidox 2025-10-19 17:30:22 +03:00
parent 9d80345b77
commit bb40d17e6b
5 changed files with 118 additions and 39 deletions

View file

@ -20,12 +20,27 @@
justify-content: center;
}
.center-button-container {
.button-container-center {
display: flex;
justify-content: center;
margin-top: 10px;
}
.button-container-evenly {
display: flex;
justify-content: space-between !important;
margin-top: 10px;
}
.button-container-evenly .n-button {
flex: 1;
min-width: 0;
}
.button-gap {
gap: 10px;
}
.center-button {
min-width: 320px;
}
@ -137,3 +152,17 @@ body,
.main-content {
flex: 1;
}
.image-preview-modal {
width: 80%;
max-width: 800px;
}
.image-preview-modal .n-card__content {
padding: 0;
text-align: center;
}
.preview-modal-image {
max-width: 99%;
max-height: 70vh;
object-fit: contain;
}

View file

@ -190,7 +190,7 @@ onMounted(() => {
</n-card>
<div v-else>Not found</div>
<div class="center-button-container">
<div class="button-container-center">
<n-button type="error" class="center-button" @click="onDelete">Delete</n-button>
</div>

View file

@ -31,8 +31,27 @@ function onModalClose() {
previewImageUrl.value = ''
}
function beforeUpload({ fileList: newFileList }) {
return newFileList.length <= 1
const fileInput = ref(null)
function triggerFileInput() {
fileInput.value?.click()
}
function onFileInputChange(event) {
const files = event.target.files
if (!files || files.length === 0) return
const file = files[0]
handleUpload({
fileList: [
{
file,
name: file.name,
status: 'pending',
},
],
})
event.target.value = ''
}
async function handleUpload({ fileList: newFileList }) {
@ -100,36 +119,30 @@ const cancelDelete = () => {
<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">
<div v-if="fileList.length === 0" class="upload-wrapper" @click="triggerFileInput">
<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"
>
<div v-else class="preview-container clickable-image">
<div>
<img
:src="previewImageUrl"
style="width: 100%; max-height: 600px; object-fit: contain"
:src="fileList[0].url"
alt="Preview"
class="preview-image"
@click="triggerFileInput"
/>
</n-modal>
</div>
<div class="button-container-evenly button-gap">
<n-button type="primary" @click="handlePreview(fileList[0])">Preview</n-button>
<n-button type="error" @click="deleteImageHandler">Delete</n-button>
</div>
</div>
</div>
<n-modal v-model:show="showModal" preset="card" title="Preview" @after-leave="onModalClose" class="image-preview-modal">
<img :src="previewImageUrl" alt="Preview" class="preview-modal-image"/>
</n-modal>
<n-modal v-model:show="showConfirmDelete" preset="dialog" title="Confirmation">
<template #default>
@ -140,13 +153,22 @@ const cancelDelete = () => {
<n-button @click="cancelDelete">Cancel</n-button>
</template>
</n-modal>
<input
ref="fileInput"
type="file"
accept="image/*"
style="display: none"
@change="onFileInputChange"
/>
</template>
<style scoped>
.upload-trigger {
.upload-wrapper {
width: 300px;
max-height: 600px;
min-height: 180px;
max-width: 300px;
height: 300px;
max-height: 300px;
border: 1px dashed #ccc;
border-radius: 8px;
background-color: #fafafa;
@ -160,14 +182,16 @@ const cancelDelete = () => {
border-color 0.2s ease;
}
.upload-trigger:hover {
.upload-wrapper:hover {
border-color: #18a058;
background-color: #f0fdf4;
}
.upload-icon {
width: 150px;
max-width: 150px;
height: 150px;
max-height: 150px;
opacity: 0.6;
}
@ -175,6 +199,32 @@ const cancelDelete = () => {
margin-top: 8px;
font-size: 14px;
color: #555;
background-color: rgba(24, 160, 88, 0.4);
padding: 6px 12px;
border-radius: 6px;
display: inline-block;
}
.preview-container {
display: block;
width: 100%;
max-width: 400px;
margin: 0 auto;
}
.preview-image {
width: 100%;
height: auto;
display: block;
max-width: 100%;
object-fit: contain;
}
.clickable-image {
cursor: pointer;
transition: opacity 0.2s;
}
.clickable-image:hover {
opacity: 0.9;
}
</style>

View file

@ -108,7 +108,7 @@ const insertAutoCompletedLink = () => {
</script>
<template>
<div v-if="props.origin === 'mandarake'" class="center-button-container mb-10">
<div v-if="props.origin === 'mandarake'" class="button-container-center mb-10">
<n-button type="warning" class="center-button" @click="insertAutoCompletedLink"
>Insert auto-completed link</n-button
>
@ -127,7 +127,7 @@ const insertAutoCompletedLink = () => {
<n-button size="small" type="primary" :loading="loading" @click="save">Save</n-button>
<n-button size="small" @click="cancel">Cancel</n-button>
</div>
<div class="center-button-container">
<div class="button-container-center">
<n-button type="error" class="center-button" @click="clearLink">Clear link</n-button>
</div>

View file

@ -48,7 +48,7 @@ const onLogout = () => {
{{ formattedDate }}
</n-thing>
</n-list-item>
<div class="center-button-container">
<div class="button-container-center">
<n-button type="info" class="center-button" @click="onLogout">Log out</n-button>
</div>
</n-list>