|
|
@@ -0,0 +1,161 @@
|
|
|
+<template>
|
|
|
+ <div class="file-preview-container">
|
|
|
+ <!-- PDF预览 -->
|
|
|
+ <div v-if="isPDF" class="pdf-preview">
|
|
|
+ <iframe v-if="pdfUrl" :src="pdfUrl" class="preview-iframe" frameborder="0"></iframe>
|
|
|
+ <div v-else class="no-preview">无法加载PDF预览</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- Office文档预览 -->
|
|
|
+ <div v-else-if="isOffice" class="office-preview">
|
|
|
+ <iframe v-if="officeUrl" :src="officeUrl" class="preview-iframe" frameborder="0"></iframe>
|
|
|
+ <div v-else class="no-preview">无法加载文档预览</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 不支持的文件类型 -->
|
|
|
+ <div v-else class="unsupported-file">
|
|
|
+ 不支持预览此文件类型: {{ fileType }}
|
|
|
+ <a-button type="primary" @click="handleDownload">下载文件</a-button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 加载状态 -->
|
|
|
+ <div v-if="loading" class="loading-overlay">
|
|
|
+ <a-spin size="large" />
|
|
|
+ <div>加载预览中...</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+ import { computed, ref, onMounted } from 'vue'
|
|
|
+ import { message } from 'ant-design-vue'
|
|
|
+
|
|
|
+ const props = defineProps({
|
|
|
+ fileUrl: {
|
|
|
+ type: String,
|
|
|
+ required: true
|
|
|
+ },
|
|
|
+ fileName: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ fileType: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ // 文件预览服务基础URL
|
|
|
+ previewServiceUrl: {
|
|
|
+ type: String,
|
|
|
+ default: 'https://view.officeapps.live.com/op/embed.aspx'
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const loading = ref(false)
|
|
|
+ const officeUrl = ref('')
|
|
|
+
|
|
|
+ // // 获取文件类型
|
|
|
+ // const fileType = computed(() => {
|
|
|
+ // if (!props.fileName) return ''
|
|
|
+ // const ext = props.fileName.split('.').pop().toLowerCase()
|
|
|
+ // return ext
|
|
|
+ // })
|
|
|
+
|
|
|
+ // 判断文件类型
|
|
|
+ const isPDF = computed(() => props.fileType === 'pdf')
|
|
|
+ const isWord = computed(() => ['doc', 'docx'].includes(props.fileType))
|
|
|
+ const isExcel = computed(() => ['xls', 'xlsx'].includes(props.fileType))
|
|
|
+ const isPPT = computed(() => ['ppt', 'pptx'].includes(props.fileType))
|
|
|
+ const isOffice = computed(() => isWord.value || isExcel.value || isPPT.value)
|
|
|
+
|
|
|
+ // 处理文件URL
|
|
|
+ const fullFileUrl = computed(() => {
|
|
|
+ // 如果是相对路径,转换为绝对路径
|
|
|
+ if (!props.fileUrl.startsWith('http') && !props.fileUrl.startsWith('/')) {
|
|
|
+ return `${window.location.origin}/${props.fileUrl}`
|
|
|
+ }
|
|
|
+ return props.fileUrl
|
|
|
+ })
|
|
|
+
|
|
|
+ // PDF预览URL
|
|
|
+ const pdfUrl = computed(() => {
|
|
|
+ if (!isPDF.value) return null
|
|
|
+ // 使用Google Docs Viewer进行PDF预览
|
|
|
+ return `https://docs.google.com/viewer?url=${encodeURIComponent(fullFileUrl.value)}&embedded=true`
|
|
|
+ })
|
|
|
+
|
|
|
+ // 初始化Office预览
|
|
|
+ const initOfficePreview = () => {
|
|
|
+ if (!isOffice.value) return
|
|
|
+
|
|
|
+ // 使用微软Office Online Viewer预览Word/Excel/PPT
|
|
|
+ officeUrl.value = `${props.previewServiceUrl}?src=${encodeURIComponent(fullFileUrl.value)}`
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下载文件
|
|
|
+ const handleDownload = () => {
|
|
|
+ const link = document.createElement('a')
|
|
|
+ link.href = props.fileUrl
|
|
|
+ link.download = props.fileName || `download.${fileType.value}`
|
|
|
+ document.body.appendChild(link)
|
|
|
+ link.click()
|
|
|
+ document.body.removeChild(link)
|
|
|
+ message.success('开始下载文件')
|
|
|
+ }
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ loading.value = true
|
|
|
+ initOfficePreview()
|
|
|
+ loading.value = false
|
|
|
+ })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+ .file-preview-container {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 500px;
|
|
|
+ border: 1px solid #e8e8e8;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-iframe {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 500px;
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .pdf-preview,
|
|
|
+ .office-preview {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .no-preview,
|
|
|
+ .unsupported-file {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ height: 300px;
|
|
|
+ color: #999;
|
|
|
+ font-size: 16px;
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .loading-overlay {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ bottom: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ background: rgba(255, 255, 255, 0.8);
|
|
|
+ z-index: 10;
|
|
|
+ }
|
|
|
+</style>
|