前端PDF下载、打印界面

需求分析:界面展示的文字信息通过jsPDF、html2canvas渲染打印

html 复制代码
<template>
  <div style="padding: 16px; background: #fff">
    <div>
      <el-button type="primary" @click="downloadPDF">{{ '下载' }}</el-button>
      <el-button type="primary" v-print="printConfig">{{ '打印' }}</el-button>
    </div>
    <div id="printContent" style="padding: 10px">
      <h2 style="text-align: center">实验报告申请单</h2>
      <el-divider>{{ '基本信息' }}</el-divider>
      <el-descriptions :column="4" border>
        <!-- 第一行 -->
        <el-descriptions-item :label="'报告书编号'">{{ form.reportNo || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'发行人'">{{ form.issuerName || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'发行部门'">{{ form.issuerDept || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'发行日期'">{{ form.issueDate || '-' }}</el-descriptions-item>
        <!-- 第二行 -->
        <el-descriptions-item :label="'报告书名称'" :span="4">
          <div style="white-space: pre-wrap">{{ form.reportName || '-' }}</div>
        </el-descriptions-item>
        <!-- 第三行 -->
        <el-descriptions-item :label="'副标题'" :span="4">
          <div style="white-space: pre-wrap">{{ form.subtitle || '-' }}</div>
        </el-descriptions-item>
        <!-- 第四行 -->
        <el-descriptions-item :label="'车型'">{{ form.carModel || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'阶段'">{{ form.phase || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'关联计划书'">{{ form.planNo || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'实验执行人'">{{ form.testExecutorName || '-' }}</el-descriptions-item>
        <!-- 第五行 -->
        <el-descriptions-item :label="'抄送人'">{{ form.ccPersonName || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'实验类型'">{{ form.testType || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'实验开始时间'">{{ form.testStartTime || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'实验结束时间'">{{ form.testEndTime || '-' }}</el-descriptions-item>
        <!-- 第六和七行 -->
        <el-descriptions-item :label="'实验目的'" :span="4">
          <div style="white-space: pre-wrap; white-space: pre-line">{{ form.testPurpose || '-' }}</div>
        </el-descriptions-item>
        <!-- 第八行 -->
        <el-descriptions-item :label="'实验结论'">{{ form.testConclusion || '-' }}</el-descriptions-item>
        <el-descriptions-item :label="'其他附件'" :span="3">
          <a v-if="form.fileName !== ''" style="margin-right: 2%; cursor: pointer" @click="handleClickLook">{{ form.fileName }}</a>
          <span v-else> {{ '-' }}</span>
        </el-descriptions-item>
      </el-descriptions>
      <el-divider>{{ '车辆信息' }}</el-divider>
      <table class="print-table" border="1" cellpadding="8" cellspacing="0" width="100%">
        <thead>
          <tr style="background: #f5f7fa; text-align: left">
            <th width="120">样车名称</th>
            <th width="120">样车款型</th>
            <th width="120">车牌号</th>
            <th width="160">样车选装包信息</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(item, i) in tableConfig.tableDataList" :key="i" style="text-align: left">
            <td>{{ item.vehicleModel || '-' }}</td>
            <td>{{ item.vehicleConfig || '-' }}</td>
            <td>{{ item.licensePlate || '-' }}</td>
            <td>{{ item.optionPackInfo || '-' }}</td>
          </tr>
          <tr v-if="tableConfig.tableDataList.length === 0">
            <td colspan="6" align="center">暂无数据</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>
javascript 复制代码
<script setup>
import { reactive, onMounted, ref, watch } from 'vue'
import { flowReport } from '@workspace/api/parts/partStorage'
import { useRoute } from 'vue-router'
import { downFile } from '@workspace/api/driver.ts'
import { vPrint } from 'vue-print-next'
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'
const route = useRoute()
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import moment from 'moment'
import { ElMessage } from 'element-plus'


// 优化下载PDF
const downloadPDF = () => {
  const element = document.getElementById('printContent')
  if (!element) return
  // 可选:给个加载提示(Element Plus)
  ElMessage.success('PDF生成中,请稍候...')
  // 关键优化配置
  setTimeout(async () => {
    const canvas = await html2canvas(element, {
      scale: 1.5, // 从 2 → 1.5,清晰度足够且快很多
      logging: false,
      useCORS: true, // 解决图片跨域
      allowTaint: false,
      removeContainer: true, // 自动清理DOM
      backgroundColor: '#fff', // 强制白底,避免透明渲染慢
      onclone: (clonedDoc) => {
        // 克隆文档里:隐藏滚动条、去掉多余样式、加速渲染
        const cloneEl = clonedDoc.getElementById('printContent')
        if (cloneEl) {
          cloneEl.style.maxHeight = 'none'
          cloneEl.style.overflow = 'visible'
        }
        // 隐藏表格滚动条(非常影响速度)
        const tables = clonedDoc.querySelectorAll('.el-table')
        tables.forEach((t) => {
          t.style.overflow = 'visible'
          t.style.maxHeight = 'none'
        })
      }
    })
    // A4 适配
    const pdf = new jsPDF('portrait', 'mm', 'a4')
    const imgWidth = 210
    const imgHeight = (canvas.height * imgWidth) / canvas.width

    // 压缩图片(核心提速!)
    const imgData = canvas.toDataURL('image/jpeg', 0.85)

    // 分页处理(内容超长也不卡)
    let position = 0
    pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)

    // 内容超过一页自动分页
    while (imgHeight > pdf.internal.pageSize.height) {
      position -= pdf.internal.pageSize.height
      pdf.addPage()
      pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)
    }

    pdf.save(`${form.reportNo}${form.reportName}${moment().format('YYYY-MM-DD')}.pdf`)
    ElMessage.success('PDF下载完成')
  }, 100)
}


// 打印
const printConfig = {
  el: '#printContent', // 需要打印的元素选择器
  preview: true, // 开启打印预览(推荐,可以看到打印效果再决定是否打印)
  previewTitle: '打印预览', // 预览窗口标题
  noPrintSelector: '.no-print', // 忽略指定选择器的元素不打印
  paperSize: 'A4', // 纸张尺寸,可选 A4, Letter, Legal 等
  orientation: 'portrait', // 纸张方向: 'portrait' 纵向, 'landscape' 横向
  popTitle: '实验报告详情', // 打印页面的页眉标题
  extraCss: 'https://example.com/print.css', // 额外引入的打印样式表
  openCallback() {
    console.log('打印窗口已打开')
  },
  closeCallback() {
    console.log('打印窗口关闭')
  }
}

</script>
css 复制代码
<style>
/* 全局打印样式,确保表格永远对齐 */
@media print {
  .print-table {
    width: 100% !important;
    table-layout: fixed !important;
    border-collapse: collapse !important;
  }
  .print-table th,
  .print-table td {
    word-break: break-all !important;
    white-space: normal !important;
  }
}
</style>
<style scoped>
/* 页面展示样式 */
.print-table {
  width: 100%;
  border-collapse: collapse;
  margin: 10px 0;
}
.print-table th,
.print-table td {
  word-break: break-all;
}
</style>

这样打印的pdf文件只有一百多kb,之前打印的文件是十几M

之前的写法: 转成了图片再进行下载

javascript 复制代码
// 下载PDF
const downloadPDF = async () => {
  const element = document.getElementById('printContent')
  const canvas = await html2canvas(element, { scale: 2, logging: false, useCORS: true })
  const pdf = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' })
  const imgData = canvas.toDataURL('image/png')
  const imgWidth = 210
  const imgHeight = (canvas.height * imgWidth) / canvas.width
  pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight)
  pdf.save(`${'实验计划'}_${moment().format('YYYYMMDD')}.pdf`)
}
相关推荐
柳杉42 分钟前
Three.js × Blender:从建模到 Web 3D 的完整工作流深度解析
前端·javascript·数据可视化
reembarkation2 小时前
vue3中使用howler播放音频列表
前端·vue.js·音视频
手握风云-2 小时前
基于 Java 的网页聊天室(三)
服务器·前端·数据库
weixin199701080162 小时前
《识货商品详情页前端性能优化实战》
前端·性能优化
Forever7_2 小时前
重磅!Vue3 手势工具正式发布!免费使用!
前端·前端框架·前端工程化
用户806138166592 小时前
发布为一个 npm 包
前端·javascript
开开心心_Every2 小时前
限时免费加密、隐藏、锁定文件文件夹好工具
运维·服务器·人工智能·edge·pdf·逻辑回归·深度优先
树上有只程序猿3 小时前
低代码何时能出个“秦始皇”一统天下?我是真学不动啦!
前端·后端·低代码
TT_哲哲3 小时前
小程序双模式(文件 / 照片)上传组件封装与解析
前端·javascript
不知名的老吴3 小时前
PDF神器-PDF Candy v2.89全能PDF工具箱便携版
pdf