85 lines
2.0 KiB
Vue
85 lines
2.0 KiB
Vue
<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>
|