Merge branch 'WarehouseShippingNote/update-tablet-view' into 'master'
added new logout button and fixed pdf viewing See merge request fronk/thetool!1638
This commit is contained in:
@@ -706,7 +706,7 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
$endTimestamp = $endDate->setTime(23, 59, 59)->getTimestamp();
|
$endTimestamp = $endDate->setTime(23, 59, 59)->getTimestamp();
|
||||||
|
|
||||||
$filteredEvents = array_filter($allEvents, function ($event) use ($startTimestamp, $endTimestamp, $calendarId) {
|
$filteredEvents = array_filter($allEvents, function ($event) use ($startTimestamp, $endTimestamp, $calendarId) {
|
||||||
if (!isset($event['cstart']) && !isset($event['category']) || (intval($event['calendar_id']['calendar_id']) !== $calendarId)) {
|
if (!isset($event['cstart']) && !isset($event['category']) || (intval($event['calendar_id']['calendar_id']) != $calendarId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$eventStartTimestamp = strtotime($event['cstart']['cstart'] ?? $event['cstart']);
|
$eventStartTimestamp = strtotime($event['cstart']['cstart'] ?? $event['cstart']);
|
||||||
@@ -739,6 +739,15 @@ class WarehouseShippingNoteController extends TTCrud {
|
|||||||
|
|
||||||
self::returnJson($finalResponse);
|
self::returnJson($finalResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function warehouseLogoutAction() {
|
||||||
|
if (isset($_SESSION[MFAPPNAME . '_warehouse_login_override'])) {
|
||||||
|
unset($_SESSION[MFAPPNAME . '_warehouse_login_override']);
|
||||||
|
self::returnJson(['success' => true, 'message' => 'Logout erfolgreich']);
|
||||||
|
} else {
|
||||||
|
self::returnJson(['success' => false, 'message' => 'Kein aktiver Login gefunden']);
|
||||||
|
}
|
||||||
|
}
|
||||||
protected function createNewLogAction() {
|
protected function createNewLogAction() {
|
||||||
$postData = json_decode(file_get_contents('php://input'), true);
|
$postData = json_decode(file_get_contents('php://input'), true);
|
||||||
|
|
||||||
|
|||||||
@@ -405,6 +405,13 @@ Vue.component('warehouse-shipping-note', {
|
|||||||
//language=Vue
|
//language=Vue
|
||||||
template: `
|
template: `
|
||||||
<tt-card>
|
<tt-card>
|
||||||
|
<tt-fullscreen-viewer
|
||||||
|
v-if="viewerItem"
|
||||||
|
:item="viewerItem"
|
||||||
|
:url="viewerUrl"
|
||||||
|
@close="viewerItem = null; viewerUrl = null"
|
||||||
|
/>
|
||||||
|
|
||||||
<warehouse-shipping-note-see-through
|
<warehouse-shipping-note-see-through
|
||||||
@close="shippingNoteSeeThrough = false;$refs.table.$refs.table.refreshTable()"
|
@close="shippingNoteSeeThrough = false;$refs.table.$refs.table.refreshTable()"
|
||||||
v-if="shippingNoteSeeThrough !== false"
|
v-if="shippingNoteSeeThrough !== false"
|
||||||
@@ -469,7 +476,7 @@ Vue.component('warehouse-shipping-note', {
|
|||||||
|
|
||||||
<tt-table-crud emit-edit
|
<tt-table-crud emit-edit
|
||||||
@openHistory="historyModal = true; historyModalId = $event.id"
|
@openHistory="historyModal = true; historyModalId = $event.id"
|
||||||
@print="window.open(window.TT_CONFIG['BASE_PATH'] + '/WarehouseShippingNote/createPDF?id=' + $event.id)"
|
@print="showPrintPreview($event)"
|
||||||
@status_to_progress="changeStatus($event.id, 'in_progress')"
|
@status_to_progress="changeStatus($event.id, 'in_progress')"
|
||||||
@status_to_accepted="changeStatus($event.id, 'accepted')"
|
@status_to_accepted="changeStatus($event.id, 'accepted')"
|
||||||
@status_to_invoiced="changeStatus($event.id, 'invoiced')"
|
@status_to_invoiced="changeStatus($event.id, 'invoiced')"
|
||||||
@@ -497,6 +504,8 @@ Vue.component('warehouse-shipping-note', {
|
|||||||
shippingNoteModalId: null,
|
shippingNoteModalId: null,
|
||||||
signingShippingNoteId: null,
|
signingShippingNoteId: null,
|
||||||
addLogModalId: null,
|
addLogModalId: null,
|
||||||
|
viewerItem: null,
|
||||||
|
viewerUrl: null,
|
||||||
shippingNoteSeeThrough: false,
|
shippingNoteSeeThrough: false,
|
||||||
articleModalId: null,
|
articleModalId: null,
|
||||||
calendarEvents: [],
|
calendarEvents: [],
|
||||||
@@ -523,6 +532,10 @@ Vue.component('warehouse-shipping-note', {
|
|||||||
window.notify('error', 'Kalendereinträge konnten nicht geladen werden.');
|
window.notify('error', 'Kalendereinträge konnten nicht geladen werden.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
showPrintPreview(row) {
|
||||||
|
this.viewerUrl = `${window.TT_CONFIG['BASE_PATH']}/WarehouseShippingNote/createPDF?id=${row.id}`;
|
||||||
|
this.viewerItem = { mimetype: 'application/pdf' };
|
||||||
|
},
|
||||||
formatEventDate(dateString) {
|
formatEventDate(dateString) {
|
||||||
return window.moment(dateString).format('DD.MM.YYYY HH:mm');
|
return window.moment(dateString).format('DD.MM.YYYY HH:mm');
|
||||||
},
|
},
|
||||||
@@ -582,3 +595,45 @@ document.addEventListener('visibilitychange', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
if (!window.matchMedia('(display-mode: standalone)').matches) return;
|
||||||
|
|
||||||
|
const logoutButton = document.createElement('button');
|
||||||
|
logoutButton.innerHTML = '<i class="fas fa-sign-out-alt"></i> Logout';
|
||||||
|
|
||||||
|
Object.assign(logoutButton.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '10px',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translateX(-50%)',
|
||||||
|
zIndex: '10000',
|
||||||
|
padding: '8px 16px',
|
||||||
|
backgroundColor: '#f44336',
|
||||||
|
color: 'white',
|
||||||
|
border: 'none',
|
||||||
|
borderRadius: '4px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: '14px',
|
||||||
|
boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLogout = async () => {
|
||||||
|
logoutButton.disabled = true;
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get('/WarehouseShippingNote/warehouseLogoutAction');
|
||||||
|
const type = data.success ? 'success' : 'error';
|
||||||
|
const message = data.message || (data.success ? 'Erfolgreich ausgeloggt' : 'Logout fehlgeschlagen');
|
||||||
|
window.notify(type, message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Logout request failed:', error);
|
||||||
|
window.notify('error', 'Ein Netzwerkfehler ist aufgetreten.');
|
||||||
|
} finally {
|
||||||
|
setTimeout(() => window.location.reload(), 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
logoutButton.addEventListener('click', handleLogout);
|
||||||
|
document.body.appendChild(logoutButton);
|
||||||
|
});
|
||||||
@@ -209,3 +209,27 @@
|
|||||||
.tt-fullscreen-nav-btn.right {
|
.tt-fullscreen-nav-btn.right {
|
||||||
right: 15px;
|
right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Center the loader on the screen */
|
||||||
|
.tt-fullscreen-loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply the animation to the SVG inside the loader */
|
||||||
|
.tt-fullscreen-loader svg {
|
||||||
|
animation: svg-spinner 1.2s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Define the keyframes for the spinning animation */
|
||||||
|
@keyframes svg-spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,102 +1,89 @@
|
|||||||
// tt-file-gallery.js
|
Vue.component('tt-fullscreen-viewer', {
|
||||||
Vue.component('tt-file-gallery', {
|
|
||||||
props: {
|
props: {
|
||||||
files: { type: Array, default: () => [] },
|
item: {type: Object, required: true},
|
||||||
editMode: { type: Boolean, default: false },
|
url: {type: String, default: null},
|
||||||
deleteMode: { type: Boolean, default: false },
|
items: {type: Array, default: () => []},
|
||||||
selectable: { type: Boolean, default: false },
|
initialIndex: {type: Number, default: -1},
|
||||||
},
|
},
|
||||||
data() {
|
data: () => ({
|
||||||
return {
|
currentItem: null,
|
||||||
fullscreenItem: null,
|
currentImageIndex: -1,
|
||||||
currentImageIndex: 0,
|
|
||||||
editingFile: null,
|
|
||||||
selectedFiles: [],
|
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
pan: {x: 0, y: 0},
|
pan: {x: 0, y: 0},
|
||||||
isPanning: false,
|
isPanning: false,
|
||||||
panStart: {x: 0, y: 0},
|
panStart: {x: 0, y: 0},
|
||||||
lastPinchDist: 0,
|
lastPinchDist: 0,
|
||||||
}
|
// NEW: State to track if the content is loading
|
||||||
},
|
isLoading: true,
|
||||||
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
imageFiles() {
|
contentSrc() {
|
||||||
return this.files.filter(this.isImage);
|
if (this.url) {
|
||||||
|
return this.url;
|
||||||
|
}
|
||||||
|
if (this.currentItem?.fileId) {
|
||||||
|
return `/File/show?id=${this.currentItem.fileId}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
isViewingImage() {
|
isViewingImage() {
|
||||||
return this.fullscreenItem && this.isImage(this.fullscreenItem);
|
return this.currentItem && this.isImage(this.currentItem);
|
||||||
},
|
},
|
||||||
imageTransformStyle() {
|
imageTransformStyle() {
|
||||||
const { x, y } = this.pan;
|
|
||||||
return {
|
return {
|
||||||
transform: `translate(${x}px, ${y}px) scale(${this.zoom})`,
|
transform: `translate(${this.pan.x}px, ${this.pan.y}px) scale(${this.zoom})`,
|
||||||
cursor: this.isPanning ? 'grabbing' : 'grab',
|
cursor: this.isPanning ? 'grabbing' : 'grab',
|
||||||
transition: this.isPanning ? 'none' : 'transform 0.2s',
|
transition: this.isPanning ? 'none' : 'transform 0.2s',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
fullscreenDownloadUrl() {
|
downloadUrl() {
|
||||||
if (!this.fullscreenItem) return '#';
|
return this.currentItem?.fileId ? `/File/download?id=${this.currentItem.fileId}` : '#';
|
||||||
return `/File/download?id=${this.fullscreenItem.fileId}`;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isImage: file => file.mimetype && file.mimetype.startsWith('image/'),
|
isImage: file => file?.mimetype?.startsWith('image/'),
|
||||||
isPdf: file => file.mimetype === 'application/pdf',
|
isPdf: file => file?.mimetype === 'application/pdf',
|
||||||
getFileIcon(file) {
|
onContentLoad() {
|
||||||
const extension = file.fileName?.split('.').pop().toLowerCase();
|
this.isLoading = false;
|
||||||
switch (extension) {
|
|
||||||
case 'doc': case 'docx': return 'fas fa-file-word text-primary';
|
|
||||||
case 'xls': case 'xlsx': return 'fas fa-file-excel text-success';
|
|
||||||
case 'zip': case 'rar': return 'fas fa-file-archive text-warning';
|
|
||||||
default: return 'fas fa-file text-secondary';
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
toggleSelection(fileId) {
|
closeViewer() {
|
||||||
if (!this.selectable) return;
|
this.$emit('close');
|
||||||
const index = this.selectedFiles.indexOf(fileId);
|
|
||||||
if (index > -1) {
|
|
||||||
this.selectedFiles.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
this.selectedFiles.push(fileId);
|
|
||||||
}
|
|
||||||
this.$emit('selection-changed', this.selectedFiles);
|
|
||||||
},
|
},
|
||||||
openViewer(file) {
|
|
||||||
if(this.editingFile) return;
|
|
||||||
this.fullscreenItem = file;
|
|
||||||
if (this.isImage(file)) {
|
|
||||||
this.currentImageIndex = this.imageFiles.findIndex(img => img.id === file.id);
|
|
||||||
}
|
|
||||||
this.resetZoomAndPan();
|
|
||||||
this.$nextTick(() => { this.$refs.viewer?.focus(); });
|
|
||||||
},
|
|
||||||
closeViewer() { this.fullscreenItem = null; },
|
|
||||||
navigateImage(direction) {
|
navigateImage(direction) {
|
||||||
const newIndex = this.currentImageIndex + direction;
|
const newIndex = this.currentImageIndex + direction;
|
||||||
if (newIndex >= 0 && newIndex < this.imageFiles.length) {
|
if (newIndex >= 0 && newIndex < this.items.length) {
|
||||||
|
// NEW: Reset loading state before loading the new image
|
||||||
|
this.isLoading = true;
|
||||||
this.currentImageIndex = newIndex;
|
this.currentImageIndex = newIndex;
|
||||||
this.fullscreenItem = this.imageFiles[newIndex];
|
this.currentItem = this.items[newIndex];
|
||||||
this.resetZoomAndPan();
|
this.resetZoomAndPan();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleKeyDown(event) {
|
handleKeyDown(e) {
|
||||||
event.stopPropagation();
|
e.stopPropagation();
|
||||||
if (!this.fullscreenItem) return;
|
if (!this.currentItem) return;
|
||||||
switch (event.key) {
|
switch (e.key) {
|
||||||
case 'Escape': this.closeViewer(); break;
|
case 'Escape':
|
||||||
case 'ArrowLeft': this.isViewingImage && this.navigateImage(-1); break;
|
this.closeViewer();
|
||||||
case 'ArrowRight': this.isViewingImage && this.navigateImage(1); break;
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
this.isViewingImage && this.navigateImage(-1);
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
this.isViewingImage && this.navigateImage(1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resetZoomAndPan() {
|
resetZoomAndPan() {
|
||||||
this.zoom = 1; this.pan = { x: 0, y: 0 }; this.isPanning = false;
|
this.zoom = 1;
|
||||||
|
this.pan = {x: 0, y: 0};
|
||||||
|
this.isPanning = false;
|
||||||
},
|
},
|
||||||
handleWheel(e) {
|
handleWheel(e) {
|
||||||
if (!this.isViewingImage) return;
|
if (!this.isViewingImage) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const scaleFactor = 0.2;
|
const scaleFactor = e.deltaY > 0 ? -0.2 : 0.2;
|
||||||
const newZoom = this.zoom - (e.deltaY > 0 ? scaleFactor : -scaleFactor);
|
this.zoom = Math.max(1, Math.min(this.zoom + scaleFactor, 5));
|
||||||
this.zoom = Math.max(1, Math.min(newZoom, 5));
|
|
||||||
},
|
},
|
||||||
onPanStart(e) {
|
onPanStart(e) {
|
||||||
if (this.zoom <= 1) return;
|
if (this.zoom <= 1) return;
|
||||||
@@ -110,16 +97,134 @@ Vue.component('tt-file-gallery', {
|
|||||||
this.pan.x = e.clientX - this.panStart.x;
|
this.pan.x = e.clientX - this.panStart.x;
|
||||||
this.pan.y = e.clientY - this.panStart.y;
|
this.pan.y = e.clientY - this.panStart.y;
|
||||||
},
|
},
|
||||||
onPanEnd() { this.isPanning = false; },
|
onPanEnd() {
|
||||||
onTouchStart(e) { /* ... touch logic ... */ },
|
this.isPanning = false;
|
||||||
onTouchMove(e) { /* ... touch logic ... */ },
|
},
|
||||||
onTouchEnd(e) { /* ... touch logic ... */ },
|
onTouchStart(e) {},
|
||||||
|
onTouchMove(e) {},
|
||||||
|
onTouchEnd(e) {},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.currentItem = this.item;
|
||||||
|
this.currentImageIndex = this.initialIndex;
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
this.$nextTick(() => this.$refs.viewer?.focus());
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
},
|
||||||
|
//language=Vue
|
||||||
|
template: `
|
||||||
|
<div class="tt-fullscreen-overlay" @click.self="closeViewer" @keydown="handleKeyDown" tabindex="-1" ref="viewer">
|
||||||
|
<div class="tt-fullscreen-toolbar">
|
||||||
|
<a v-if="isViewingImage && currentItem.fileId" :href="downloadUrl" download class="tt-fullscreen-btn"
|
||||||
|
title="Download">
|
||||||
|
<i class="fas fa-download"></i>
|
||||||
|
</a>
|
||||||
|
<button class="tt-fullscreen-btn" @click="closeViewer" title="Close"><i class="fas fa-times"></i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tt-fullscreen-content" @click.self="closeViewer">
|
||||||
|
<div v-if="isLoading" class="tt-fullscreen-loader">
|
||||||
|
<svg version="1.1" id="L9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve"
|
||||||
|
style="width: 80px; height: 80px;">
|
||||||
|
<path fill="#fff" d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<template v-if="isViewingImage">
|
||||||
|
<div class="tt-fullscreen-image-wrapper" @wheel="handleWheel" @mousedown="onPanStart" @mousemove="onPanMove"
|
||||||
|
@mouseup="onPanEnd" @mouseleave="onPanEnd">
|
||||||
|
<img v-show="!isLoading" :src="contentSrc" class="tt-fullscreen-image" :style="imageTransformStyle"
|
||||||
|
@load="onContentLoad" @error="onContentLoad" @click.stop/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<iframe v-else-if="isPdf(currentItem)" v-show="!isLoading" :src="contentSrc" class="tt-fullscreen-pdf"
|
||||||
|
@load="onContentLoad" @error="onContentLoad" @click.stop></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="isViewingImage && items.length > 1">
|
||||||
|
<button class="tt-fullscreen-nav-btn left" @click.stop="navigateImage(-1)" v-if="currentImageIndex > 0">
|
||||||
|
<i class="fas fa-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
<button class="tt-fullscreen-nav-btn right" @click.stop="navigateImage(1)"
|
||||||
|
v-if="currentImageIndex < items.length - 1">
|
||||||
|
<i class="fas fa-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</div>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
// You can add this style to your main CSS file or a <style> tag in the component file
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
Vue.component('tt-file-gallery', {
|
||||||
|
props: {
|
||||||
|
files: {type: Array, default: () => []},
|
||||||
|
editMode: {type: Boolean, default: false},
|
||||||
|
deleteMode: {type: Boolean, default: false},
|
||||||
|
selectable: {type: Boolean, default: false},
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
fullscreenItem: null,
|
||||||
|
editingFile: null,
|
||||||
|
selectedFiles: [],
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
imageFiles() {
|
||||||
|
return this.files.filter(this.isImage);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isImage: file => file.mimetype?.startsWith('image/'),
|
||||||
|
isPdf: file => file.mimetype === 'application/pdf',
|
||||||
|
|
||||||
|
getFileIcon(file) {
|
||||||
|
const extension = file.fileName?.split('.').pop().toLowerCase();
|
||||||
|
switch (extension) {
|
||||||
|
case 'doc':
|
||||||
|
case 'docx':
|
||||||
|
return 'fas fa-file-word text-primary';
|
||||||
|
case 'xls':
|
||||||
|
case 'xlsx':
|
||||||
|
return 'fas fa-file-excel text-success';
|
||||||
|
case 'zip':
|
||||||
|
case 'rar':
|
||||||
|
return 'fas fa-file-archive text-warning';
|
||||||
|
default:
|
||||||
|
return 'fas fa-file text-secondary';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleSelection(fileId) {
|
||||||
|
if (!this.selectable) return;
|
||||||
|
const index = this.selectedFiles.indexOf(fileId);
|
||||||
|
if (index > -1) this.selectedFiles.splice(index, 1);
|
||||||
|
else this.selectedFiles.push(fileId);
|
||||||
|
this.$emit('selection-changed', this.selectedFiles);
|
||||||
|
},
|
||||||
|
|
||||||
|
openViewer(file) {
|
||||||
|
if (this.editingFile) return;
|
||||||
|
this.fullscreenItem = file;
|
||||||
|
},
|
||||||
|
closeViewer() {
|
||||||
|
this.fullscreenItem = null;
|
||||||
|
},
|
||||||
|
|
||||||
startEdit(file, event) {
|
startEdit(file, event) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
this.editingFile = { ...file }; // create a copy for editing
|
this.editingFile = {...file};
|
||||||
},
|
},
|
||||||
cancelEdit(event) {
|
cancelEdit() {
|
||||||
this.editingFile = null;
|
this.editingFile = null;
|
||||||
},
|
},
|
||||||
saveEdit(event) {
|
saveEdit(event) {
|
||||||
@@ -134,77 +239,46 @@ Vue.component('tt-file-gallery', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
fullscreenItem(newItem) {
|
|
||||||
document.body.style.overflow = newItem ? 'hidden' : '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template: `
|
template: `
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header"><i class="fas fa-images mr-2"></i><h5>Hochgeladene Dokumente</h5></div>
|
<div class="card-header"><i class="fas fa-images mr-2"></i><h5>Hochgeladene Dokumente</h5></div>
|
||||||
<div v-if="!files.length" class="card-body text-center text-muted">Keine Dokumente vorhanden.</div>
|
<div v-if="!files.length" class="card-body text-center text-muted">Keine Dokumente vorhanden.</div>
|
||||||
<div v-else class="card-body">
|
<div v-else class="card-body">
|
||||||
<div class="tt-file-gallery-grid">
|
<div class="tt-file-gallery-grid">
|
||||||
<div v-for="file in files" :key="file.id"
|
<div v-for="file in files" :key="file.id" class="tt-file-gallery-item"
|
||||||
class="tt-file-gallery-item"
|
|
||||||
:class="[{ 'selected': selectable && selectedFiles.includes(file.id) }, file.class]"
|
:class="[{ 'selected': selectable && selectedFiles.includes(file.id) }, file.class]"
|
||||||
@click="openViewer(file)">
|
@click="openViewer(file)">
|
||||||
|
|
||||||
<div v-if="selectable" class="selection-indicator" @click.stop="toggleSelection(file.id)">
|
<div v-if="selectable" class="selection-indicator" @click.stop="toggleSelection(file.id)">
|
||||||
<i :class="selectedFiles.includes(file.id) ? 'fas fa-check-circle text-primary' : 'far fa-circle text-muted'"></i>
|
<i :class="selectedFiles.includes(file.id) ? 'fas fa-check-circle text-primary' : 'far fa-circle text-muted'"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tt-file-gallery-thumbnail-wrapper">
|
<div class="tt-file-gallery-thumbnail-wrapper">
|
||||||
<template v-if="isImage(file)">
|
<img v-if="isImage(file)" :src="'/File/show?id=' + file.fileId + '&size=small'"
|
||||||
<img :src="'/File/show?id=' + file.fileId + '&size=small'" class="tt-file-gallery-thumbnail" :alt="file.fileName">
|
class="tt-file-gallery-thumbnail" :alt="file.fileName">
|
||||||
</template>
|
<div v-else-if="isPdf(file)" class="tt-file-gallery-icon-container"><i
|
||||||
<template v-else-if="isPdf(file)">
|
class="fas fa-file-pdf fa-3x text-danger"></i></div>
|
||||||
<div class="tt-file-gallery-icon-container"><i class="fas fa-file-pdf fa-3x text-danger"></i></div>
|
<a v-else :href="'/File/download?id=' + file.fileId" target="_blank" @click.stop
|
||||||
</template>
|
class="tt-file-gallery-icon-container">
|
||||||
<template v-else>
|
|
||||||
<a :href="'/File/download?id=' + file.fileId" target="_blank" @click.stop class="tt-file-gallery-icon-container">
|
|
||||||
<i :class="getFileIcon(file)" class="fa-3x"></i>
|
<i :class="getFileIcon(file)" class="fa-3x"></i>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
<div class="tt-file-gallery-overlay" @click.stop="openViewer(file)"><i class="fas fa-search-plus"></i>
|
||||||
<div class="tt-file-gallery-overlay" @click.stop="openViewer(file)"><i class="fas fa-search-plus"></i></div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="tt-file-gallery-filename" :title="file.fileName">
|
<div class="tt-file-gallery-filename" :title="file.fileName">
|
||||||
<span>{{ file.fileName }}</span>
|
<span>{{ file.fileName }}</span>
|
||||||
<i v-if="editMode" class="fas fa-edit text-primary ml-1 action-icon" @click="startEdit(file, $event)"></i>
|
<i v-if="editMode" class="fas fa-edit text-primary ml-1 action-icon"
|
||||||
<i v-if="deleteMode" class="fas fa-trash text-danger ml-1 action-icon" @click="deleteFile(file, $event)"></i>
|
@click="startEdit(file, $event)"></i>
|
||||||
|
<i v-if="deleteMode" class="fas fa-trash text-danger ml-1 action-icon"
|
||||||
|
@click="deleteFile(file, $event)"></i>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<tt-modal v-if="editingFile" :show="true" title="Dokument bearbeiten" @update:show="cancelEdit"
|
||||||
<tt-modal v-if="editingFile" :show="true" title="Dokument bearbeiten" @update:show="cancelEdit" @submit="saveEdit" :delete="false" :disable-min-height="true">
|
@submit="saveEdit" :delete="false" :disable-min-height="true">
|
||||||
<slot name="file-edit" :file="editingFile"></slot>
|
<slot name="file-edit" :file="editingFile"></slot>
|
||||||
</tt-modal>
|
</tt-modal>
|
||||||
|
<tt-fullscreen-viewer v-if="fullscreenItem" :item="fullscreenItem" :items="imageFiles"
|
||||||
<div v-if="fullscreenItem" class="tt-fullscreen-overlay" @click.self="closeViewer" @keydown="handleKeyDown" tabindex="-1" ref="viewer">
|
:initial-index="imageFiles.findIndex(f => f.id === fullscreenItem.id)"
|
||||||
<div class="tt-fullscreen-toolbar">
|
@close="closeViewer"/>
|
||||||
<a v-if="isViewingImage" :href="fullscreenDownloadUrl" download class="tt-fullscreen-btn" title="Download">
|
</div>`
|
||||||
<i class="fas fa-download"></i>
|
|
||||||
</a>
|
|
||||||
<button class="tt-fullscreen-btn" @click="closeViewer" title="Close"><i class="fas fa-times"></i></button>
|
|
||||||
</div>
|
|
||||||
<div class="tt-fullscreen-content" @click.self="closeViewer">
|
|
||||||
<template v-if="isViewingImage">
|
|
||||||
<div class="tt-fullscreen-image-wrapper" @wheel="handleWheel" @mousedown="onPanStart" @mousemove="onPanMove" @mouseup="onPanEnd" @mouseleave="onPanEnd">
|
|
||||||
<img :src="'/File/show?id=' + fullscreenItem.fileId" class="tt-fullscreen-image" :style="imageTransformStyle" @click.stop />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="isPdf(fullscreenItem)">
|
|
||||||
<iframe :src="'/File/show?id=' + fullscreenItem.fileId" class="tt-fullscreen-pdf" @click.stop></iframe>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<template v-if="isViewingImage">
|
|
||||||
<button class="tt-fullscreen-nav-btn left" @click.stop="navigateImage(-1)" v-if="currentImageIndex > 0"><i class="fas fa-chevron-left"></i></button>
|
|
||||||
<button class="tt-fullscreen-nav-btn right" @click.stop="navigateImage(1)" v-if="currentImageIndex < imageFiles.length - 1"><i class="fas fa-chevron-right"></i></button>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user