将图表和表格导出为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)可能在不同设备上显示不一致

    • 边距控制需要更精细

相关推荐
sen_shan32 分钟前
Vue3+Vite+TypeScript+Element Plus开发-04.静态菜单设计
前端·javascript·typescript·vue3·element·element plus·vue 动态菜单
旧识君1 小时前
移动端1px终极解决方案:Sass混合宏工程化实践
开发语言·前端·javascript·前端框架·less·sass·scss
ElasticPDF-新国产PDF编辑器1 小时前
Angular use pdf.js and Elasticpdf tutorial
javascript·pdf·angular.js
揣晓丹1 小时前
JAVA实战开源项目:校园失物招领系统(Vue+SpringBoot) 附源码
java·开发语言·vue.js·spring boot·开源
Carlos_sam1 小时前
Openlayers:海量图形渲染之图片渲染
前端·javascript
你的人类朋友2 小时前
MQTT协议是用来做什么的?此协议常用的概念有哪些?
javascript·后端·node.js
顽疲2 小时前
从零用java实现 小红书 springboot vue uniapp (11)集成AI聊天机器人
java·vue.js·spring boot·ai
美食制作家2 小时前
【无标题】Threejs第一个3D场景
javascript·three
派小汤2 小时前
Springboot + Vue + WebSocket + Notification实现消息推送功能
vue.js·spring boot·websocket
HelloRevit3 小时前
React DndKit 实现类似slack 类别、频道拖动调整位置功能
前端·javascript·react.js