将图表和表格导出为PDF的功能

复制代码
<template>
  <div>
    <div
      ref="pdfContent"
      style="position: relative; width: 800px; margin: 0 auto"
    >
      <!-- ECharts 图表 -->
      <div id="chart" style="width: 100%; height: 400px" />

      <!-- Element UI 表格 -->
      <el-table
        ref="myTable"
        :data="tableData"
        style="width: 100%; margin-top: 20px"
        border
        stripe
      >
        <el-table-column prop="date" label="日期" width="180" />
        <el-table-column prop="name" label="姓名" width="180" />
        <el-table-column prop="address" label="地址" />
      </el-table>
    </div>
    <button @click="exportPDF">导出PDF</button>
  </div>
</template>

<script>
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf'
import * as echarts from 'echarts'
import 'element-ui/lib/theme-chalk/index.css' // 确保引入了Element UI的样式

export default {
  data() {
    return {
      tableData: [
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: '王小虎',
          address: '上海市普陀区金沙江路 1518 弄'
        }
      ],

      tableDataeer: [
        {
          date: '2016-05-02',
          name: [
            { add: '数据', cs: '23' },
            { add: '数据1', cs: '23' }
          ],
          address: '上海市普陀区金沙江路 1518 弄'
        },
        {
          date: '2016-05-02',
          name: [
            { add: '数据1', cs: '231' },
            { add: '数据11', cs: '231' }
          ],
          address: '上海市普陀区金沙江路 1518 弄'
        }
      ]
    }
  },
  mounted() {
    this.initChart()
  },
  methods: {
    initChart() {
      const chartDom = document.getElementById('chart')
      const myChart = echarts.init(chartDom)
      const option = {
        title: {
          text: 'Referer of a Website',
          subtext: 'Fake Data',
          left: 'center'
        },
        tooltip: {
          trigger: 'item'
        },
        legend: {
          orient: 'vertical',
          left: 'right',
          overflow: 'truncate', // 超出部分截断
          ellipsis: '...' // 显示省略号
        },
        series: [
          {
            name: 'Access From',
            type: 'pie',
            radius: '50%',
            data: [
              { value: 1048, name: 'Search Engine' },
              { value: 735, name: 'Direct' },
              { value: 580, name: 'Email' },
              { value: 484, name: 'Union Ads' },
              { value: 300, name: 'Video Ads' }
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)'
              }
            }
          }
        ]
      }
      myChart.setOption(option)
    },
    exportPDF() {
      const element = this.$refs.pdfContent
      html2canvas(element).then((canvas) => {
        const imgData = canvas.toDataURL('image/png')
        // eslint-disable-next-line new-cap
        const pdf = new jsPDF({
          orientation: 'portrait',
          unit: 'pt',
          format: 'a4'
        })

        const imgProps = pdf.getImageProperties(imgData)
        const margin = 20 // 设置页边距
        const pdfWidth = pdf.internal.pageSize.getWidth() - margin * 2 // 减去左右边距
        const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width // 按比例计算高度

        let position = margin // 初始Y位置(顶部边距)
        let remainingHeight = pdfHeight // 剩余需要渲染的高度

        while (remainingHeight > 0) {
          // 当前页可用的高度(减去上下边距)
          const pageHeight = pdf.internal.pageSize.getHeight() - margin

          // 计算本次渲染的高度(不能超过页面剩余高度)
          const renderHeight = Math.min(remainingHeight, pageHeight - position)

          // 添加图像部分
          pdf.addImage(
            imgData,
            'PNG',
            margin, // x位置(左边距)
            position - (remainingHeight - pdfHeight), // y位置(考虑偏移)
            pdfWidth,
            renderHeight,
            null,
            'FAST' // 使用FAST模式提高性能
          )

          remainingHeight -= renderHeight
          position += renderHeight

          // 如果还有内容未渲染,添加新页面
          if (remainingHeight > 0) {
            pdf.addPage()
            position = margin // 新页面从顶部边距开始
          }
        }

        pdf.save('chart_and_table.pdf')
      })
    }
  }
}
</script>

<style scoped>
/* 你的样式 */
</style>

主要需要处理的问题:

  1. 内容截断问题

    • 当内容超过一页时,分页逻辑可能会导致内容被截断在页面中间

    • 特别是表格行可能会被不自然地分割在两页之间

  2. 样式完整性问题

    • Element UI表格的样式可能在PDF中显示不完整

    • ECharts图表在高分辨率导出时可能模糊

  3. 性能优化

    • 对于大量数据,html2canvas渲染可能较慢

    • PDF生成过程可能阻塞UI

  4. 布局适应性

    • 固定宽度(800px)可能在不同设备上显示不一致

    • 边距控制需要更精细

相关推荐
kyriewen17 分钟前
我让AI改一个bug——它偷偷动了5个我没让它碰的地方
前端·javascript·ai编程
慧一居士2 小时前
Element Plus 按需引入的配置使用说明和完整示例
前端·vue.js
gis开发之家4 小时前
《Vue3 从入门到大神35篇》Vue3 源码详解(五):effect 依赖收集原理——track 与 trigger 是如何工作的?
javascript·typescript·前端框架·vue3·vue3源码
橘子星6 小时前
我一个前端切图仔,凭什么能在浏览器里跑大模型?
前端·javascript·前端框架
半个落月6 小时前
用 LangChain JS 做可控写作实验:理解温度参数、提示词与异步调用
javascript·人工智能·后端
观远数据6 小时前
Excel到数据资产池:文件数据入湖的治理规范怎么建
前端·javascript·excel
CoderWeen6 小时前
我写了个能一步步点着看的 Dijkstra 可视化项目(Vue3 + Leaflet + Generator)
前端·javascript·vue.js
无人生还6 小时前
第 1 篇:从 Vue3 到 React 的思维转变
前端·vue.js·react.js
就业发动机7 小时前
我用 Tauri 做了一个约 1.5 MB 的开源屏幕标注工具:MarkerOn
vue.js·rust
爱分享的程序猿-Clark7 小时前
【前端分享】vue开发项目,如何实现 从A页面跳转到B页面,并传值!
前端·javascript·vue.js