|
@@ -18,14 +18,38 @@
|
|
|
/>
|
|
|
|
|
|
<!-- Word 文档预览 -->
|
|
|
- <div
|
|
|
+ <div v-if="isWord" class="word-container">
|
|
|
+ <!-- 加载中状态 -->
|
|
|
+ <div v-if="!wordLoaded && !previewError" class="loading-word">
|
|
|
+ <el-progress
|
|
|
+ v-if="loadingProgress > 0"
|
|
|
+ type="circle"
|
|
|
+ :percentage="loadingProgress"
|
|
|
+ ></el-progress>
|
|
|
+ <span v-else>文档加载中...</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 预览错误状态 -->
|
|
|
+ <div v-else-if="previewError" class="preview-error">
|
|
|
+ <i class="el-icon-warning-outline"></i>
|
|
|
+ <p>{{ errorMessage }}</p>
|
|
|
+ <div class="error-actions">
|
|
|
+ <el-button type="primary" @click="retryPreview">重试</el-button>
|
|
|
+ <el-button @click="downloadFile">下载文件</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 预览内容 -->
|
|
|
+ <div v-else v-html="wordContent" class="word-preview"></div>
|
|
|
+ </div>
|
|
|
+ <!-- <div
|
|
|
v-if="isWord && wordLoaded"
|
|
|
v-html="wordContent"
|
|
|
class="word-preview"
|
|
|
></div>
|
|
|
<div v-else-if="isWord && !wordLoaded" class="loading-word">
|
|
|
{{ wordLoadingError ? "加载失败,请重试" : "加载中..." }}
|
|
|
- </div>
|
|
|
+ </div> -->
|
|
|
<div
|
|
|
v-if="isExcel"
|
|
|
id="luckysheet"
|
|
@@ -57,6 +81,8 @@ import { searchTaskInfo } from "@/api/knowledge";
|
|
|
import mammoth from "mammoth";
|
|
|
// 添加 LuckyExcel 导入
|
|
|
import LuckyExcel from "luckyexcel";
|
|
|
+import { saveAs } from "file-saver";
|
|
|
+import JSZip from "jszip";
|
|
|
export default {
|
|
|
props: {
|
|
|
fileUrl: {
|
|
@@ -93,6 +119,13 @@ export default {
|
|
|
wordLoaded: false,
|
|
|
wordLoadingError: false,
|
|
|
internalFileUrl: "", // 新增:内部使用的 URL
|
|
|
+
|
|
|
+ loadingProgress: 0,
|
|
|
+ wordContent: "",
|
|
|
+ wordLoaded: false,
|
|
|
+ previewError: false,
|
|
|
+ errorMessage: "",
|
|
|
+ docData: null, // 保存文档数据用于下载
|
|
|
};
|
|
|
},
|
|
|
watch: {
|
|
@@ -100,13 +133,25 @@ export default {
|
|
|
immediate: true,
|
|
|
handler(newValue) {
|
|
|
/* this.internalFileUrl = newValue; */
|
|
|
- /* if (this.isWord) {
|
|
|
+ if (this.isWord) {
|
|
|
this.loadWordContent();
|
|
|
- } */
|
|
|
+ }
|
|
|
},
|
|
|
},
|
|
|
},
|
|
|
computed: {
|
|
|
+ officePreviewUrl() {
|
|
|
+ // 添加此计算属性
|
|
|
+ if (this.isOffice) {
|
|
|
+ // 使用微软Office Online Viewer
|
|
|
+ return `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
|
|
|
+ this.fileUrl
|
|
|
+ )}`;
|
|
|
+ // 或者使用Google Docs Viewer
|
|
|
+ // return `https://docs.google.com/gview?url=${encodeURIComponent(this.fileUrl)}&embedded=true`;
|
|
|
+ }
|
|
|
+ return "";
|
|
|
+ },
|
|
|
isSupported() {
|
|
|
return this.supportedTypes.includes(this.fileType);
|
|
|
},
|
|
@@ -114,7 +159,7 @@ export default {
|
|
|
return ["jpg", "jpeg", "png", "gif"].includes(this.fileType);
|
|
|
},
|
|
|
isWord() {
|
|
|
- return ["doc", "docx"].includes(this.fileType);
|
|
|
+ return ["doc", "docx"].includes(this.fileType) || this.type === "doc";
|
|
|
},
|
|
|
isExcel() {
|
|
|
return ["xls", "xlsx"].includes(this.fileType);
|
|
@@ -124,58 +169,131 @@ export default {
|
|
|
},
|
|
|
},
|
|
|
mounted() {
|
|
|
- console.log(this.isExcel);
|
|
|
+ console.log(this.isWord);
|
|
|
if (this.fileType === "txt") {
|
|
|
this.loadTextContent();
|
|
|
} else if (this.isWord) {
|
|
|
this.loadWordContent();
|
|
|
- } else if (this.$route.query.type=='xlsx') {
|
|
|
+ } else if (this.$route.query.type == "xlsx") {
|
|
|
this.loadExcelContent();
|
|
|
}
|
|
|
},
|
|
|
methods: {
|
|
|
async loadWordContent() {
|
|
|
this.wordLoaded = false;
|
|
|
- this.wordLoadingError = false;
|
|
|
- try {
|
|
|
- // 获取文档 URL
|
|
|
- /* if (this.type !== "ai") { */
|
|
|
- const res = await searchTaskInfo({
|
|
|
- page: 1,
|
|
|
- page_size: 10,
|
|
|
- document_id: this.$route.query.id,
|
|
|
- });
|
|
|
- this.internalFileUrl = res.data.documentUrl;
|
|
|
- /* } else {
|
|
|
- this.internalFileUrl = localStorage.getItem("href");
|
|
|
- } */
|
|
|
+ this.loadingProgress = 0;
|
|
|
+ this.previewError = false;
|
|
|
+ this.errorMessage = "";
|
|
|
|
|
|
- // 使用 fetch 获取文件内容,指定 responseType 为 'arraybuffer'
|
|
|
- const response = await fetch(this.internalFileUrl, {
|
|
|
- method: "GET",
|
|
|
- headers: {
|
|
|
- "Content-Type":
|
|
|
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
|
- },
|
|
|
+ try {
|
|
|
+ // 获取文档URL
|
|
|
+ const res = await searchTaskInfo({
|
|
|
+ page: 1,
|
|
|
+ page_size: 10,
|
|
|
+ document_id: this.$route.query.id,
|
|
|
});
|
|
|
|
|
|
- if (!response.ok) {
|
|
|
- throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
+ const docUrl = res.data.documentUrl;
|
|
|
+
|
|
|
+ // 下载文件并显示进度
|
|
|
+ const response = await fetch(docUrl);
|
|
|
+ const reader = response.body.getReader();
|
|
|
+ const contentLength = +response.headers.get("Content-Length");
|
|
|
+
|
|
|
+ let receivedLength = 0;
|
|
|
+ let chunks = [];
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ const { done, value } = await reader.read();
|
|
|
+
|
|
|
+ if (done) break;
|
|
|
+
|
|
|
+ chunks.push(value);
|
|
|
+ receivedLength += value.length;
|
|
|
+
|
|
|
+ this.loadingProgress = Math.round(
|
|
|
+ (receivedLength / contentLength) * 100
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- const arrayBuffer = await response.arrayBuffer();
|
|
|
- const result = await mammoth.convertToHtml({ arrayBuffer });
|
|
|
- this.wordContent = result.value;
|
|
|
- this.wordLoaded = true;
|
|
|
+ // 合并数据
|
|
|
+ this.docData = new Uint8Array(receivedLength);
|
|
|
+ let position = 0;
|
|
|
+ for (let chunk of chunks) {
|
|
|
+ this.docData.set(chunk, position);
|
|
|
+ position += chunk.length;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理不同的文档格式
|
|
|
+ if (this.fileType === "doc") {
|
|
|
+ try {
|
|
|
+ const result = await mammoth.convertToHtml({
|
|
|
+ arrayBuffer: this.docData.buffer,
|
|
|
+ });
|
|
|
+ this.wordContent = this.enhanceWordContent(result.value);
|
|
|
+ this.wordLoaded = true;
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error("无法预览该文档,可能是格式不兼容或文件已损坏");
|
|
|
+ }
|
|
|
+ } else if (this.fileType === "docx") {
|
|
|
+ const result = await mammoth.convertToHtml({
|
|
|
+ arrayBuffer: this.docData.buffer,
|
|
|
+ });
|
|
|
+ this.wordContent = this.enhanceWordContent(result.value);
|
|
|
+ this.wordLoaded = true;
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
- console.error("加载Word文档内容时出错:", error);
|
|
|
- this.wordContent = `无法加载Word文档内容: ${error.message}`;
|
|
|
- this.wordLoadingError = true;
|
|
|
- this.$message.error(`文档加载失败: ${error.message}`);
|
|
|
- } finally {
|
|
|
- this.wordLoaded = true;
|
|
|
+ console.error("文档加载失败:", error);
|
|
|
+ this.previewError = true;
|
|
|
+ this.errorMessage = error.message || "文档加载失败,请重试或下载查看";
|
|
|
}
|
|
|
},
|
|
|
+
|
|
|
+ // 重试预览
|
|
|
+ retryPreview() {
|
|
|
+ this.loadWordContent();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下载文件
|
|
|
+ downloadFile() {
|
|
|
+ if (this.docData) {
|
|
|
+ const blob = new Blob([this.docData], {
|
|
|
+ type:
|
|
|
+ this.fileType === "doc"
|
|
|
+ ? "application/msword"
|
|
|
+ : "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
|
+ });
|
|
|
+ saveAs(blob, `document.${this.fileType}`);
|
|
|
+ } else {
|
|
|
+ this.$message.error("文件数据不存在,请重试");
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 增强文档内容显示
|
|
|
+ enhanceWordContent(content) {
|
|
|
+ return `
|
|
|
+ <div class="enhanced-word-content">
|
|
|
+ <style>
|
|
|
+ .enhanced-word-content {
|
|
|
+ font-family: "Microsoft YaHei", Arial, sans-serif;
|
|
|
+ line-height: 1.6;
|
|
|
+ color: #333;
|
|
|
+ padding: 20px;
|
|
|
+ }
|
|
|
+ .enhanced-word-content h1 { font-size: 24px; margin: 20px 0; }
|
|
|
+ .enhanced-word-content h2 { font-size: 20px; margin: 16px 0; }
|
|
|
+ .enhanced-word-content p { margin: 12px 0; }
|
|
|
+ .enhanced-word-content img { max-width: 100%; height: auto; }
|
|
|
+ .enhanced-word-content table { border-collapse: collapse; width: 100%; }
|
|
|
+ .enhanced-word-content td, .enhanced-word-content th {
|
|
|
+ border: 1px solid #ddd;
|
|
|
+ padding: 8px;
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+ ${content}
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ },
|
|
|
blobToArrayBuffer(blob) {
|
|
|
return new Promise((resolve, reject) => {
|
|
|
const reader = new FileReader();
|
|
@@ -187,13 +305,13 @@ export default {
|
|
|
async loadTextContent() {
|
|
|
try {
|
|
|
// 获取文档 URL
|
|
|
- /* if (this.type !== "ai") { */
|
|
|
- const res = await searchTaskInfo({
|
|
|
- page: 1,
|
|
|
- page_size: 10,
|
|
|
- document_id: this.$route.query.id,
|
|
|
- });
|
|
|
- this.internalFileUrl = res.data.documentUrl;
|
|
|
+ /* if (this.type !== "ai") { */
|
|
|
+ const res = await searchTaskInfo({
|
|
|
+ page: 1,
|
|
|
+ page_size: 10,
|
|
|
+ document_id: this.$route.query.id,
|
|
|
+ });
|
|
|
+ this.internalFileUrl = res.data.documentUrl;
|
|
|
/* } else {
|
|
|
this.internalFileUrl = localStorage.getItem("href");
|
|
|
} */
|
|
@@ -283,4 +401,44 @@ export default {
|
|
|
font-size: 18px;
|
|
|
color: #909399;
|
|
|
}
|
|
|
+.preview-error {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 400px;
|
|
|
+ color: #909399;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.preview-error i {
|
|
|
+ font-size: 48px;
|
|
|
+ color: #f56c6c;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.preview-error p {
|
|
|
+ font-size: 16px;
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.error-actions {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.loading-word {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 400px;
|
|
|
+ gap: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.word-preview {
|
|
|
+ overflow-y: auto;
|
|
|
+ max-height: 830px;
|
|
|
+ padding: 20px;
|
|
|
+}
|
|
|
</style>
|