153 lines
5.0 KiB
Vue
153 lines
5.0 KiB
Vue
<template>
|
|
<teleport to="body">
|
|
<div
|
|
v-if="visible"
|
|
class="fixed inset-0 z-[1200] flex items-center justify-center bg-black/60 backdrop-blur-sm px-4 py-6"
|
|
@click.self="close"
|
|
>
|
|
<div class="w-full max-w-[1600px] h-full max-h-[94vh] bg-base-100 rounded-2xl shadow-2xl flex flex-col overflow-hidden">
|
|
<header class="flex items-start justify-between gap-4 p-6 border-b border-base-200">
|
|
<div class="min-w-0">
|
|
<h3 class="font-bold text-xl truncate">Prévisualisation</h3>
|
|
<p class="text-sm text-gray-500 truncate">
|
|
{{ document?.name || document?.filename }}<span v-if="documentDescription"> • {{ documentDescription }}</span>
|
|
</p>
|
|
</div>
|
|
<button type="button" class="btn btn-ghost btn-sm shrink-0" @click="close">
|
|
✕
|
|
</button>
|
|
</header>
|
|
|
|
<section class="flex-1 bg-base-200/40 px-6 py-5 overflow-hidden">
|
|
<div class="h-full w-full rounded-xl border border-base-300 bg-base-100 flex items-center justify-center overflow-hidden">
|
|
<template v-if="previewType === 'image'">
|
|
<img :src="document?.path" alt="preview" class="max-h-full max-w-full object-contain" />
|
|
</template>
|
|
|
|
<template v-else-if="previewType === 'pdf'">
|
|
<iframe
|
|
:src="document?.path"
|
|
class="w-full h-full bg-white"
|
|
frameborder="0"
|
|
title="Aperçu PDF"
|
|
></iframe>
|
|
</template>
|
|
|
|
<template v-else-if="previewType === 'audio'">
|
|
<audio :src="document?.path" controls class="w-full"></audio>
|
|
</template>
|
|
|
|
<template v-else-if="previewType === 'video'">
|
|
<video :src="document?.path" controls class="w-full h-full bg-black"></video>
|
|
</template>
|
|
|
|
<template v-else-if="previewType === 'text'">
|
|
<div class="w-full h-full overflow-auto">
|
|
<div v-if="textLoading" class="flex items-center justify-center py-10 text-sm text-gray-500">
|
|
<span class="loading loading-spinner loading-md mr-2"></span>
|
|
Chargement du document...
|
|
</div>
|
|
<div v-else-if="textError" class="alert alert-error text-sm">
|
|
{{ textError }}
|
|
</div>
|
|
<pre v-else class="bg-base-100 border border-base-300 rounded-lg p-4 whitespace-pre-wrap">
|
|
{{ textContent }}
|
|
</pre>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<div class="text-sm text-gray-500 text-center px-6">
|
|
Prévisualisation non disponible pour ce type de document.
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</section>
|
|
|
|
<footer class="border-t border-base-200 px-6 py-4 flex flex-wrap gap-2 justify-end bg-base-100">
|
|
<button type="button" class="btn" @click="close">Fermer</button>
|
|
<button type="button" class="btn btn-primary" @click="download">
|
|
Télécharger
|
|
</button>
|
|
</footer>
|
|
</div>
|
|
</div>
|
|
</teleport>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watch } from 'vue'
|
|
import { getPreviewType, describeDocument } from '~/utils/documentPreview'
|
|
|
|
const props = defineProps({
|
|
document: {
|
|
type: Object,
|
|
default: null,
|
|
},
|
|
visible: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits(['close'])
|
|
|
|
const previewType = computed(() => getPreviewType(props.document))
|
|
const documentDescription = computed(() => describeDocument(props.document))
|
|
|
|
const textContent = ref('')
|
|
const textLoading = ref(false)
|
|
const textError = ref('')
|
|
|
|
watch(
|
|
() => props.document,
|
|
async (doc) => {
|
|
textContent.value = ''
|
|
textError.value = ''
|
|
textLoading.value = false
|
|
|
|
if (!doc) return
|
|
if (getPreviewType(doc) !== 'text') return
|
|
|
|
try {
|
|
textLoading.value = true
|
|
const path = doc.path || ''
|
|
if (path.startsWith('data:')) {
|
|
const base64Part = path.split(',')[1] || ''
|
|
if (!base64Part) {
|
|
textError.value = 'Impossible de lire ce document texte.'
|
|
return
|
|
}
|
|
const decoded = atob(base64Part)
|
|
textContent.value = decodeURIComponent(escape(decoded))
|
|
} else {
|
|
const response = await fetch(path)
|
|
if (!response.ok) {
|
|
throw new Error('Téléchargement du document impossible')
|
|
}
|
|
textContent.value = await response.text()
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement du texte:', error)
|
|
textError.value = error.message || 'Impossible de lire ce document.'
|
|
} finally {
|
|
textLoading.value = false
|
|
}
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
const close = () => {
|
|
emit('close')
|
|
}
|
|
|
|
const download = () => {
|
|
if (!props.document?.path) return
|
|
const link = document.createElement('a')
|
|
link.href = props.document.path
|
|
link.download = props.document.filename || props.document.name || 'document'
|
|
link.target = '_blank'
|
|
link.click()
|
|
}
|
|
</script>
|