| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- <template>
- <ele-modal
- :visible.sync="showEditFlag"
- :close-on-click-modal="false"
- custom-class="ele-dialog-form"
- append-to-body
- :fullscreen="true"
- v-if="showEditFlag"
- >
- <iframe
- :src="fileUrl"
- v-if="showEditFlag"
- style="height: 100vh; width: 100%; position: absolute; left: -1000000px; top: 0;"
- frameborder="0"
- allowfullscreen="true"
- ref="Iframe"
- id="Iframe"
- ></iframe>
- <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="!allPagesLoaded"
- >保存</el-button
- >
- <el-button size="small" @click="showEditFlag = false" :disabled="false"
- >取消</el-button
- >
- </template>
- </ele-modal>
- </template>
- <script>
- import seal from './seal.vue';
- import { uploadFileNew } from './api';
- export default {
- data() {
- return {
- fileUrl: '',
- showEditFlag: false,
- restoreData: null,
- allPagesLoaded: false // 是否已加载全部页(控制"加载全部页"按钮 + 盖章交互的启用)
- };
- },
- components: { seal },
- methods: {
- open(row) {
- this.showEditFlag = true;
- this.restoreData = row;
- this.setFileUrl(row);
- },
- restore() {
- this.setFileUrl(this.restoreData);
- },
- handleSave() {
- this.$refs.sealRef.exportToPDF();
- },
- pdfGenerated({ pdfFile, droppedImages }) {
- this.$emit('save', {
- pdfFile: pdfFile,
- id: this.restoreData.id,
- droppedImages
- });
- this.showEditFlag = false;
- // uploadFileNew({
- // module: 'fm',
- // multiPartFile: pdfFile
- // })
- // .then((res) => {
- // this.$emit('save', {
- // stampStoragePath: res.data ? [res.data] : [],
- // id: this.restoreData.id,
- // droppedImages
- // });
- // this.showEditFlag = false;
- // })
- // .finally(() => {
- // this.loading = false;
- // });
- },
- 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 =
- 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 = () => {
- 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">
- .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>
|