| | |
| | | > |
| | | <!-- 加载状态 --> |
| | | <div v-if="loading" class="preview-loading"> |
| | | <i class="el-icon-loading" style="font-size: 40px; margin-bottom: 16px;"></i> |
| | | <i |
| | | class="el-icon-loading" |
| | | style="font-size: 40px; margin-bottom: 16px;" |
| | | ></i> |
| | | <span>文件加载中...</span> |
| | | </div> |
| | | |
| | |
| | | <div v-else-if="fileType === 'image'" class="preview-container"> |
| | | <div class="image-toolbar"> |
| | | <el-button-group> |
| | | <el-button size="mini" @click="zoomImageIn" :disabled="imageScale >= 300"> |
| | | <el-button |
| | | size="mini" |
| | | @click="zoomImageIn" |
| | | :disabled="imageScale >= 300" |
| | | > |
| | | <i class="el-icon-zoom-in"></i> 放大 |
| | | </el-button> |
| | | <el-button size="mini" disabled>{{ imageScale }}%</el-button> |
| | | <el-button size="mini" @click="zoomImageOut" :disabled="imageScale <= 50"> |
| | | <el-button |
| | | size="mini" |
| | | @click="zoomImageOut" |
| | | :disabled="imageScale <= 50" |
| | | > |
| | | <i class="el-icon-zoom-out"></i> 缩小 |
| | | </el-button> |
| | | <el-button size="mini" @click="resetImageZoom"> |
| | |
| | | <!-- PDF预览 --> |
| | | <div v-else-if="fileType === 'pdf'" class="preview-container pdf-preview"> |
| | | <div class="pdf-toolbar"> |
| | | <el-button-group> |
| | | <el-button-group v-if="!useNativePdf"> |
| | | <el-button |
| | | size="mini" |
| | | @click="changePage(currentPage - 1)" |
| | |
| | | </el-button> |
| | | </el-button-group> |
| | | |
| | | <el-button-group class="zoom-controls"> |
| | | <el-button-group v-if="!useNativePdf" class="zoom-controls"> |
| | | <el-button size="mini" @click="zoomOut" :disabled="scale <= 50"> |
| | | <i class="el-icon-zoom-out"></i> 缩小 |
| | | </el-button> |
| | |
| | | </el-button> |
| | | </el-button-group> |
| | | |
| | | <el-button |
| | | size="mini" |
| | | type="success" |
| | | @click="handleDownload" |
| | | > |
| | | <el-button size="mini" type="success" @click="handleDownload"> |
| | | <i class="el-icon-download"></i> 下载 |
| | | </el-button> |
| | | </div> |
| | | |
| | | <div class="pdf-content" v-loading="pdfLoading"> |
| | | <!-- pdf.js 因跨域(CORS)加载失败时,降级为浏览器原生 PDF 预览(iframe 导航不受同源策略限制) --> |
| | | <iframe |
| | | v-if="useNativePdf" |
| | | :src="fileUrl" |
| | | class="pdf-native-iframe" |
| | | frameborder="0" |
| | | ></iframe> |
| | | |
| | | <div class="pdf-content" v-else v-loading="pdfLoading"> |
| | | <pdf |
| | | ref="pdf" |
| | | :src="pdfUrl" |
| | |
| | | </div> |
| | | |
| | | <!-- Office文档预览 --> |
| | | <div v-else-if="fileType === 'office'" class="preview-container office-preview"> |
| | | <div |
| | | v-else-if="fileType === 'office'" |
| | | class="preview-container office-preview" |
| | | > |
| | | <div class="office-toolbar"> |
| | | <el-alert |
| | | title="Office文档预览提示" |
| | |
| | | |
| | | <div class="office-content"> |
| | | <iframe |
| | | :src="`https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(fileUrl)}`" |
| | | :src=" |
| | | `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent( |
| | | fileUrl |
| | | )}` |
| | | " |
| | | width="100%" |
| | | height="600px" |
| | | frameborder="0" |
| | |
| | | type: Object, |
| | | default: () => ({}) |
| | | } |
| | | // 添加baseUrlHt作为props传入 |
| | | // baseUrlHt: { |
| | | // type: String, |
| | | // default: process.env.VUE_APP_BASE_API |
| | | // } |
| | | }, |
| | | data() { |
| | | return { |
| | |
| | | // 加载状态 |
| | | loading: false, |
| | | pdfLoading: false, |
| | | baseUrlHt: "", |
| | | // 文件信息 |
| | | fileUrl: "", |
| | | fileName: "", |
| | |
| | | pageCount: 0, |
| | | scale: 100, |
| | | pageRotate: 0, |
| | | // pdf.js 加载失败(如 CORS)时降级为浏览器原生预览 |
| | | useNativePdf: false, |
| | | // 图片相关 |
| | | imageScale: 100 |
| | | }; |
| | |
| | | } |
| | | }, |
| | | previewVisible(newVal) { |
| | | this.$emit('update:visible', newVal); |
| | | this.$emit("update:visible", newVal); |
| | | if (!newVal) { |
| | | this.handleClose(); |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.calculateBaseUrl(); |
| | | }, |
| | | methods: { |
| | | /** URL处理函数 - 将完整URL替换为baseUrlHt */ |
| | | processFileUrl(url) { |
| | | if (!url) return ""; |
| | | |
| | | // 如果已经是完整的http或https链接 |
| | | if (url.startsWith("http://") || url.startsWith("https://")) { |
| | | // 找到第三个斜杠后的位置,提取路径部分 |
| | | const thirdSlashIndex = url.indexOf("/", 8); // 从http://或https://之后开始找 |
| | | if (thirdSlashIndex !== -1) { |
| | | return `${this.baseUrlHt}${url.substring(thirdSlashIndex)}`; |
| | | } |
| | | return this.baseUrlHt; // 如果没有路径部分,只返回baseUrlHt |
| | | } |
| | | |
| | | // 相对路径处理 |
| | | if (url.startsWith("/")) { |
| | | return `${this.baseUrlHt}${url}`; |
| | | } |
| | | |
| | | return `${this.baseUrlHt}/${url}`; |
| | | }, |
| | | /** 计算 baseUrlHt */ |
| | | calculateBaseUrl() { |
| | | // 走当前域名(同域相对路径),由 nginx 的 /profile/ 代理转发到后端 9095。 |
| | | // 直连 9095 端口会因端口不同产生跨域(CORS),而后端存在双重 CORS 响应头问题, |
| | | // 同域请求不触发 CORS 检查,可从根源上绕开该问题(图片/PDF/下载全部生效)。 |
| | | this.baseUrlHt = ""; |
| | | console.log("文件访问走同域代理: /profile/"); |
| | | }, |
| | | |
| | | /** 初始化预览 */ |
| | | initPreview() { |
| | | if (!this.file) return; |
| | | |
| | | this.fileName = this.file.fileName || this.file.name || "未知文件"; |
| | | this.fileUrl = this.file.fileUrl || this.file.path || this.file.url; |
| | | const originalUrl = this.file.fileUrl || this.file.path || this.file.url; |
| | | |
| | | // 处理URL,替换域名部分 |
| | | this.fileUrl = this.processFileUrl(originalUrl); |
| | | console.log(this.fileUrl,'this.fileUrl'); |
| | | |
| | | this.fileType = this.getFileType(this.fileName); |
| | | |
| | | this.loading = true; |
| | |
| | | this.scale = 100; |
| | | this.imageScale = 100; |
| | | this.pageCount = 0; |
| | | this.useNativePdf = false; |
| | | |
| | | // 模拟加载延迟 |
| | | setTimeout(() => { |
| | |
| | | getFileType(fileName) { |
| | | if (!fileName) return "other"; |
| | | |
| | | const extension = fileName.split('.').pop().toLowerCase(); |
| | | const extension = fileName |
| | | .split(".") |
| | | .pop() |
| | | .toLowerCase(); |
| | | const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"]; |
| | | const pdfTypes = ["pdf"]; |
| | | const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"]; |
| | |
| | | |
| | | /** 获取文件扩展名 */ |
| | | getFileExtension(filename) { |
| | | return filename.split('.').pop().toLowerCase() || "未知"; |
| | | return ( |
| | | filename |
| | | .split(".") |
| | | .pop() |
| | | .toLowerCase() || "未知" |
| | | ); |
| | | }, |
| | | |
| | | /** PDF加载完成 */ |
| | |
| | | pdfErrorHandler(error) { |
| | | console.error("PDF加载失败:", error); |
| | | this.pdfLoading = false; |
| | | this.$message.error("PDF文件加载失败,请尝试下载后查看"); |
| | | this.previewVisible = false; |
| | | // pdf.js 通过 fetch 加载跨域 PDF 受 CORS 限制,失败时降级为浏览器原生预览 |
| | | // (iframe 属于导航请求,不受同源策略限制,可以正常加载跨域 PDF) |
| | | this.useNativePdf = true; |
| | | this.$message({ |
| | | type: "info", |
| | | message: "已切换为浏览器原生预览模式" |
| | | }); |
| | | }, |
| | | |
| | | /** 图片加载完成 */ |
| | |
| | | this.imageScale = 100; |
| | | }, |
| | | |
| | | /** 下载文件 */ |
| | | /** 下载文件 - 也需要处理URL */ |
| | | handleDownload() { |
| | | if (!this.fileUrl) { |
| | | this.$message.warning("文件路径不存在,无法下载"); |
| | |
| | | align-items: flex-start; |
| | | } |
| | | |
| | | /* 原生 PDF 预览(CORS 降级方案) */ |
| | | .pdf-native-iframe { |
| | | flex: 1; |
| | | border: none; |
| | | background: #f8f9fa; |
| | | } |
| | | |
| | | /* Office预览样式 */ |
| | | .office-content { |
| | | flex: 1; |