generated from corrad-software/corrad-af-2024
265 lines
14 KiB
Vue
265 lines
14 KiB
Vue
<script setup>
|
|
import { ref, computed, onMounted } from 'vue';
|
|
|
|
const props = defineProps({
|
|
document: {
|
|
type: Object,
|
|
required: true
|
|
},
|
|
viewMode: {
|
|
type: String,
|
|
default: 'preview' // preview, fullscreen
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['close', 'download', 'print', 'requestAccess']);
|
|
|
|
// State
|
|
const isLoading = ref(true);
|
|
const zoomLevel = ref(100); // percentage
|
|
const currentPage = ref(1);
|
|
const totalPages = ref(1);
|
|
const viewerReady = ref(false);
|
|
|
|
// Computed properties
|
|
const documentType = computed(() => {
|
|
const extension = props.document.extension?.toLowerCase();
|
|
|
|
if (['pdf'].includes(extension)) return 'pdf';
|
|
if (['doc', 'docx'].includes(extension)) return 'word';
|
|
if (['xls', 'xlsx'].includes(extension)) return 'excel';
|
|
if (['ppt', 'pptx'].includes(extension)) return 'powerpoint';
|
|
if (['jpg', 'jpeg', 'png', 'gif', 'webp'].includes(extension)) return 'image';
|
|
if (['txt', 'md', 'json', 'csv'].includes(extension)) return 'text';
|
|
|
|
return 'generic';
|
|
});
|
|
|
|
const documentUrl = computed(() => {
|
|
// In production, this would come from the document API
|
|
return props.document.url || `#`;
|
|
});
|
|
|
|
const documentTitle = computed(() => {
|
|
return props.document.name || 'Untitled Document';
|
|
});
|
|
|
|
// Get SVG icon based on document type
|
|
const getDocumentIcon = (type, size = 24) => {
|
|
let svg = '';
|
|
|
|
switch (type) {
|
|
case 'pdf':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><path d="M9 15h6"></path><path d="M9 11h6"></path></svg>`;
|
|
break;
|
|
case 'word':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>`;
|
|
break;
|
|
case 'excel':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><rect x="8" y="12" width="8" height="6"></rect><line x1="8" y1="16" x2="16" y2="16"></line><line x1="11" y1="12" x2="11" y2="18"></line></svg>`;
|
|
break;
|
|
case 'powerpoint':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><rect x="8" y="12" width="8" height="6"></rect></svg>`;
|
|
break;
|
|
case 'image':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>`;
|
|
break;
|
|
case 'text':
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>`;
|
|
break;
|
|
default:
|
|
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>`;
|
|
}
|
|
|
|
return svg;
|
|
};
|
|
|
|
// Methods
|
|
const closeViewer = () => {
|
|
emit('close');
|
|
};
|
|
|
|
const downloadDocument = () => {
|
|
emit('download', props.document);
|
|
};
|
|
|
|
const printDocument = () => {
|
|
emit('print', props.document);
|
|
};
|
|
|
|
const requestAccess = () => {
|
|
emit('requestAccess', props.document);
|
|
};
|
|
|
|
const zoomIn = () => {
|
|
zoomLevel.value += 10;
|
|
};
|
|
|
|
const zoomOut = () => {
|
|
if (zoomLevel.value > 50) {
|
|
zoomLevel.value -= 10;
|
|
}
|
|
};
|
|
|
|
const resetZoom = () => {
|
|
zoomLevel.value = 100;
|
|
};
|
|
|
|
const goToPage = (page) => {
|
|
if (page >= 1 && page <= totalPages.value) {
|
|
currentPage.value = page;
|
|
}
|
|
};
|
|
|
|
const nextPage = () => {
|
|
if (currentPage.value < totalPages.value) {
|
|
currentPage.value++;
|
|
}
|
|
};
|
|
|
|
const prevPage = () => {
|
|
if (currentPage.value > 1) {
|
|
currentPage.value--;
|
|
}
|
|
};
|
|
|
|
// Mock viewer loading for demonstration
|
|
onMounted(() => {
|
|
setTimeout(() => {
|
|
isLoading.value = false;
|
|
viewerReady.value = true;
|
|
totalPages.value = 5; // Mock total pages
|
|
}, 1000);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="dms-document-viewer bg-gray-900 text-white flex flex-col"
|
|
:class="viewMode === 'fullscreen' ? 'fixed inset-0 z-50' : 'h-full'"
|
|
>
|
|
<!-- Toolbar -->
|
|
<div class="viewer-toolbar p-2 flex items-center justify-between bg-gray-800">
|
|
<div class="flex items-center gap-3">
|
|
<button @click="closeViewer" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
|
</button>
|
|
<h3 class="font-medium truncate max-w-md">{{ documentTitle }}</h3>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<!-- Page navigation (for multi-page documents) -->
|
|
<div v-if="['pdf', 'word', 'powerpoint'].includes(documentType)" class="flex items-center gap-1">
|
|
<button @click="prevPage" class="p-2 rounded hover:bg-gray-700" :disabled="currentPage <= 1">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
|
|
</button>
|
|
<span class="text-sm">
|
|
{{ currentPage }} / {{ totalPages }}
|
|
</span>
|
|
<button @click="nextPage" class="p-2 rounded hover:bg-gray-700" :disabled="currentPage >= totalPages">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Zoom controls -->
|
|
<div class="flex items-center gap-1">
|
|
<button @click="zoomOut" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>
|
|
</button>
|
|
<span class="text-sm">{{ zoomLevel }}%</span>
|
|
<button @click="zoomIn" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>
|
|
</button>
|
|
<button @click="resetZoom" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"></path><path d="M9 21H3v-6"></path><path d="M21 3l-7 7"></path><path d="M3 21l7-7"></path></svg>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Actions -->
|
|
<div class="flex items-center gap-1">
|
|
<button @click="downloadDocument" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
|
|
</button>
|
|
<button @click="printDocument" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>
|
|
</button>
|
|
<button @click="requestAccess" class="p-2 rounded hover:bg-gray-700">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Document content area -->
|
|
<div class="document-content flex-1 overflow-auto flex justify-center items-start p-4">
|
|
<!-- Loading state -->
|
|
<div v-if="isLoading" class="flex flex-col items-center justify-center h-full w-full">
|
|
<Loading />
|
|
<p class="mt-4 text-gray-400">Loading document...</p>
|
|
</div>
|
|
|
|
<!-- PDF Viewer -->
|
|
<div v-else-if="documentType === 'pdf'" class="document-display">
|
|
<div class="bg-white p-4 shadow-lg" :style="{ transform: `scale(${zoomLevel/100})` }">
|
|
<div class="w-[816px] h-[1056px] flex justify-center items-center border border-gray-300">
|
|
<!-- This would be replaced with an actual PDF viewer like vue-pdf-embed or similar -->
|
|
<div class="text-gray-800 text-center">
|
|
<span v-html="getDocumentIcon('pdf', 64)" class="mx-auto mb-4 block"></span>
|
|
<p class="font-medium">PDF Viewer</p>
|
|
<p class="text-sm text-gray-500">Page {{ currentPage }} of {{ totalPages }}</p>
|
|
<p class="text-sm text-gray-500 mt-2">{{ props.document.name }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Image Viewer -->
|
|
<div v-else-if="documentType === 'image'" class="document-display">
|
|
<div :style="{ transform: `scale(${zoomLevel/100})` }">
|
|
<img src="https://via.placeholder.com/800x600" alt="Document preview" class="max-w-full shadow-lg" />
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Office Document Viewer -->
|
|
<div v-else-if="['word', 'excel', 'powerpoint'].includes(documentType)" class="document-display">
|
|
<div class="bg-white p-4 shadow-lg" :style="{ transform: `scale(${zoomLevel/100})` }">
|
|
<div class="w-[816px] h-[1056px] flex justify-center items-center border border-gray-300">
|
|
<!-- This would be replaced with an actual Office viewer -->
|
|
<div class="text-gray-800 text-center">
|
|
<span v-html="getDocumentIcon(documentType, 64)" class="mx-auto mb-4 block"></span>
|
|
<p class="font-medium">{{ documentType.charAt(0).toUpperCase() + documentType.slice(1) }} Viewer</p>
|
|
<p class="text-sm text-gray-500 mt-2">{{ props.document.name }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Text Viewer -->
|
|
<div v-else-if="documentType === 'text'" class="document-display">
|
|
<div class="bg-white p-6 shadow-lg text-gray-800 w-[816px]" :style="{ transform: `scale(${zoomLevel/100})` }">
|
|
<!-- This would be replaced with actual text content -->
|
|
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor ligula nec nisl consectetur, nec tincidunt nisl tincidunt. Duis vitae urna euismod, volutpat nisl ac, malesuada nunc.</p>
|
|
<p>Sed vel lectus vel orci ultrices tincidunt. Nulla facilisi. Donec vitae nisi vel elit elementum tincidunt. Sed vel justo vel nisi volutpat tincidunt. Duis vitae urna euismod, volutpat nisl ac, malesuada nunc.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Generic/Unsupported File -->
|
|
<div v-else class="document-display flex flex-col items-center justify-center">
|
|
<span v-html="getDocumentIcon('generic', 96)" class="mb-4"></span>
|
|
<h3 class="text-lg font-medium mb-2">Preview not available</h3>
|
|
<p class="text-gray-400 mb-6">This file type cannot be previewed</p>
|
|
<rs-button @click="downloadDocument" color="primary">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
|
|
Download File
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.document-display {
|
|
transition: transform 0.2s ease;
|
|
transform-origin: center top;
|
|
}
|
|
</style> |