diff --git a/package.json b/package.json index ac3a747bb..001df1d3c 100644 --- a/package.json +++ b/package.json @@ -44,10 +44,12 @@ "element-ui": "https://github.com/jumpserver-dev/element/releases/download/v2.15.15/jumpserver-element-ui-2.15.15.tgz", "eslint-plugin-html": "^6.0.0", "highlight.js": "^11.9.0", + "html2canvas": "^1.4.1", "install": "^0.13.0", "jquery": "^3.6.1", "js-cookie": "2.2.0", "jsencrypt": "^3.2.1", + "jspdf": "2.5.1", "less": "^3.10.3", "less-loader": "^5.0.0", "lodash": "^4.17.21", diff --git a/src/utils/common/pdf.js b/src/utils/common/pdf.js new file mode 100644 index 000000000..761f0b372 --- /dev/null +++ b/src/utils/common/pdf.js @@ -0,0 +1,98 @@ +import html2canvas from 'html2canvas' +import { jsPDF as JsPDF } from 'jspdf' + +export async function exportElementToPdf(element, options = {}) { + if (!element) { + throw new Error('Export element is required') + } + + const { + filename = 'report.pdf', + scale = 2, + backgroundColor = '#ffffff', + waitMs = 120, + output = 'save' + } = options + + const originStyle = { + height: element.style.height, + maxHeight: element.style.maxHeight, + overflowY: element.style.overflowY, + overflowX: element.style.overflowX + } + + try { + element.style.height = 'auto' + element.style.maxHeight = 'none' + element.style.overflowY = 'visible' + element.style.overflowX = 'visible' + element.scrollTop = 0 + + await new Promise(resolve => setTimeout(resolve, waitMs)) + + const captureWidth = element.scrollWidth || element.clientWidth + const captureHeight = element.scrollHeight || element.clientHeight + const stitchedCanvas = await html2canvas(element, { + useCORS: true, + backgroundColor, + scale, + width: captureWidth, + height: captureHeight, + windowWidth: captureWidth, + windowHeight: captureHeight, + scrollX: 0, + scrollY: 0 + }) + + const pdf = new JsPDF('p', 'mm', 'a4') + const pageWidth = pdf.internal.pageSize.getWidth() + const pageHeight = pdf.internal.pageSize.getHeight() + + const pageCanvas = window.document.createElement('canvas') + const pageCtx = pageCanvas.getContext('2d') + const pageHeightPx = Math.floor(stitchedCanvas.width * (pageHeight / pageWidth)) + + pageCanvas.width = stitchedCanvas.width + let renderedHeight = 0 + let isFirstPage = true + + while (renderedHeight < stitchedCanvas.height) { + const sliceHeight = Math.min(pageHeightPx, stitchedCanvas.height - renderedHeight) + pageCanvas.height = sliceHeight + pageCtx.clearRect(0, 0, pageCanvas.width, pageCanvas.height) + pageCtx.drawImage( + stitchedCanvas, + 0, + renderedHeight, + stitchedCanvas.width, + sliceHeight, + 0, + 0, + pageCanvas.width, + pageCanvas.height + ) + + const imgData = pageCanvas.toDataURL('image/jpeg', 0.95) + const imgHeightMm = (sliceHeight * pageWidth) / stitchedCanvas.width + + if (!isFirstPage) { + pdf.addPage() + } + pdf.addImage(imgData, 'JPEG', 0, 0, pageWidth, imgHeightMm) + + renderedHeight += sliceHeight + isFirstPage = false + } + + if (output === 'blob') { + return pdf.output('blob') + } + pdf.save(filename) + return null + } finally { + element.style.height = originStyle.height + element.style.maxHeight = originStyle.maxHeight + element.style.overflowY = originStyle.overflowY + element.style.overflowX = originStyle.overflowX + } +} diff --git a/src/views/reports/base/BaseReport.vue b/src/views/reports/base/BaseReport.vue index 9f7e71ca8..5772c9f32 100644 --- a/src/views/reports/base/BaseReport.vue +++ b/src/views/reports/base/BaseReport.vue @@ -25,7 +25,7 @@ [{{ new Date().toLocaleString() }}] -