Bläddra i källkod

fix: 优化 PDF 加载提示和交互,添加加载全部页功能

xieyong 1 dag sedan
förälder
incheckning
7bc4bead21
2 ändrade filer med 162 tillägg och 28 borttagningar
  1. 6 5
      src/components/addDoc/seal.vue
  2. 156 23
      src/components/addDoc/sealManagement.vue

+ 6 - 5
src/components/addDoc/seal.vue

@@ -3,7 +3,10 @@
     <div class="tool-card">
       <div class="converter-layout">
         <!-- 右侧: 预览 & Canvas 结果 -->
-        <div id="captureTarget" style="position: fixed; left: -100000px">
+        <div
+          id="captureTarget"
+          style="position: fixed; left: -100000px; top: 0; width: 1240px; min-height: 1754px; background: #ffffff;"
+        >
           <div v-html="editableHtml"> </div>
         </div>
         <div class="preview-panel">
@@ -143,10 +146,8 @@
       });
     },
     mounted() {
-      // 组件挂载后自动渲染一次
-      this.$nextTick(() => {
-        this.renderToCanvas();
-      });
+      // 不在挂载时自动渲染(editableHtml 此时为空,会触发 html2canvas InvalidStateError)
+      // 由父组件在 iframe onload 完成后调用 init() 触发渲染
 
       // 保存组件实例引用,用于事件处理器
       window._aaComponentInstance = this;

+ 156 - 23
src/components/addDoc/sealManagement.vue

@@ -9,23 +9,38 @@
   >
     <iframe
       :src="fileUrl"
-      width="100%"
       v-if="showEditFlag"
-      style="height: calc(100vh - 100px); position: absolute; left: -1000000px"
+      style="height: 100vh; width: 100%; position: absolute; left: -1000000px; top: 0;"
       frameborder="0"
       allowfullscreen="true"
       ref="Iframe"
       id="Iframe"
     ></iframe>
 
-    <seal ref="sealRef" @pdf-generated="pdfGenerated"></seal>
+    <div class="seal-wrapper" :class="{ 'is-locking': !allPagesLoaded }">
+      <seal ref="sealRef" @pdf-generated="pdfGenerated"></seal>
+
+      <div v-if="!allPagesLoaded" class="seal-lock-hint">
+        <i class="el-icon-loading seal-lock-spinner"></i>
+        <span class="seal-lock-text">
+          PDF 只加载了前 2 页,点击下方"加载全部页"按钮后可盖印到任意页
+        </span>
+      </div>
+    </div>
 
     <template slot="footer">
+      <el-button
+        size="small"
+        @click="renderAllPages"
+        :disabled="allPagesLoaded"
+        >{{ allPagesLoaded ? '已加载全部页' : '加载全部页' }}</el-button
+      >
+
       <el-button
         type="primary"
         size="small"
         @click="handleSave"
-        :disabled="false"
+        :disabled="!allPagesLoaded"
         >保存</el-button
       >
 
@@ -44,7 +59,8 @@
       return {
         fileUrl: '',
         showEditFlag: false,
-        restoreData: null
+        restoreData: null,
+        allPagesLoaded: false // 是否已加载全部页(控制"加载全部页"按钮 + 盖章交互的启用)
       };
     },
     components: { seal },
@@ -88,36 +104,153 @@
       setFileUrl(row) {
         let file = row.stampStoragePath[0] || row.storagePath[0];
         let fileNames = file.storePath.split('/');
+        // kkFileView 部署在远程,需用远程地址拼 URL(VUE_APP_API_BASE_HOST 已配置为 http://aiot.zoomwin.com.cn:51001)
+        const apiBaseHost =
+          process.env.VUE_APP_API_BASE_HOST || window.location.origin;
         let url =
-          window.location.origin +
+          apiBaseHost +
           '/api/main/file/getFile?objectName=' +
           file.storePath +
           '&fullfilename=' +
           fileNames[fileNames.length - 1];
         this.fileUrl = '/kkfile/onlinePreview?url=' + btoa(url);
+        this.allPagesLoaded = false;
+
         this.$nextTick(() => {
           this.$refs.Iframe.onload = () => {
-            var iframe = document.getElementById('Iframe');
-            console.log(iframe);
-            var iframeDocument = iframe.contentWindow.document;
-            console.log(iframeDocument);
-
-            var container = iframeDocument.querySelectorAll('.container');
-            console.log(container);
-
-            this.$refs.sealRef.init(
-              container[0].innerHTML.replace(
-                /src="https?:\/\/[^\/]+(\/[^"]*)"/g,
-                `src="${window.location.origin}$1"`
-              ),
-              {
-                droppedImages: row.droppedImages || ''
+            setTimeout(() => {
+              // 检查 PDF 总页数(kkFileView 在 .container 内为每页生成一个 .page / img)
+              var iframeDoc = this.$refs.Iframe.contentWindow.document;
+              var pages = iframeDoc.querySelectorAll(
+                '.container .page, .container img'
+              );
+              console.log('PDF 检测到总页数:', pages.length);
+              if (pages.length > 0 && pages.length <= 2) {
+                // ≤ 2 页:无需加载全部,直接视为已加载
+                console.log('PDF ≤ 2 页,自动标记为已加载,跳过手动加载步骤');
+                this.allPagesLoaded = true;
               }
-            );
+              this.extractAndInit('初始');
+            }, 800);
           };
         });
+      },
+      renderAllPages() {
+        if (this.allPagesLoaded) return;
+        var iframe = document.getElementById('Iframe');
+        if (!iframe) return;
+        var iframeWin = iframe.contentWindow;
+        var iframeDoc = iframeWin.document;
+
+        var scrollHeight = Math.max(
+          iframeDoc.documentElement.scrollHeight,
+          iframeDoc.body.scrollHeight
+        );
+
+        console.log('开始反向滚动让所有页面渲染,scrollHeight=', scrollHeight);
+
+        // 从底反向滚到顶,让 pdfjs 把所有 page canvas 渲染到 viewport
+        var scrolled = scrollHeight;
+        var step = -300;
+        var delay = 60;
+        var interval = setInterval(() => {
+          scrolled += step;
+          iframeWin.scrollTo(0, Math.max(0, scrolled));
+          if (scrolled <= 0) {
+            clearInterval(interval);
+            iframeWin.scrollTo(0, scrollHeight);
+            setTimeout(() => {
+              this.extractAndInit('全渲染完成');
+              this.allPagesLoaded = true;
+            }, 800);
+          }
+        }, delay);
+
+        // 30s 兜底
+        setTimeout(() => {
+          clearInterval(interval);
+          iframeWin.scrollTo(0, scrollHeight);
+          this.extractAndInit('全渲染超时');
+          this.allPagesLoaded = true;
+        }, 30000);
+      },
+      extractAndInit(stage) {
+        var iframe = document.getElementById('Iframe');
+        if (!iframe) return;
+        var iframeDoc = iframe.contentWindow.document;
+        var container = iframeDoc.querySelectorAll('.container');
+        console.log(
+          '[' + (stage || '未知阶段') + '] container NodeList 长度:',
+          container.length
+        );
+
+        if (!container[0]) {
+          console.warn(
+            '[' +
+              (stage || '未知阶段') +
+              '] 未找到 .container 元素。可能原因:\n' +
+              '1. kkFileView 服务未部署或 /kkfile 代理路径不对\n' +
+              '2. URL ' +
+              this.fileUrl +
+              ' 返回的不是 kkFileView 的 HTML'
+          );
+          console.log(
+            'iframe body innerHTML 前 500 字符:',
+            ((iframeDoc.body && iframeDoc.body.innerHTML) || '').slice(0, 500)
+          );
+          return;
+        }
+
+        this.$refs.sealRef.init(
+          container[0].innerHTML.replace(
+            // 把 kkFileView 输出的绝对/协议相对 URL 改为相对路径
+            // 这样浏览器从父页面 localhost:8083 加载,走 /kkfile 代理到 51001(浏览器感知同源,无 CORS)
+            /src="(?:https?:)?\/\/[^/]+(\/[^"]*)"/g,
+            `src="$1"`
+          ),
+          {
+            droppedImages:
+              (this.restoreData && this.restoreData.droppedImages) || ''
+          }
+        );
       }
     }
   };
 </script>
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+  .seal-wrapper {
+    position: relative;
+    width: 100%;
+    height: 100%;
+  }
+  // PDF 未全部加载时,禁用盖章区域所有交互(视觉不变)
+  .seal-wrapper.is-locking {
+    pointer-events: none;
+    user-select: none;
+  }
+  .seal-lock-hint {
+    position: absolute;
+    top: 12px;
+    left: 50%;
+    transform: translateX(-50%);
+    z-index: 10;
+    background: rgba(64, 158, 255, 0.92);
+    color: #fff;
+    padding: 8px 18px;
+    border-radius: 20px;
+    font-size: 13px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  }
+  .seal-lock-spinner {
+    font-size: 14px;
+    animation: seal-spin 1s linear infinite;
+  }
+  @keyframes seal-spin {
+    to {
+      transform: rotate(360deg);
+    }
+  }
+</style>