Appearance
导出页面为HTML或PDF
说明
可以导出任意页面为pdf,传参为要导出父组件的一个ID或者ref
代码
bash
<template>
<div class="export-page-div">
<div class="searchBtn_position">
<el-button class="btn-add" type="primary" @click="exportToHTML">导出Html</el-button>
<el-button class="btn-add" type="primary" @click="exportToPDF">导出PDF</el-button>
</div>
</div>
</template>
<script>
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
export default {
name: 'ExportPage',
props: {
pageRef: {
type: HTMLDivElement,
required: true,
},
elementId: {
type: String,
// required: true,
default: '',
},
fileName: {
type: String,
required: true,
default: 'exported_page',
},
},
methods: {
exportToPDF () {
const loading = this.$loading({
lock: true,
text: '正在导出PDF...',
background: 'rgba(0, 0, 0, 0.7)',
});
const element = document.getElementById(this.elementId);
// const element = this.pageRef;
if (!element) {
console.error('Element not found');
loading.close();
return;
}
// 临时改变元素的样式以确保元素完全可见
const originalStyle = {
width: element.style.width,
height: element.style.height,
overflow: element.style.overflow,
};
element.style.width = '100%';
element.style.height = `${element.scrollHeight}px`;
element.style.overflow = 'visible';
html2canvas(element, {
scale: 2, // 提高图像质量
useCORS: true, // 处理跨域问题
scrollY: 0, // 确保从元素顶部开始捕获
scrollX: 0, // 确保从元素左侧开始捕获
width: element.scrollWidth,
height: element.scrollHeight,
}).then(canvas => {
// 恢复元素原始样式
element.style.width = originalStyle.width;
element.style.height = originalStyle.height;
element.style.overflow = originalStyle.overflow;
const imgData = canvas.toDataURL('image/png');
const imgWidth = canvas.width / 2;
const imgHeight = canvas.height / 2;
const pdf = new jsPDF({
orientation: imgWidth > imgHeight ? 'landscape' : 'portrait',
unit: 'pt',
format: [imgWidth, imgHeight],
});
const pageHeight = pdf.internal.pageSize.getHeight();
const pageWidth = pdf.internal.pageSize.getWidth();
const ratio = imgWidth / imgHeight;
let heightLeft = imgHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, pageWidth, pageWidth / ratio);
heightLeft -= pageHeight;
while (heightLeft > 0) {
pdf.addPage();
position = 0;
pdf.addImage(imgData, 'PNG', 0, position, pageWidth, pageWidth / ratio);
heightLeft -= pageHeight;
}
pdf.save(`${this.fileName}.pdf`);
loading.close();
}).catch(error => {
console.error('Error generating PDF: ', error);
loading.close();
// 恢复元素原始样式
element.style.width = originalStyle.width;
element.style.height = originalStyle.height;
element.style.overflow = originalStyle.overflow;
});
},
exportToHTML () {
const loading = this.$loading({
lock: true,
text: '正在导出...',
background: 'rgba(0, 0, 0, 0.7)',
});
this.getHtmlContent().then((htmlContent) => {
const blob = new Blob([htmlContent], { type: 'text/html' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `${this.fileName}.html`;
link.click();
URL.revokeObjectURL(link.href);
loading.close();
}).catch((error) => {
console.log();
});
},
async getHtmlContent () {
// const element = this.pageRef;
const element = document.getElementById(this.elementId);
if (!element) {
console.error('Element not found');
return;
}
try {
const clonedElement = element.cloneNode(true);
await this.replaceImagesAndSVGsWithBase64(clonedElement);
await this.replaceCanvasWithImages(element, clonedElement);
this.copyInlineStyles(element, clonedElement);
const htmlContent = clonedElement.outerHTML;
const styles = this.getStyles();
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${this.fileName}</title>
<style>${styles}</style>
</head>
<body>
${htmlContent}
</body>
</html>
`;
} catch (error) {
console.error('Error generating HTML: ', error);
}
},
getStyles () {
let styles = '';
for (const sheet of document.styleSheets) {
try {
if (sheet.cssRules) {
for (const rule of sheet.cssRules) {
styles += rule.cssText;
}
}
} catch (e) {
console.error('Error accessing stylesheet rules: ', e);
}
}
return styles;
},
async replaceImagesAndSVGsWithBase64 (element) {
const images = element.querySelectorAll('img');
for (const img of images) {
img.src = await this.getImageBase64(img.src);
}
const svgs = element.querySelectorAll('svg');
for (const svg of svgs) {
svg.outerHTML = new XMLSerializer().serializeToString(svg);
}
},
async replaceCanvasWithImages (source, target) {
const sourceCanvases = source.querySelectorAll('canvas');
const targetCanvases = target.querySelectorAll('canvas');
for (let i = 0; i < sourceCanvases.length; i++) {
const sourceCanvas = sourceCanvases[i];
const targetCanvas = targetCanvases[i];
if (targetCanvas) {
const img = new Image();
img.src = sourceCanvas.toDataURL('image/png', 1.0);
await img.decode();
const imgElement = document.createElement('img');
imgElement.src = img.src;
targetCanvas.replaceWith(imgElement);
} else {
console.error('Target canvas not found at index:', i);
}
}
},
getImageBase64 (url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
resolve(canvas.toDataURL('image/png', 1.0));
};
img.onerror = () => reject(new Error('Failed to convert image to Base64'));
img.src = url;
});
},
copyInlineStyles (source, target) {
const sourceNodes = source.querySelectorAll('*');
const targetNodes = target.querySelectorAll('*');
sourceNodes.forEach((sourceNode, index) => {
const targetNode = targetNodes[index];
const computedStyle = window.getComputedStyle(sourceNode);
for (const key of computedStyle) {
targetNode.style[key] = computedStyle[key];
}
});
},
},
};
</script>
<style scoped>
</style>