From 7a5f1f359ffaa7df3a27757a92764905debe921a Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Fri, 7 Nov 2025 15:08:41 +0800 Subject: [PATCH] =?UTF-8?q?onlyoffice=E9=9B=86=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/common/OnlyOfficeViewer.vue | 294 ++++++++++++++++++++++---- 1 file changed, 258 insertions(+), 36 deletions(-) diff --git a/src/views/common/OnlyOfficeViewer.vue b/src/views/common/OnlyOfficeViewer.vue index a38a06f..3c02e63 100644 --- a/src/views/common/OnlyOfficeViewer.vue +++ b/src/views/common/OnlyOfficeViewer.vue @@ -1,6 +1,21 @@ @@ -43,14 +58,26 @@ export default { return { docEditor: null, onlyOfficeScriptLoaded: false, - editorConfig: {} + editorConfig: null, + configReady: false, + loading: false, + error: null }; }, - created() { - this.getConfig(); - }, - mounted() { - this.initOnlyOffice(); + async mounted() { + try { + this.loading = true; + this.error = null; + // 先加载配置 + await this.getConfig(); + // 再初始化 OnlyOffice(成功后会自动设置 loading = false) + await this.initOnlyOffice(); + } catch (error) { + console.error('初始化失败:', error); + this.loading = false; + this.error = error.message || '初始化失败'; + this.$emit('error', error); + } }, beforeDestroy() { @@ -61,33 +88,51 @@ export default { // 加载编辑器配置 async getConfig() { try { + console.log('开始获取编辑器配置...', { + fileId: this.documentKey, + fileName: this.fileName, + mode: this.mode, + type: this.type + }); + const res = await getConfigAPI({ fileId: this.documentKey, fileName: this.fileName, mode: this.mode, type: this.type - }) + }); + + console.log('获取配置响应:', res); + if (res.code !== 200) { throw new Error(res.msg || '获取编辑器配置失败'); } if (!res.data) { throw new Error('配置数据为空'); } - - this.editorConfig = res.data; - this.$set(this.editorConfig, 'events', { - onDocumentReady: this.onDocumentReady, - onError: this.onEditorError, - onAppReady: this.onAppReady - }); - - console.log('编辑器配置', this.editorConfig); + // 验证必要的配置字段 + if (!res.data.document || !res.data.document.url) { + throw new Error('配置中缺少文档URL'); + } - } - catch (error) { + // 设置配置,添加事件回调 + this.editorConfig = { + ...res.data, + events: { + onDocumentReady: () => this.onDocumentReady(), + onError: (error) => this.onEditorError(error), + onAppReady: () => this.onAppReady() + } + }; + + this.configReady = true; + console.log('编辑器配置准备完成:', this.editorConfig); + + } catch (error) { console.error('获取编辑器配置失败:', error); - throw new Error('获取编辑器配置失败'); + this.error = error.message || '获取编辑器配置失败'; + throw error; } }, @@ -96,14 +141,26 @@ export default { */ async initOnlyOffice() { try { - // 加载 OnlyOffice 脚本 - await this.loadOnlyOfficeScript(); + // 确保配置已准备好 + if (!this.configReady || !this.editorConfig) { + throw new Error('编辑器配置未准备好,请先加载配置'); + } + // 加载 OnlyOffice 脚本 + console.log('开始加载 OnlyOffice 脚本...'); + await this.loadOnlyOfficeScript(); + console.log('OnlyOffice 脚本加载完成'); + + // 等待 DOM 准备好 + await this.$nextTick(); + // 初始化编辑器 - this.initDocEditor(); + await this.initDocEditor(); } catch (error) { console.error('初始化 OnlyOffice 失败:', error); + this.error = error.message || '初始化 OnlyOffice 失败'; this.$emit('error', error); + throw error; } }, @@ -133,8 +190,16 @@ export default { // 创建新的脚本元素 const script = document.createElement('script'); // onlyOfficeUrl 是 OnlyOffice 服务地址 - const onlyOfficeUrl = process.env.VUE_APP_ONLYOFFICE_URL; - script.src = `${onlyOfficeUrl}/web-apps/apps/api/documents/api.js`; + const onlyOfficeUrl = process.env.VUE_APP_ONLYOFFICE_URL || process.env.VUE_APP_BASE_API || ''; + + if (!onlyOfficeUrl) { + reject(new Error('OnlyOffice 服务地址未配置,请检查环境变量 VUE_APP_ONLYOFFICE_URL')); + return; + } + + const scriptUrl = `${onlyOfficeUrl}/web-apps/apps/api/documents/api.js`; + console.log('加载 OnlyOffice 脚本,URL:', scriptUrl); + script.src = scriptUrl; script.setAttribute('data-onlyoffice', 'true'); script.onload = () => { @@ -160,18 +225,81 @@ export default { /** * 初始化文档编辑器 */ - initDocEditor() { - if (!window.DocsAPI) { - console.error('DocsAPI 未加载'); - return; + async initDocEditor() { + // 验证 DocsAPI + if (!window.DocsAPI || !window.DocsAPI.DocEditor) { + throw new Error('OnlyOffice DocsAPI 未正确加载,请检查脚本是否已加载'); + } + + // 验证配置 + if (!this.editorConfig) { + throw new Error('编辑器配置不存在'); + } + + // 验证容器元素(容器可能被 v-show 隐藏,但 DOM 中应该存在) + await this.$nextTick(); + let container = document.getElementById('placeholder'); + + // 如果容器不存在,等待一下再试 + if (!container) { + console.warn('容器元素不存在,等待 DOM 渲染...'); + await new Promise(resolve => setTimeout(resolve, 100)); + await this.$nextTick(); + container = document.getElementById('placeholder'); + } + + if (!container) { + throw new Error('找不到编辑器容器元素 #placeholder,请确保容器已正确渲染'); + } + + // 即使容器被 v-show 隐藏,也要强制显示以便创建编辑器 + container.style.display = 'block'; + container.style.visibility = 'visible'; + container.style.width = '100%'; + container.style.height = '100%'; + + console.log('容器元素:', container); + console.log('容器尺寸:', { + width: container.offsetWidth, + height: container.offsetHeight, + rect: container.getBoundingClientRect() + }); + + // 确保容器有尺寸 + if (container.offsetWidth === 0 || container.offsetHeight === 0) { + console.warn('容器尺寸为0,等待尺寸调整...'); + await new Promise(resolve => setTimeout(resolve, 300)); + + if (container.offsetWidth === 0 || container.offsetHeight === 0) { + console.warn('容器尺寸仍为0,但继续尝试创建编辑器'); + } } try { - this.docEditor = new window.DocsAPI.DocEditor("placeholder", this.editorConfig); + console.log('创建 OnlyOffice 编辑器,配置:', this.editorConfig); + + // 清空容器 + container.innerHTML = ''; + + // 创建编辑器实例 + this.docEditor = new window.DocsAPI.DocEditor('placeholder', this.editorConfig); + + console.log('编辑器实例创建成功:', this.docEditor); + + // 编辑器创建成功,隐藏加载状态 + this.loading = false; this.$emit('initialized', this.docEditor); } catch (error) { console.error('创建 OnlyOffice 编辑器失败:', error); + console.error('错误详情:', { + message: error.message, + stack: error.stack, + config: this.editorConfig + }); + this.loading = false; + this.error = error.message || '创建编辑器失败'; this.$emit('error', error); + throw error; } }, @@ -216,8 +344,10 @@ export default { /** * 重新加载文档 */ - reloadDocument(newConfig = {}) { + async reloadDocument(newConfig = {}) { this.destroyEditor(); + this.configReady = false; + this.error = null; // 可以在这里更新配置 if (newConfig.documentUrl) { @@ -230,10 +360,34 @@ export default { this.mode = newConfig.mode; } - // 等待 DOM 更新后重新初始化 - this.$nextTick(() => { - this.initDocEditor(); - }); + // 重新加载配置并初始化 + try { + this.loading = true; + await this.getConfig(); + await this.initDocEditor(); + } catch (error) { + console.error('重新加载文档失败:', error); + this.error = error.message || '重新加载失败'; + } finally { + this.loading = false; + } + }, + + /** + * 重试 + */ + async retry() { + this.error = null; + this.loading = true; + try { + await this.getConfig(); + await this.initOnlyOffice(); + } catch (error) { + console.error('重试失败:', error); + this.error = error.message || '重试失败'; + } finally { + this.loading = false; + } } } }; @@ -245,11 +399,79 @@ export default { height: 90vh; display: flex; flex-direction: column; + position: relative; } .editor-placeholder { width: 100%; - height: 100vh; + height: 100%; min-height: 600px; } + +.loading-state, +.error-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + min-height: 400px; + text-align: center; + padding: 40px; +} + +.loading-spinner { + width: 40px; + height: 40px; + border: 4px solid #f3f3f3; + border-top: 4px solid #3498db; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 16px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.error-icon { + font-size: 48px; + margin-bottom: 16px; +} + +.error-state h3 { + margin: 0 0 8px 0; + color: #e74c3c; + font-size: 18px; +} + +.error-state p { + margin: 0 0 16px 0; + color: #666; + font-size: 14px; +} + +.retry-btn { + background: #3498db; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background 0.3s; +} + +.retry-btn:hover { + background: #2980b9; +} + +/* 确保 OnlyOffice iframe 正确显示 */ +.editor-placeholder ::v-deep iframe { + width: 100% !important; + height: 100% !important; + border: none; + display: block; +} \ No newline at end of file