Compare commits
2 Commits
2c7cd17e2b
...
36969e272d
| Author | SHA1 | Date | |
|---|---|---|---|
| 36969e272d | |||
| dd063fd648 |
84
frontend/components/AuthenticatedFilePreview.client.vue
Normal file
84
frontend/components/AuthenticatedFilePreview.client.vue
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeUnmount, ref, watch } from "vue"
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
fileId: string
|
||||||
|
path?: string | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const { $api } = useNuxtApp()
|
||||||
|
const objectUrl = ref<string | null>(null)
|
||||||
|
const loadFailed = ref(false)
|
||||||
|
|
||||||
|
const isPdf = computed(() => String(props.path || "").toLowerCase().endsWith(".pdf"))
|
||||||
|
|
||||||
|
const revokeObjectUrl = () => {
|
||||||
|
if (!objectUrl.value) return
|
||||||
|
URL.revokeObjectURL(objectUrl.value)
|
||||||
|
objectUrl.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadFile = async () => {
|
||||||
|
revokeObjectUrl()
|
||||||
|
loadFailed.value = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const blob = await $api<Blob>(`/api/files/content/${encodeURIComponent(props.fileId)}`, {
|
||||||
|
responseType: "blob"
|
||||||
|
})
|
||||||
|
objectUrl.value = URL.createObjectURL(blob)
|
||||||
|
} catch (error) {
|
||||||
|
loadFailed.value = true
|
||||||
|
console.error("Dateivorschau konnte nicht geladen werden:", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.fileId, loadFile, { immediate: true })
|
||||||
|
onBeforeUnmount(revokeObjectUrl)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="authenticated-file-preview">
|
||||||
|
<iframe
|
||||||
|
v-if="objectUrl && isPdf"
|
||||||
|
:src="`${objectUrl}#toolbar=0&navpanes=0&scrollbar=0`"
|
||||||
|
title="PDF-Vorschau"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<img v-else-if="objectUrl" :src="objectUrl" alt="Dateivorschau" />
|
||||||
|
<div v-else-if="loadFailed" class="preview-placeholder">
|
||||||
|
<UIcon name="i-heroicons-document" class="h-10 w-10" />
|
||||||
|
<span>Keine Vorschau verfügbar</span>
|
||||||
|
</div>
|
||||||
|
<USkeleton v-else class="h-full min-h-32 w-full" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.authenticated-file-preview,
|
||||||
|
.authenticated-file-preview iframe,
|
||||||
|
.authenticated-file-preview img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authenticated-file-preview iframe {
|
||||||
|
border: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authenticated-file-preview img {
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-placeholder {
|
||||||
|
display: flex;
|
||||||
|
min-height: 8rem;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: rgb(107 114 128);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -38,16 +38,10 @@ const showFile = (file) => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :id="`docDisplay-${documentData.id}`" class="documentListItem" @click="returnEmit ? $emit('clicked', documentData.id) : showFile(documentData)">
|
<div :id="`docDisplay-${documentData.id}`" class="documentListItem" @click="returnEmit ? $emit('clicked', documentData.id) : showFile(documentData)">
|
||||||
<iframe
|
<AuthenticatedFilePreview
|
||||||
:src="`${documentData.url}#toolbar=0&navpanes=0&scrollbar=0`"
|
:file-id="documentData.id"
|
||||||
|
:path="documentData.path"
|
||||||
class="previewEmbed"
|
class="previewEmbed"
|
||||||
v-if="documentData.path.toLowerCase().includes('pdf')"
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
v-else
|
|
||||||
alt=""
|
|
||||||
:src="documentData.url"
|
|
||||||
/>
|
/>
|
||||||
<!-- TODO: Remove Scrollbar -->
|
<!-- TODO: Remove Scrollbar -->
|
||||||
<UTooltip class="w-full" :text="documentData.path.split('/')[documentData.path.split('/').length -1]">
|
<UTooltip class="w-full" :text="documentData.path.split('/')[documentData.path.split('/').length -1]">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const modal = useModal()
|
const modal = useModal()
|
||||||
|
const fileService = useFiles()
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
documentData: {
|
documentData: {
|
||||||
@@ -68,6 +69,15 @@ const archiveDocument = async () => {
|
|||||||
await updateDocument({archived: true}, true)
|
await updateDocument({archived: true}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openDocument = async () => {
|
||||||
|
try {
|
||||||
|
await fileService.openFile(props.documentData.id)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
toast.add({title: "Datei konnte nicht geöffnet werden", color: "error"})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getItemsBySelectedResource = async () => {
|
const getItemsBySelectedResource = async () => {
|
||||||
idToAssign.value = null
|
idToAssign.value = null
|
||||||
itemOptions.value = selectedResource.value?.entity
|
itemOptions.value = selectedResource.value?.entity
|
||||||
@@ -155,11 +165,11 @@ setup()
|
|||||||
v-if="props.documentData.id && String(props.documentData.path || '').toLowerCase().includes('pdf')"
|
v-if="props.documentData.id && String(props.documentData.path || '').toLowerCase().includes('pdf')"
|
||||||
:file-id="props.documentData.id" />
|
:file-id="props.documentData.id" />
|
||||||
|
|
||||||
<img
|
<AuthenticatedFilePreview
|
||||||
v-else
|
v-else
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:src="props.documentData.url"
|
:file-id="props.documentData.id"
|
||||||
alt=""
|
:path="props.documentData.path"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -173,10 +183,9 @@ setup()
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<UButton
|
<UButton
|
||||||
:to="props.documentData.url"
|
@click="openDocument"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
icon="i-heroicons-arrow-top-right-on-square"
|
icon="i-heroicons-arrow-top-right-on-square"
|
||||||
target="_blank"
|
|
||||||
>
|
>
|
||||||
Öffnen
|
Öffnen
|
||||||
</UButton>
|
</UButton>
|
||||||
|
|||||||
@@ -119,8 +119,9 @@ function isImage(file) {
|
|||||||
v-else-if="activeFile && isImage(activeFile)"
|
v-else-if="activeFile && isImage(activeFile)"
|
||||||
class="p-4 flex justify-center"
|
class="p-4 flex justify-center"
|
||||||
>
|
>
|
||||||
<img
|
<AuthenticatedFilePreview
|
||||||
:src="activeFile.url"
|
:file-id="activeFile.id"
|
||||||
|
:path="activeFile.path"
|
||||||
class="max-w-full max-h-[80vh] rounded-lg shadow"
|
class="max-w-full max-h-[80vh] rounded-lg shadow"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -146,6 +146,26 @@ export const useFiles = () => {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openFile = async (id: string) => {
|
||||||
|
const targetWindow = window.open("about:blank", "_blank")
|
||||||
|
|
||||||
|
try {
|
||||||
|
const blob = await downloadFile(id, undefined, true) as Blob
|
||||||
|
const objectUrl = URL.createObjectURL(blob)
|
||||||
|
|
||||||
|
if (targetWindow) {
|
||||||
|
targetWindow.location.replace(objectUrl)
|
||||||
|
} else {
|
||||||
|
window.open(objectUrl, "_blank")
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(() => URL.revokeObjectURL(objectUrl), 60_000)
|
||||||
|
} catch (error) {
|
||||||
|
targetWindow?.close()
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const dataURLtoFile = (dataurl:string, filename:string) => {
|
const dataURLtoFile = (dataurl:string, filename:string) => {
|
||||||
let arr = dataurl.split(","),
|
let arr = dataurl.split(","),
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@@ -160,5 +180,5 @@ export const useFiles = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return {uploadFiles, selectDocuments, selectSomeDocuments, selectDocument, downloadFile, dataURLtoFile}
|
return {uploadFiles, selectDocuments, selectSomeDocuments, selectDocument, downloadFile, openFile, dataURLtoFile}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,12 +154,21 @@ const filteredStatementAllocations = computed(() => {
|
|||||||
return statementAllocations.value.filter((allocation) => matchesSelectedPeriod(getStatementDate(allocation)))
|
return statementAllocations.value.filter((allocation) => matchesSelectedPeriod(getStatementDate(allocation)))
|
||||||
})
|
})
|
||||||
|
|
||||||
const filteredAccountStatementAllocations = computed(() => {
|
const filteredDirectAccountStatementAllocations = computed(() => {
|
||||||
return filteredStatementAllocations.value.filter((allocation) => {
|
return filteredStatementAllocations.value.filter((allocation) => {
|
||||||
if (allocation.account === null || allocation.account === undefined) {
|
if (allocation.account === null || allocation.account === undefined) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return !allocation?.incominginvoice
|
||||||
|
&& !allocation?.createddocument
|
||||||
|
&& !allocation?.ii_id
|
||||||
|
&& !allocation?.cd_id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const filteredAccountStatementAllocations = computed(() => {
|
||||||
|
return filteredDirectAccountStatementAllocations.value.filter((allocation) => {
|
||||||
return getStatementAllocationImmediateExpenseAmount(allocation) > 0
|
return getStatementAllocationImmediateExpenseAmount(allocation) > 0
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -298,8 +307,7 @@ const accountRows = computed(() => {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const directBookings = filteredAccountStatementAllocations.value
|
const directBookings = filteredDirectAccountStatementAllocations.value
|
||||||
.filter((allocation) => getStatementAllocationImmediateExpenseAmount(allocation) > 0)
|
|
||||||
.filter((allocation) => sameId(allocation.account?.id || allocation.account, account.id))
|
.filter((allocation) => sameId(allocation.account?.id || allocation.account, account.id))
|
||||||
.map((allocation) => {
|
.map((allocation) => {
|
||||||
const amount = Number(allocation.amount || 0)
|
const amount = Number(allocation.amount || 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user