added multiple pic viewer and additional fixes

This commit is contained in:
Luca Haid
2025-07-10 11:58:44 +02:00
parent 081c548bd9
commit 02a47fa0b2
2 changed files with 188 additions and 12 deletions

View File

@@ -30,19 +30,20 @@
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column; /* Allow content to stack */
justify-content: center;
align-items: center;
z-index: 9999;
cursor: zoom-out; /* Indicate it's closable */
/* Ensure it can receive keyboard events for 'esc' */
outline: none;
}
.tt-fullscreen-image {
max-width: 90%;
max-height: 90%;
max-height: 80%; /* Give space for gallery */
object-fit: contain;
cursor: default; /* Change cursor back to default when over the image */
transition: all 0.3s ease-in-out;
}
.tt-fullscreen-close-btn {
@@ -61,4 +62,130 @@
.tt-fullscreen-close-btn:hover {
color: #f0f0f0;
}
.tt-fullscreen-gallery-nav {
position: absolute;
bottom: 20px; /* Position at the bottom */
width: 90%; /* Match image width */
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
z-index: 10000;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background for readability */
padding: 10px;
border-radius: 8px;
}
.tt-fullscreen-gallery-nav .nav-arrow {
background: none;
border: none;
color: white;
font-size: 24px;
cursor: pointer;
padding: 0 10px;
}
.tt-fullscreen-gallery-nav .nav-arrow:hover {
color: #f0f0f0;
}
.tt-fullscreen-gallery-nav .gallery-thumbnails {
display: flex;
gap: 8px;
overflow-x: auto; /* Allow horizontal scrolling for many thumbnails */
padding: 5px 0;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.tt-fullscreen-gallery-nav .gallery-thumbnails::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
.tt-fullscreen-gallery-nav .thumbnail {
width: 70px;
height: 70px;
object-fit: cover;
border: 2px solid transparent;
border-radius: 4px;
cursor: pointer;
transition: border-color 0.2s ease, transform 0.2s ease;
}
.tt-fullscreen-gallery-nav .thumbnail:hover {
border-color: #fff;
transform: scale(1.05);
}
.tt-fullscreen-gallery-nav .thumbnail.active {
border-color: #3b82f6; /* Highlight active thumbnail */
transform: scale(1.05);
}
/* Modal gallery grid for uploaded images */
.asset-gallery-upload-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); /* Responsive grid */
gap: 15px;
padding: 10px 0;
}
.asset-gallery-upload-grid .gallery-item {
position: relative;
border: 1px solid #eee;
border-radius: 5px;
overflow: hidden;
padding-bottom: 100%; /* Creates a square aspect ratio */
background-color: #f8f8f8;
}
.asset-gallery-upload-grid .gallery-item img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.asset-gallery-upload-grid .gallery-item.is-main::before {
content: '\f005'; /* FontAwesome star icon */
font-family: 'Font Awesome 5 Free';
font-weight: 900;
position: absolute;
top: 5px;
left: 5px;
color: gold;
background-color: rgba(0, 0, 0, 0.4);
border-radius: 3px;
padding: 2px 5px;
font-size: 14px;
z-index: 5;
}
.asset-gallery-upload-grid .gallery-item-actions {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: space-around;
padding: 5px;
opacity: 0;
transition: opacity 0.2s ease-in-out;
z-index: 4;
}
.asset-gallery-upload-grid .gallery-item:hover .gallery-item-actions {
opacity: 1;
}
.asset-gallery-upload-grid .gallery-item-actions .btn {
color: white;
font-size: 1rem;
padding: .25rem .5rem;
}

View File

@@ -36,11 +36,11 @@ Vue.component('asset-management', {
<template v-slot:assetdetails="{ row }">
<div class="d-flex align-items-center">
<tt-asset-image :image-id="row.mainImageId" class="mr-3"/>
<tt-asset-image :image-id="row.mainImageId" :all-image-ids="row.imageIds" class="mr-3"/>
<div>
<strong>{{ row.name }}</strong><br>
<strong v-html="row.name.replace(/\\n/g, '<br>')"></strong><br>
<small class="text-muted">{{ row.assetNumber }}</small><br>
<small v-if="row.description">{{ row.description }}</small>
<small v-if="row.description" v-html="row.description.replace(/\\n/g, '<br>')"></small>
</div>
</div>
</template>
@@ -103,15 +103,20 @@ Vue.component('asset-management', {
Vue.component('tt-asset-image', {
props: {
imageId: Number,
allImageIds: {
type: Array,
default: () => []
}
},
data() {
return {
isFullScreen: false,
currentFullScreenImageId: null,
};
},
template: `
<div class="asset-image-container" :style="{ cursor: imageId ? 'pointer' : 'default' }">
<img v-if="imageId" :src="'/File/show?id=' + imageId" @error="onImageError" @click="openFullScreen" class="asset-image"/>
<img v-if="imageId" :src="'/File/show?id=' + imageId" @error="onImageError" @click="openFullScreen(imageId)" class="asset-image"/>
<div v-else class="asset-image-placeholder">
<i class="fas fa-camera"></i>
</div>
@@ -122,11 +127,29 @@ Vue.component('tt-asset-image', {
@keydown.esc="closeFullScreen"
tabindex="-1"
ref="fullScreenOverlay">
<img :src="'/File/show?id=' + imageId"
<img :src="'/File/show?id=' + currentFullScreenImageId"
class="tt-fullscreen-image"
@click.stop /> <button class="tt-fullscreen-close-btn" @click="closeFullScreen">
<i class="fas fa-times"></i>
</button>
@click.stop />
<button class="tt-fullscreen-close-btn" @click="closeFullScreen">
<i class="fas fa-times"></i>
</button>
<div v-if="allImageIds.length > 1" class="tt-fullscreen-gallery-nav">
<button class="nav-arrow left" @click.stop="prevImage">
<i class="fas fa-chevron-left"></i>
</button>
<div class="gallery-thumbnails">
<img v-for="imgId in allImageIds"
:key="imgId"
:src="'/File/show?id=' + imgId"
:class="{'active': imgId === currentFullScreenImageId}"
@click.stop="setFullScreenImage(imgId)"
class="thumbnail" />
</div>
<button class="nav-arrow right" @click.stop="nextImage">
<i class="fas fa-chevron-right"></i>
</button>
</div>
</div>
</div>
`,
@@ -134,18 +157,42 @@ Vue.component('tt-asset-image', {
onImageError(event) {
event.target.src = 'https://placehold.co/60x60/eee/ccc?text=Error'; // Fallback placeholder
},
openFullScreen() {
openFullScreen(initialImageId) {
if (this.imageId) { // Only open if an image exists
this.isFullScreen = true;
this.currentFullScreenImageId = initialImageId;
this.$nextTick(() => {
if (this.$refs.fullScreenOverlay) {
this.$refs.fullScreenOverlay.focus(); // Focus the overlay to capture keydown events
}
document.addEventListener('keydown', this.handleKeyDown);
});
}
},
closeFullScreen() {
this.isFullScreen = false;
this.currentFullScreenImageId = null;
document.removeEventListener('keydown', this.handleKeyDown);
},
setFullScreenImage(imageId) {
this.currentFullScreenImageId = imageId;
},
prevImage() {
const currentIndex = this.allImageIds.indexOf(this.currentFullScreenImageId);
const prevIndex = (currentIndex - 1 + this.allImageIds.length) % this.allImageIds.length;
this.currentFullScreenImageId = this.allImageIds[prevIndex];
},
nextImage() {
const currentIndex = this.allImageIds.indexOf(this.currentFullScreenImageId);
const nextIndex = (currentIndex + 1) % this.allImageIds.length;
this.currentFullScreenImageId = this.allImageIds[nextIndex];
},
handleKeyDown(event) {
if (event.key === 'ArrowLeft') {
this.prevImage();
} else if (event.key === 'ArrowRight') {
this.nextImage();
}
}
},
watch: {
@@ -365,7 +412,7 @@ Vue.component('asset-management-modal', {
<div v-if="!asset.imageIds || asset.imageIds.length === 0" class="text-muted text-center p-3">
Noch keine Bilder hochgeladen.
</div>
<div v-else class="asset-gallery">
<div v-else class="asset-gallery-upload-grid">
<div v-for="imgId in asset.imageIds" :key="imgId"
class="gallery-item"
:class="{ 'is-main': imgId === asset.mainImageId }">
@@ -413,6 +460,8 @@ Vue.component('asset-management-modal', {
const response = await axios.get(`${window.TT_CONFIG.BASE_PATH}/AssetManagement/getById`, { params: { id: this.id } });
if (!response.data.imageIds) {
response.data.imageIds = [];
} else if (typeof response.data.imageIds === 'string') {
response.data.imageIds = JSON.parse(response.data.imageIds);
}
this.asset = response.data;
} else {