主要使用html2canvas+jspdf
1.將前端頁面導出為pdf
2.處理導出后圖表的截斷問題
export default function AIReport() {const handleExport = async () => {try {// 需要導出的內容idconst element = document.querySelector('#AI-REPORT-CONTAINER');if (!element) {message.error('未找到要導出的內容');return;}message.loading({ content: '正在導出PDF...', key: 'export' });// 使用html2canvas生成整個報告的畫布const canvas = await html2canvas(element as HTMLElement, {scale: 2, // 提高清晰度useCORS: true,allowTaint: true,backgroundColor: '#ffffff',logging: false,});// 創建PDF對象const pdf = new jsPDF({orientation: 'p', // 縱向unit: 'pt', // 使用點作為單位format: 'a4', // A4紙張});// 獲取A4頁面尺寸const a4Width = pdf.internal.pageSize.getWidth();const a4Height = pdf.internal.pageSize.getHeight();// 計算等比例下A4高度對應的canvas高度const a4HeightInCanvas = Math.floor((canvas.width / a4Width) * a4Height);// 獲取canvas的總高度let leftHeight = canvas.height;// PDF頁面偏移量let position = 0;// 創建臨時canvas用于分頁const tempCanvas = document.createElement('canvas');const ctx = tempCanvas.getContext('2d');// 遞歸處理每一頁const processNextPage = () => {if (leftHeight <= 0) {// 完成所有頁面處理,保存并下載PDFconst pdfOutput = pdf.output('blob');const url = URL.createObjectURL(pdfOutput);const link = document.createElement('a');link.href = url;link.download = `AI基金報告_${data?.title || '未命名'}_${data?.date || ''}.pdf`;document.body.appendChild(link);link.click();document.body.removeChild(link);URL.revokeObjectURL(url);message.success({ content: 'PDF導出成功', key: 'export' });return;}// 計算當前頁的高度let currentPageHeight;if (leftHeight > a4HeightInCanvas) {// 需要尋找合適的分頁點let cutPoint = position + a4HeightInCanvas;let isFound = false;// 向上搜索連續的空白行作為分頁點let checkCount = 0;for (let y = position + a4HeightInCanvas; y >= position; y--) {let isBlankLine = true;// 檢查這一行的像素是否全為白色for (let x = 0; x < canvas.width; x += 10) {// 每10個像素采樣一次提高性能const pixelData = canvas?.getContext('2d')?.getImageData(x, y, 1, 1).data;// 檢查像素是否接近白色(允許一些誤差)if (pixelData?.[0] < 250 ||pixelData?.[1] < 250 ||pixelData?.[2] < 250) {isBlankLine = false;break;}}if (isBlankLine) {checkCount++;// 找到連續10行空白,確定為分頁點if (checkCount >= 10) {cutPoint = y;isFound = true;break;}} else {checkCount = 0;}}// 如果沒找到合適的分頁點,就使用默認值currentPageHeight = isFound? Math.round(cutPoint - position): Math.min(leftHeight, a4HeightInCanvas);// 確保高度不為0if (currentPageHeight <= 0) {currentPageHeight = a4HeightInCanvas;}} else {// 最后一頁,直接使用剩余高度currentPageHeight = leftHeight;}// 設置臨時canvas的尺寸tempCanvas.width = canvas.width;tempCanvas.height = currentPageHeight;// 將原canvas對應部分繪制到臨時canvasctx?.drawImage(canvas,0,position,canvas.width,currentPageHeight,0,0,canvas.width,currentPageHeight,);// 從第二頁開始添加新頁面if (position > 0) {pdf.addPage();}// 將當前頁添加到PDFpdf.addImage(tempCanvas.toDataURL('image/jpeg', 1.0),'JPEG',0,0,a4Width,(a4Width / tempCanvas.width) * currentPageHeight,);// 更新剩余高度和位置leftHeight -= currentPageHeight;position += currentPageHeight;// 處理下一頁setTimeout(processNextPage, 100);};// 開始處理頁面processNextPage();} catch {message.error({ content: '導出PDF失敗,請稍后重試', key: 'export' });}};return (<Spin spinning={loading} wrapperClassName={styles.spinWrapper}><div className={styles.exportBtn}><Button type="primary" onClick={handleExport}>導出PDF</Button></div><div className={styles.container} id="AI-REPORT-CONTAINER">這里為需要導出的頁面內容,table,echart等</div></Spin>);
}