From 93e8d77069f4cb693560b0536b18fdd7625b1563 Mon Sep 17 00:00:00 2001 From: xuhuajiao <13476289682@163.com> Date: Wed, 23 Sep 2026 13:53:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E8=A7=88=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vendors/pdf/PdfView.vue | 76 ++++---- src/views/preview/index.vue | 95 ++++++---- .../processManage/index-old.vue | 167 ++++++++++++++++++ 3 files changed, 269 insertions(+), 69 deletions(-) create mode 100644 src/views/system/fileLibraryManage/processManage/index-old.vue diff --git a/src/vendors/pdf/PdfView.vue b/src/vendors/pdf/PdfView.vue index 45a782b..3464e1e 100644 --- a/src/vendors/pdf/PdfView.vue +++ b/src/vendors/pdf/PdfView.vue @@ -53,7 +53,9 @@ export default { initColor: null, fileParentInfo: null, selectedCategory:null, - formatData:[] + formatData:[], + // 带水印的 PDF 字节,供外部打印使用 + pdfBytes: null } }, mounted() { @@ -78,7 +80,8 @@ export default { return } this.pdf_scale = this.pdf_scale + 0.1 - this.loadFile() + // 只重新渲染 canvas,不重新加载文档,避免大文件每次缩放都卡顿 + this.renderPagePdf() }, scaleX() { // 缩小 @@ -87,7 +90,7 @@ export default { return } this.pdf_scale = this.pdf_scale - 0.1 - this.loadFile() + this.renderPagePdf() }, formateTime() { const time = new Date() @@ -114,35 +117,8 @@ export default { await getInitWatermark().then((res) => { this.watermarkInfo = res if (res.watermarkType === '1') { - // 图片水印 - let dom = document.getElementsByClassName('page') - let canvas = document.createElement('canvas') - let cxt = canvas.getContext('2d') - let divImg = document.createElement('div') - - let imgScale = 0.1 - //创建新的图片对象 - let img = new Image(); - //指定图片的URL - img.src = this.watermarkInfo.context; - let _this = this; - //浏览器加载图片完毕后再绘制图片 - img.onload = function() { - //cxt.drawImage(img,0,0); - cxt.rotate(-20 * Math.PI / 180) - let whScale = img.width / img.height - cxt.clearRect(0, 0, canvas.width, canvas.height); - cxt.drawImage(img, //规定要使用的图像、画布或视频。 - 0, 0, //开始剪切的 x 坐标位置。 - img.width, img.height, //被剪切图像的高度。 - 0, 50,//在画布上放置图像的 x 、y坐标位置。 - img.width * imgScale, img.height * imgScale //要使用的图像的宽度、高度 - ) - - divImg.style.opacity = parseFloat(_this.watermarkInfo.transparency) / 100 - divImg.style.background = 'url(' + canvas.toDataURL('image/png') + ') left top repeat' - _this.drawImage2(divImg,dom) - } + // 图片水印画到 DOM 层(.page div),延迟到页面渲染后再画,避免此时 .page div 还未渲染导致 drawImage2 取不到节点 + this._pendingImageWatermark = true } else if (res.watermarkType === '0') { const rgbaValue = this.watermarkInfo.transparency; const startIndex = rgbaValue.indexOf('('); // 找到第一个括号的位置 @@ -195,9 +171,14 @@ export default { }) const pdfBytes = await pdfDoc.save() + this.pdfBytes = pdfBytes this.pdfDoc = await PDFJS.getDocument(pdfBytes).promise this.pdf_pages = this.pdfDoc.numPages - this.$nextTick(() => this.renderPagePdf()) + this.$nextTick(() => { + this.renderPagePdf() + // 归档章只在文档加载后调一次,不在每页渲染时重复请求 + this.getFilingsealFormat() + }) }, drawImage2 (div,dom) { div.style.pointerEvents = 'none' @@ -273,9 +254,32 @@ export default { return this.renderPagePdf(num + 1) }) } - - // 归档章数据获取操作 - this.getFilingsealFormat() + + // 图片水印延迟到第一页渲染后画,此时 .page div 已存在 + if (num === 1 && this._pendingImageWatermark) { + this._pendingImageWatermark = false + this.drawImageWatermarkToDom() + } + }, + // 图片水印画到 DOM 层(.page div),从 loadFile 延迟到此调用,解决 .page div 未渲染时画不上的竞态 + drawImageWatermarkToDom() { + if (!this.watermarkInfo || this.watermarkInfo.watermarkType !== '1') return + const dom = document.getElementsByClassName('page') + if (!dom || dom.length === 0) return + const canvas = document.createElement('canvas') + const cxt = canvas.getContext('2d') + const divImg = document.createElement('div') + const imgScale = 0.1 + const img = new Image() + img.src = this.watermarkInfo.context + img.onload = () => { + cxt.rotate(-20 * Math.PI / 180) + cxt.clearRect(0, 0, canvas.width, canvas.height) + cxt.drawImage(img, 0, 0, img.width, img.height, 0, 50, img.width * imgScale, img.height * imgScale) + divImg.style.opacity = parseFloat(this.watermarkInfo.transparency) / 100 + divImg.style.background = 'url(' + canvas.toDataURL('image/png') + ') left top repeat' + this.drawImage2(divImg, dom) + } }, getFilingsealFormat() { FetchGetFilingsealFormat().then((res) => { diff --git a/src/views/preview/index.vue b/src/views/preview/index.vue index ba727a2..decded0 100644 --- a/src/views/preview/index.vue +++ b/src/views/preview/index.vue @@ -43,7 +43,7 @@
- +
@@ -221,6 +221,49 @@ export default { this.downloading = false }) }, + // 打印入口:按文件类型分流,保证水印带入打印 + handlePrint() { + if (!this.fileCurrent || !this.fileCurrent.file_name) { + this.$message.warning('请先选择文件') + return + } + const extend = getExtend(this.fileCurrent.file_name) + if (extend.includes('pdf')) { + // PDF:水印已用 pdf-lib 打进内容,直接 iframe 打印带水印字节 + this.printPdf() + } else { + // 非 PDF:水印是 DOM 覆盖层,打印前克隆注入 printArea,打印完清理 + this.printWithWatermark() + } + }, + printPdf() { + // 用原始文件 Blob(不含 pdf-lib 打的水印)打印 + if (!this.toFile) { + this.$message.warning('PDF 尚未加载完成,请稍候') + return + } + const iframe = document.createElement('iframe') + iframe.style.cssText = 'position:absolute;width:0;height:0;border:0;' + document.body.appendChild(iframe) + const url = URL.createObjectURL(this.toFile) + iframe.src = url + iframe.onload = () => { + iframe.contentWindow.focus() + iframe.contentWindow.print() + } + const cleanup = () => { + URL.revokeObjectURL(url) + if (iframe.parentNode) iframe.parentNode.removeChild(iframe) + window.removeEventListener('afterprint', cleanup) + } + window.addEventListener('afterprint', cleanup) + }, + printWithWatermark() { + // 非 PDF:直接打印 printArea 内容,不注入水印,预览水印不受影响 + const printArea = document.getElementById('printArea') + if (!printArea) return + printJS({ printable: 'printArea', type: 'html', scanStyles: true }) + }, selectFile(item) { this.fileCurrent = item this.getBase64ByFileId() @@ -258,18 +301,13 @@ export default { } }, base64ToFile(base64String, fileName, mimeType) { - var byteCharacters = atob(base64String) - var byteArrays = [] - for (var offset = 0; offset < byteCharacters.length; offset += 512) { - var slice = byteCharacters.slice(offset, offset + 512) - var byteNumbers = new Array(slice.length) - for (var i = 0; i < slice.length; i++) { - byteNumbers[i] = slice.charCodeAt(i) - } - var byteArray = new Uint8Array(byteNumbers) - byteArrays.push(byteArray) + const binaryString = atob(base64String) + // 一次性创建 Uint8Array,避免 512 字节切片循环带来的多次内存分配 + const bytes = new Uint8Array(binaryString.length) + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i) } - var file = new Blob(byteArrays, { type: mimeType }) + const file = new Blob([bytes], { type: mimeType }) file.name = fileName return file }, @@ -436,41 +474,32 @@ export default { async handleChange(e) { try { clearTimeout(this.timer) - // const [file] = e.target.files - // console.log('file', file) - const fileReader = new FileReader() - fileReader.readAsArrayBuffer(this.toFile) - fileReader.onload = () => { - this.src = fileReader.result - } + // 直接用 readBuffer 读取 Blob,不再用 FileReader 重复读取同一文件 const arrayBuffer = await readBuffer(this.toFile) this.last = await this.displayResult(arrayBuffer, this.toFile) this.$nextTick(() => { - // 取得扩展名 const extend = getExtend(this.toFile.name) - // 加水印 - const maskDiv = document.getElementsByClassName('mask_div') - const imgMaskDiv = document.getElementsByClassName('weterbox') - - if (imgMaskDiv.length === 1) { - document.getElementsByClassName('water-mask')[0].removeChild(imgMaskDiv[0]) - } - // docx-wrapper docx - for (var j = maskDiv.length - 1; j >= 0; j--) { - document.getElementsByClassName('water-mask')[0].removeChild(maskDiv[j]) + // 切换文件时清除所有外部水印层(DOM + canvas),避免 PDF 与非 PDF 切换时水印残留 + const container = document.getElementsByClassName('water-mask')[0] + if (container) { + container.querySelectorAll('.mask_div, .weterbox, canvas.wm-canvas').forEach(el => el.remove()) } if (!extend.includes('pdf')) { + // 非 PDF:重新渲染外部水印并启动防篡改守卫 this.getWatermark() + } else { + // PDF 自带水印,停掉外部水印守卫,避免守卫把已清除的水印又恢复 + this.stopWatermarkGuard() } }) } catch (e) { console.error(e) + this.$message.error('文件预览失败') } finally { - this.timer = setTimeout(() => { - this.loading = false - }, 1000) + // 渲染完成即关闭 loading,不再固定等 1 秒 + this.loading = false } }, displayResult(buffer, file) { diff --git a/src/views/system/fileLibraryManage/processManage/index-old.vue b/src/views/system/fileLibraryManage/processManage/index-old.vue new file mode 100644 index 0000000..419b41f --- /dev/null +++ b/src/views/system/fileLibraryManage/processManage/index-old.vue @@ -0,0 +1,167 @@ + + + + +