From 49cdc0884a9a4381cd968cffff65e2a332bc675a Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期六, 24 十月 2026 21:05:29 +0800
Subject: [PATCH] feat(cnas-search): 1.标准查新模块  2.印章使用记录

---
 src/components/Onlyoffice/onlyoffice.vue |  185 ++++++++++++++++++++++++++++++++++------------
 1 files changed, 137 insertions(+), 48 deletions(-)

diff --git a/src/components/Onlyoffice/onlyoffice.vue b/src/components/Onlyoffice/onlyoffice.vue
index 00db4a5..eb69148 100644
--- a/src/components/Onlyoffice/onlyoffice.vue
+++ b/src/components/Onlyoffice/onlyoffice.vue
@@ -8,13 +8,39 @@
       class="native-pdf-preview"
       title="PDF棰勮"
     ></iframe>
-    <div v-else id="vabOnlyOffice"></div>
+    <template v-else>
+      <div :id="editorId"></div>
+      <div v-if="editorWarning" class="editor-warning">{{ editorWarning }}</div>
+      <div v-if="editorState !== 'ready'" class="editor-status">
+        <template v-if="editorState === 'failed'">
+          <p>{{ editorError }}</p>
+          <el-button type="primary" @click="setEditor(option)">閲嶆柊鍔犺浇</el-button>
+        </template>
+        <span v-else>姝e湪杩炴帴鏂囨。鏈嶅姟骞跺姞杞芥姤鍛�...</span>
+      </div>
+    </template>
   </div>
 </template>
 
 <script>
 // OnlyOffice 鏂囨。鏈嶅姟鍦板潃锛屾寜鐜閰嶇疆锛涜剼鏈湪鐪熸闇�瑕侀瑙堟椂鎵嶅姞杞�
 const OFFICE_API_BASE = import.meta.env.VITE_APP_ONLYOFFICE_API || "http://192.168.21.53:9001";
+
+function normalizeOptions(source = {}) {
+  return {
+    url: source.url || "",
+    isEdit: source.isEdit === true || source.isEdit === "true",
+    fileType: source.fileType || "",
+    title: source.title || "",
+    lang: source.lang || "zh-CN",
+    isPrint: source.isPrint !== false,
+    user: { id: source.user_id, name: source.user_name },
+    editUrl: source.editUrl || "",
+    key: source.key || "",
+    model: source.model,
+    token: source.token
+  };
+}
 
 // 澶氫釜椤甸潰鍏辩敤缂栬緫鍣紝api.js 鍙姞杞戒竴娆�
 let docsApiPromise = null;
@@ -26,14 +52,23 @@
     docsApiPromise = new Promise((resolve, reject) => {
       const script = document.createElement("script");
       script.src = OFFICE_API_BASE.replace(/\/$/, "") + "/web-apps/apps/api/documents/api.js";
+      const timeout = window.setTimeout(() => {
+        script.remove();
+        reject(new Error("OnlyOffice 鏂囨。鏈嶅姟杩炴帴瓒呮椂"));
+      }, 15000);
       script.onload = () => {
+        window.clearTimeout(timeout);
         if (window.DocsAPI) {
           resolve(window.DocsAPI);
         } else {
           reject(new Error("api.js 鍔犺浇瀹屾垚浣嗘湭娉ㄥ唽 DocsAPI"));
         }
       };
-      script.onerror = () => reject(new Error("OnlyOffice 鏂囨。鏈嶅姟鑴氭湰鍔犺浇澶辫触锛�" + script.src));
+      script.onerror = () => {
+        window.clearTimeout(timeout);
+        script.remove();
+        reject(new Error("OnlyOffice 鏂囨。鏈嶅姟鑴氭湰鍔犺浇澶辫触"));
+      };
       document.head.appendChild(script);
     });
     // 鍔犺浇澶辫触鍚庡厑璁镐笅娆¢噸璇�
@@ -47,21 +82,18 @@
 export default {
   name: "VabOnlyOffice",
   props: ['options'],
+  emits: ['ready', 'error'],
   data() {
     return {
+      editorId: `onlyoffice-${this._uid || Math.random().toString(36).slice(2)}`,
       doctype: "",
       docEditor: null,
-      //鍙傝�僾abOnlyOffice缁勪欢鍙傛暟閰嶇疆
-      option: {
-        url: "",
-        isEdit: false,
-        fileType: "",
-        title: "",
-        lang: "zh-CN",
-        isPrint: true,
-        user: {},
-        editUrl: ""
-      }
+      editorGeneration: 0,
+      editorState: 'loading',
+      editorError: '',
+      editorWarning: '',
+      readyTimer: null,
+      option: normalizeOptions(this.options || this.$route.query)
     };
   },
   computed: {
@@ -69,41 +101,24 @@
       return this.isPdfFile(this.option);
     }
   },
-  created() {
-    if(this.options){
-      const option = this.options
-      this.option.url = option.url
-      this.option.isEdit = option.isEdit === "true" ? true : false
-      this.option.fileType = option.fileType
-      this.option.title = option.title
-      this.option.lang = option.lang
-      this.option.isPrint = option.isPrint
-      this.option.user.id = option.user_id
-      this.option.user.name = option.user_name
-      this.option.editUrl = option.editUrl
-    }else{
-      const option = this.$route.query
-      this.option.url = option.url
-      this.option.isEdit = option.isEdit === "true" ? true : false
-      this.option.fileType = option.fileType
-      this.option.title = option.title
-      this.option.lang = option.lang
-      this.option.isPrint = option.isPrint
-      this.option.user.id = option.user_id
-      this.option.user.name = option.user_name
-      this.option.editUrl = option.editUrl
-    }
-  },
   beforeUnmount() {
+    this.editorGeneration++;
+    window.clearTimeout(this.readyTimer);
     this.destroyEditor();
   },
   watch: {
-    option: {
-      handler: function(n) {
-        this.setEditor(n);
-        this.doctype = this.getFileType(n.fileType);
+    options: {
+      handler(newOptions) {
+        this.option = normalizeOptions(newOptions || this.$route.query);
+        this.setEditor(this.option);
       },
       deep: true
+    },
+    '$route.query'(query) {
+      if (!this.options) {
+        this.option = normalizeOptions(query);
+        this.setEditor(this.option);
+      }
     }
   },
   mounted() {
@@ -114,19 +129,32 @@
   methods: {
     destroyEditor() {
       if (this.docEditor !== null) {
-        this.docEditor.destroyEditor();
+        try {
+          this.docEditor.destroyEditor();
+        } catch (error) {
+          console.error('閿�姣� OnlyOffice 缂栬緫鍣ㄥけ璐�', error);
+        }
         this.docEditor = null;
       }
     },
     async setEditor(option) {
+      const generation = ++this.editorGeneration;
+      window.clearTimeout(this.readyTimer);
       this.destroyEditor();
       // PDF 浣跨敤娴忚鍣ㄥ師鐢熼瑙堬紱鍏朵粬鏂囨。鎵嶅垵濮嬪寲 OnlyOffice銆�
       if (!option || !option.url || this.isPdfFile(option)) {
         return;
       }
+      this.editorState = 'loading';
+      this.editorError = '';
+      this.editorWarning = '';
       // 绛夊緟 v-else 涓殑 OnlyOffice 瀹瑰櫒瀹屾垚娓叉煋銆�
       await this.$nextTick();
       this.doctype = this.getFileType(option.fileType);
+      if (!this.doctype) {
+        this.failEditor('鎶ュ憡鏂囦欢鏍煎紡涓嶅彈鏀寔', generation);
+        return;
+      }
       let config = {
         document: {
           //鍚庣紑
@@ -149,6 +177,7 @@
           //瀹氬埗
           customization: {
             autosave: true, //鏄惁鑷姩淇濆瓨
+            forcesave: true, //鐐瑰嚮淇濆瓨鏃剁珛鍗冲洖璋冧笟鍔℃湇鍔�
             chat: true,
             comments: false,
             help: false,
@@ -164,20 +193,54 @@
         },
         width: "100%",
         height: "100%",
-        token: option.token || ""
+        token: option.token || "",
+        events: {
+          onDocumentReady: () => {
+            if (generation === this.editorGeneration) {
+              window.clearTimeout(this.readyTimer);
+              this.editorState = 'ready';
+              this.$emit('ready');
+            }
+          },
+          onError: (event) => {
+            const detail = event && event.data && (event.data.errorDescription || event.data.errorCode);
+            this.failEditor(detail ? `鎶ュ憡鍔犺浇澶辫触锛�${detail}` : '鎶ュ憡鍔犺浇澶辫触锛岃妫�鏌ユ枃妗f湇鍔�', generation);
+          }
+        }
       };
 
       try {
         await loadDocsApi();
       } catch (error) {
         console.error(error);
-        this.$message.error("鏂囨。鏈嶅姟鏆傛椂涓嶅彲鐢紝鏃犳硶棰勮");
+        this.failEditor(error.message || '鏂囨。鏈嶅姟鏆傛椂涓嶅彲鐢�', generation);
         return;
       }
-      if (this._isDestroyed) {
+      if (generation !== this.editorGeneration || !this.$el?.isConnected) {
         return;
       }
-      this.docEditor = new window.DocsAPI.DocEditor("vabOnlyOffice", config);
+      try {
+        this.readyTimer = window.setTimeout(() => {
+          this.failEditor('鎶ュ憡鍔犺浇瓒呮椂锛岃妫�鏌ユ枃妗f湇鍔′笌鎶ュ憡鍦板潃', generation);
+        }, 30000);
+        this.docEditor = new window.DocsAPI.DocEditor(this.editorId, config);
+      } catch (error) {
+        window.clearTimeout(this.readyTimer);
+        console.error(error);
+        this.failEditor('鏂囨。缂栬緫鍣ㄥ垵濮嬪寲澶辫触锛岃閲嶈瘯', generation);
+      }
+    },
+    failEditor(message, generation) {
+      if (generation !== this.editorGeneration) return;
+      window.clearTimeout(this.readyTimer);
+      if (this.editorState === 'ready') {
+        this.editorWarning = String(message);
+        this.$emit('error', this.editorWarning);
+        return;
+      }
+      this.editorError = String(message);
+      this.editorState = 'failed';
+      this.$emit('error', this.editorError);
     },
     isPdfFile(option) {
       if (!option) {
@@ -253,10 +316,36 @@
 <style scoped>
 .document-preview,
 .native-pdf-preview,
-#vabOnlyOffice {
+.document-preview > div {
   width: 100%;
   height: 100%;
 }
+.document-preview {
+  position: relative;
+}
+.editor-status {
+  position: absolute;
+  inset: 0;
+  z-index: 2;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  gap: 12px;
+  background: #fff;
+  color: #606266;
+}
+.editor-warning {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  z-index: 2;
+  height: auto !important;
+  padding: 8px 12px;
+  background: #fff3e0;
+  color: #a35d00;
+}
 .native-pdf-preview {
   display: block;
   border: 0;

--
Gitblit v1.9.3