vue3 + jspdf + echarts 前端生成 pdf报告 预览+下载

一、导包

1、导入

yarn add jspdf
yarn add jspdf-autotable

2、界面引入

import jsPDF from 'jspdf';
// 表格插件,这次我这边没用到, 根据需求选择
import 'jspdf-autotable';

3、创建pdf对象

// 创建一个新的PDF文档实例
  // 定义页面边距
  const PAGE_MARGIN = 10;
  const doc = new jsPDF({
    unit: 'mm', // 单位,本示例为mm
    format: 'a4', // 页面大小
    orientation: 'portrait', // 页面方向,portrait: 纵向,landscape: 横向
    putOnlyUsedFonts: true, // 只包含使用的字体
    compress: true, // 压缩文档
    precision: 16 // 浮点数的精度
  });
  // 设置第一页内容
  doc.setPage(1);
  // 设置字体,第二个参数为fontStyle,引入的字体是什么fontStyle就设置成什么,如果这里要使用blod粗体,则需要再引入转换后的粗体字体文件
  doc.setFont('SourceHanSerifCN-Regular', 'normal');

  // 添加第一页内容
  doc.addImage(getAssetsFile('images/xfaq.png'), 'JPEG', 13, 10, 183, 50);
  // 添加标题
  doc.setFontSize(20); // 调整为适合你的字体大小

二、引入中文字体

1、下载思源黑体字体
链接: 下载地址
2、转换字体,打开jspdf提供的在线字体转化网站:
链接: 转换地址
不同的字体样式,选择不同的fontStyle
转换完毕后:
字体js
3、引入转换后字体文件

// 引入转换后字体文件
import '@/assets/fonts/SourceHanSerifCN-Regular-normal.js';
import '@/assets/fonts/SourceHanSerifCN-SemiBold-bold.js';

4、界面使用

// 设置字体,第二个参数为fontStyle,引入的字体是什么fontStyle就设置成什么,
// 如果这里要使用blod粗体,则需要再引入转换后的粗体字体文件
doc.setFont('SourceHanSerifCN-Regular', 'normal');
doc.setFont('SourceHanSerifCN-SemiBold', 'bold'); // 请确保已经引入了粗体字体

三、引入echarts图表

对于pdf中图表来说,目前采用echarts图表方式比较合适,
注意事项:
1、多图表渲染比较耗时, 其中要解决同步异步的问题, 采用 await 方式解决
2、需要等到上一个页面的图表渲染完毕后,才能进入到下一页的内容生成, 否则会出现图表错页的问题

1、option 数据

let onePdfEchartsCfg = ref([
  {
    option: {
      title: [
        {
          text: '设备总计',
          x: '200', // 这里将 x 的值调整为 '20'
          y: '0', // 这里将 y 的值调整为 '20'
          textStyle: {
            fontSize: 20,
            fontWeight: 'bold'
          }
        },
        {
          text: deviceTypeSumDataTotal.value,
          subtext: '设备总数',
          x: 'center',
          y: 'center',
          textStyle: {
            fontSize: 16,
            fontWeight: 'bold'
          }
        }
      ],
      tooltip: {
        trigger: 'item',
        formatter: '{a} <br/>{b}: {c} ({d}%)'
      },
      legend: {
        top: 'middle',
        right: '5%',
        orient: 'vertical',
        icon: 'circle',
        formatter: function (name) {
          const seriesData = deviceTypeSumData.value;
          const total = seriesData.reduce((acc, cur) => acc + cur.value, 0);
          const dataIndex = seriesData.findIndex(item => item.name === name);
          const value = seriesData[dataIndex].value;
          // 添加条件判断,避免除法错误
          const percentage = total !== 0 ? ((value / total) * 100).toFixed(0) : 0;
          return `${name} ${value} ${percentage}%`;
        }
      },
      series: [
        {
          name: 'Access From',
          type: 'pie',
          radius: ['40%', '70%'],
          avoidLabelOverlap: false,
          label: {
            show: false,
            position: 'center'
          },
          emphasis: {
            label: {
              show: true,
              fontSize: 40,
              fontWeight: 'bold'
            }
          },
          labelLine: {
            show: false
          },
          data: deviceTypeSumData.value
        }
      ]
    },
    x: -20,
    y: 135,
    width: 120,
    height: 50,
    data: deviceTypeSumData.value
  },
  {
    option: {
      title: [
        {
          text: '设备在线率',
          x: '200', // 这里将 x 的值调整为 '20'
          y: '0', // 这里将 y 的值调整为 '20'
          textStyle: {
            fontSize: 20,
            fontWeight: 'bold'
          }
        },
        {
          text: deviceTypeSumDataTotal.value,
          subtext: '设备总数',
          x: 'center',
          y: 'center',
          textStyle: {
            fontSize: 16,
            fontWeight: 'bold'
          }
        }
      ],
      tooltip: {
        trigger: 'item',
        formatter: '{a} <br/>{b}: {c} ({d}%)'
      },
      legend: {
        top: 'middle',
        right: '5%',
        orient: 'vertical',
        icon: 'circle',
        formatter: function (name) {
          const seriesData = deviceOnlineData.value;
          const total = seriesData.reduce((acc, cur) => acc + cur.value, 0);
          const dataIndex = seriesData.findIndex(item => item.name === name);
          const value = seriesData[dataIndex].value;
          const percentage = ((value / total) * 100).toFixed(0);
          return `${name} ${value} ${percentage}%`;
        }
      },
      series: [
        {
          name: 'Access From',
          type: 'pie',
          radius: ['40%', '70%'],
          avoidLabelOverlap: false,
          label: {
            show: false,
            position: 'center'
          },
          emphasis: {
            label: {
              show: true,
              fontSize: 40,
              fontWeight: 'bold'
            }
          },
          labelLine: {
            show: false
          },
          data: deviceOnlineData.value
        }
      ]
    },
    x: 80,
    y: 135,
    width: 120,
    height: 50
  }
]);

2、添加图表

 // 在第二页添加echarts图表
  onePdfEchartsCfg.value.forEach(async ({ option, x, y, width, height, data }, index) => {
    // 插入图表图片到 jsPDF 文档
    await generateOnePDF(option, doc, x, y, width, height, data, index);
  });

3、图表方法

function generateOnePDF(option, doc, x, y, width, height, data, index) {
  // 创建一个包含 ECharts 图表的 div 元素
  const chartContainer = document.createElement('div');
  chartContainer.style.width = '700px';
  chartContainer.style.height = '300px';
  chartContainer.style.marginLeft = '50px'; // 设置左边距
  document.body.appendChild(chartContainer);

  // 使用 ECharts 在 chartContainer 中生成图表
  const chart = echarts.init(chartContainer);
  chart.setOption(option);

  // 监听图表渲染完成事件
  chart.on('finished', () => {
    // 获取 ECharts 图表的数据 URL
    const dataURL = chart.getDataURL({ type: 'png' });
    // 移除图表容器
    document.body.removeChild(chartContainer);
    // 将图表数据 URL 转换为图像
    const img = new Image();
    img.src = dataURL;

	// 指定图表的页码
    doc.setPage(2);
    // 在 PDF 中添加图表图像
    doc.addImage(img, 'JPEG', x, y, width, height);

    // 如果是最后一个图表,生成下一页, 解决图表错页的问题
    if (index === onePdfEchartsCfg.value.length - 1) {
      generateTwoPage(doc);
    }
  });
}



四、完整代码

目前缺少后端接口支撑, 前端先mock一些数据展示, 代码还有很多优化的空间,后续抽时间再调整一波, 有问题或者好的建议,随时评论哈

const mockData = ref({
  '11月': {
    oneTitleText: 'XXXXX月报',
    oneDateText: '2023年11月01日-2023年11月30日',
    oneCompanyNameContent: 'XXXXX公司',
    oneBottomDateText: '2023年11月30日',
    overviewModule: {
      allDevice: '3',
      addDevice: '3',
      allCompany: '3',
      addCompany: '3',
      addsDevice: '3',
      addsCompany: '3',
      addAlarm: '0',
      stopAlarm: '0',
      stopAlarmTime: '0',
      waitAlarm: '0',
      trueFireAlarm: '0',
      addWarnDevice: '0',
      stopWarnDevice: '0',
      waitWarnDevice: '0',
      deviceOnlineRate: '0',
      onlineDevice: '0',
      offlineDevice: '0'
    },
    fireDeviceModule: {
      // 缺图表的数据
    },
    alarmModule: {
      // 缺图表的数据
    },
    faultModule: {
      // 缺图表的数据
    }
  },
  '10月': {
    oneTitleText: 'XXXXX月报',
    oneDateText: '2023年10月01日-2023年10月31日',
    oneCompanyNameContent: 'XXXXX公司',
    oneBottomDateText: '2023年10月31日',
    overviewModule: {
      allDevice: '3',
      addDevice: '3',
      allCompany: '3',
      addCompany: '3',
      addsDevice: '3',
      addsCompany: '3',
      addAlarm: '0',
      stopAlarm: '0',
      stopAlarmTime: '0',
      waitAlarm: '0',
      trueFireAlarm: '0',
      addWarnDevice: '0',
      stopWarnDevice: '0',
      waitWarnDevice: '0',
      deviceOnlineRate: '0',
      onlineDevice: '0',
      offlineDevice: '0'
    },
    fireDeviceModule: {
      // 缺图表的数据
    },
    alarmModule: {
      // 缺图表的数据
    },
    faultModule: {
      // 缺图表的数据
    }
  },
  '2022年': {
    oneTitleText: 'XXXXX年报',
    oneDateText: '2022年01月01日-2022年12月31日',
    oneCompanyNameContent: 'XXXXX公司',
    oneBottomDateText: '2022年12月31日',
    overviewModule: {
      allDevice: '3',
      addDevice: '3',
      allCompany: '3',
      addCompany: '3',
      addsDevice: '3',
      addsCompany: '3',
      addAlarm: '0',
      stopAlarm: '0',
      stopAlarmTime: '0',
      waitAlarm: '0',
      trueFireAlarm: '0',
      addWarnDevice: '0',
      stopWarnDevice: '0',
      waitWarnDevice: '0',
      deviceOnlineRate: '0',
      onlineDevice: '0',
      offlineDevice: '0'
    },
    fireDeviceModule: {
      // 缺图表的数据
    },
    alarmModule: {
      // 缺图表的数据
    },
    faultModule: {
      // 缺图表的数据
    }
  }
});

// 预览报告
const previewBt = o => {
  console.info(o);
  let name = '';
  if (o.name.includes('月')) {
    name = '运营商运营消防安全月报-2023年' + o.name;
  } else {
    name = '运营商运营消防安全年报-' + o.name;
  }
  // 缺少去后台拉取数据接口 模拟
  pdfData = mockData.value[o.name];
  const doc = productReport('preview', name);
};

// 下载报告
const downloadBt = o => {
  console.info(o);
  let name = '';
  if (o.name.includes('月')) {
    name = '运营商运营消防安全月报-2023年' + o.name;
  } else {
    name = '运营商运营消防安全年报-' + o.name;
  }
  // 缺少去后台拉取数据接口
  pdfData = mockData.value[o.name];
  const doc = productReport('download', name);
};

function productReport(type, name) {
  // 显示加载动画
  const loading = ElLoading.service({
    lock: true,
    text: '正在生成报告',
    background: 'rgba(0, 0, 0, 0.7)'
  });
  // 创建一个新的PDF文档实例
  // 定义页面边距
  const PAGE_MARGIN = 10;
  const doc = new jsPDF({
    unit: 'mm', // 单位,本示例为mm
    format: 'a4', // 页面大小
    orientation: 'portrait', // 页面方向,portrait: 纵向,landscape: 横向
    putOnlyUsedFonts: true, // 只包含使用的字体
    compress: true, // 压缩文档
    precision: 16 // 浮点数的精度
  });
  // 设置第一页内容
  doc.setPage(1);
  // 设置字体,第二个参数为fontStyle,引入的字体是什么fontStyle就设置成什么,如果这里要使用blod粗体,则需要再引入转换后的粗体字体文件
  doc.setFont('SourceHanSerifCN-Regular', 'normal');

  // 添加第一页内容
  doc.addImage(getAssetsFile('images/xfaq.png'), 'JPEG', 13, 10, 183, 50);
  // 添加标题
  doc.setFontSize(20); // 调整为适合你的字体大小
  const textWidth =
    (doc.getStringUnitWidth(pdfData.oneTitleText) * doc.internal.getFontSize()) / doc.internal.scaleFactor;
  const textX = (doc.internal.pageSize.width - textWidth) / 2;
  const textY = 80; // 调整为适合你的位置
  doc.text(pdfData.oneTitleText, textX, textY);
  // 添加日期
  const dateFontSize = 8;
  doc.setFontSize(dateFontSize);
  const dateWidth = (doc.getStringUnitWidth(pdfData.oneDateText) * dateFontSize) / doc.internal.scaleFactor;
  const dateX = (doc.internal.pageSize.width - dateWidth) / 2;
  const dateY = textY + doc.getTextDimensions(pdfData.oneTitleText).h + 5;
  doc.text(pdfData.oneDateText, dateX, dateY);

  // 计算 "值守商名称:" 文本宽度
  const companyNameLabel = '值守商名称:';
  const companyNameLabelFontSize = 12;
  const companyNameLabelWidth =
    (doc.getStringUnitWidth(companyNameLabel) * companyNameLabelFontSize) / doc.internal.scaleFactor;

  // 计算  文本宽度
  const companyNameContentFontSize = 12;
  const companyNameContentWidth =
    (doc.getStringUnitWidth(pdfData.oneCompanyNameContent) * companyNameContentFontSize) / doc.internal.scaleFactor;
  // 计算文本总宽度
  const totalWidth = Math.max(companyNameLabelWidth, companyNameContentWidth);
  // 计算居中位置
  const center = (doc.internal.pageSize.width - totalWidth) / 2;

  // 添加 "值守商名称:" 文本
  doc.setFontSize(companyNameLabelFontSize);
  doc.text(companyNameLabel, center - 10, dateY + doc.getTextDimensions(pdfData.oneDateText).h + 50);
  // 计算  的位置
  const companyNameContentY = dateY + doc.getTextDimensions(pdfData.oneDateText).h + 50;
  // 添加 文本
  doc.setFontSize(companyNameContentFontSize);
  doc.text(pdfData.oneCompanyNameContent, center + 20, companyNameContentY);
  // 添加下方的横线
  doc.line(90, companyNameContentY + 2, doc.internal.pageSize.width - 50, companyNameContentY + 2);

  // 添加编制人员
  const authorText = '编制人员:';
  doc.text(authorText, center - 10, 152);
  doc.line(90, 152 + 2, doc.internal.pageSize.width - 50, 152 + 2);

  // 添加审核人员
  const reviewerText = '审核人员:';
  doc.text(reviewerText, center - 10, 161);
  doc.line(90, 161 + 2, doc.internal.pageSize.width - 50, 161 + 2);

  // 添加底部日期
  const bottomDateFontSize = 12;
  const bottomDateWidth =
    (doc.getStringUnitWidth(pdfData.oneBottomDateText) * bottomDateFontSize) / doc.internal.scaleFactor;
  // 计算底部日期文本居中位置
  const bottomDateX = (doc.internal.pageSize.width - bottomDateWidth) / 2;
  doc.text(pdfData.oneBottomDateText, bottomDateX, 270);

  // 新建第二页
  doc.addPage();
  // 设置第二页内容
  doc.setPage(2);

  doc.text(pdfData.oneTitleText, dateX, 10);
  doc.line(10, 10 + 5, doc.internal.pageSize.width - 10, 10 + 5);

  // 在第二页添加标题图标
  const titleIconX = 10;
  const titleIconY = 30;
  const titleIconWidth = 5;
  const titleIconHeight = 5;
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', titleIconX, titleIconY, titleIconWidth, titleIconHeight);

  // 在第二页添加标题文本
  const titleText1 = '运营总览';
  doc.setFontSize(12);
  doc.setFont('SourceHanSerifCN-SemiBold', 'bold'); // 请确保已经引入了粗体字体
  doc.text(titleText1, 16, 34);
  doc.setFont('SourceHanSerifCN-Regular', 'normal'); // 恢复正常字体

  doc.setFontSize(10.5);
  doc.text(
    '本月新增设备接入' +
      pdfData.overviewModule.addDevice +
      '个,新增单位接入' +
      pdfData.overviewModule.addCompany +
      '家,截止' +
      pdfData.oneBottomDateText +
      ',累计接入设备数' +
      pdfData.overviewModule.addsDevice +
      '个,单位数' +
      pdfData.overviewModule.addsCompany +
      '家;',
    14,
    40
  );
  doc.text(
    '本月平台接收到报警' +
      pdfData.overviewModule.addAlarm +
      '个,已处理' +
      pdfData.overviewModule.stopAlarm +
      '个,遗留未处理' +
      pdfData.overviewModule.waitAlarm +
      '个,发生真实火警' +
      pdfData.overviewModule.trueFireAlarm +
      '起;',
    14,
    45
  );
  doc.text(
    '本月平台接收到设备故障' +
      pdfData.overviewModule.addWarnDevice +
      '个,已处理' +
      pdfData.overviewModule.stopWarnDevice +
      '个,遗留未处理' +
      pdfData.overviewModule.waitWarnDevice +
      '个;',
    14,
    50
  );
  doc.text(
    '平台设备在线率为' +
      pdfData.overviewModule.deviceOnlineRate +
      '%,截止' +
      pdfData.oneBottomDateText +
      ',有' +
      pdfData.overviewModule.offlineDevice +
      '个设备处于离线状态;',
    14,
    55
  );

  // 绘制矩形 x y width height
  const backgroundColor = [247, 247, 247]; // 灰色背景色 RGB
  doc.setFillColor.apply(doc, backgroundColor);
  doc.rect(10, 60, 80, 20, 'F');

  // 在矩形内添加文本
  doc.setTextColor(0, 0, 0); // 设置文本颜色为黑色
  doc.setFontSize(12);
  doc.text('资源接入统计', 15, 65);
  doc.setFontSize(10.5);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 67.5, 4, 4);
  doc.text(
    '设备数 ' +
      pdfData.overviewModule.allDevice +
      ' (在线率:' +
      pdfData.overviewModule.deviceOnlineRate +
      '%) 新增 ' +
      pdfData.overviewModule.addDevice +
      '',
    20,
    71
  );
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 72.5, 4, 4);
  doc.text('单位数 ' + pdfData.overviewModule.allCompany + ' 新增 ' + pdfData.overviewModule.addCompany + '', 20, 76);

  // 绘制矩形 x y width height
  doc.setFillColor.apply(doc, backgroundColor);
  doc.rect(100, 60, 80, 20, 'F');

  // 在矩形内添加文本
  doc.setTextColor(0, 0, 0); // 设置文本颜色为黑色
  doc.setFontSize(12);
  doc.text('报警处理', 105, 65);
  doc.setFontSize(10.5);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 105, 67.5, 4, 4);
  doc.text(
    '报警总数 ' + pdfData.overviewModule.addAlarm + ' 真实火警 ' + pdfData.overviewModule.trueFireAlarm + '',
    110,
    71
  );
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 105, 72.5, 4, 4);
  doc.text('遗留未处理 ' + pdfData.overviewModule.waitAlarm + '', 110, 76);

  // 绘制矩形 x y width height
  doc.setFillColor.apply(doc, backgroundColor);
  doc.rect(10, 85, 80, 20, 'F');

  // 在矩形内添加文本
  doc.setTextColor(0, 0, 0); // 设置文本颜色为黑色
  doc.setFontSize(12);
  doc.text('故障处理', 15, 90);
  doc.setFontSize(10.5);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 92.5, 4, 4);
  doc.text('故障总数 ' + pdfData.overviewModule.addWarnDevice + '', 20, 96);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 97.5, 4, 4);
  doc.text('遗留未处理 ' + pdfData.overviewModule.waitWarnDevice + '', 20, 101);

  // 在第二页添加标题图标
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 10, 110, 5, 5);

  // 在第二页添加标题文本
  doc.setFontSize(12);
  doc.setFont('SourceHanSerifCN-SemiBold', 'bold'); // 请确保已经引入了粗体字体
  doc.text('消防设备统计', 16, 114);
  doc.setFont('SourceHanSerifCN-Regular', 'normal'); // 恢复正常字体

  doc.setFontSize(10.5);
  doc.text(
    '本月新增设备接入' +
      pdfData.overviewModule.addDevice +
      '个,截止' +
      pdfData.oneBottomDateText +
      ',累计接入设备数' +
      pdfData.overviewModule.allDevice +
      '个',
    14,
    120
  );
  doc.text('设备品类涵盖独立式烟温感系统,电气火灾系统', 14, 125);
  doc.text(
    '平台设备在线率为' +
      pdfData.overviewModule.deviceOnlineRate +
      '%,其中独立式烟温感系统离线率最高,截止' +
      pdfData.oneBottomDateText +
      ',有' +
      pdfData.overviewModule.offlineDevice +
      '个设备处于离线状态',
    14,
    130
  );

  // 绘制矩形 x y width height
  doc.setFillColor.apply(doc, backgroundColor);
  doc.rect(10, 185, 190, 40, 'F');

  // 在矩形内添加文本
  doc.setTextColor(0, 0, 0); // 设置文本颜色为黑色
  doc.setFontSize(12);
  doc.text(
    '设备在线率:' +
      pdfData.overviewModule.deviceOnlineRate +
      '%(在线数/总数:' +
      pdfData.overviewModule.onlineDevice +
      '/' +
      pdfData.overviewModule.allDevice +
      ')',
    15,
    190
  );
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 196.5, 4, 4);
  doc.setFontSize(10.5);
  doc.text('防排烟系统 0% (0/0)', 20, 200);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 201.5, 4, 4);
  doc.text('独立式烟温感系统 0% (0/1)', 20, 205);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 15, 206.5, 4, 4);
  doc.text('视频监控系统 0% (0/0)', 20, 210);

  // 在矩形内添加文本
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 80, 196.5, 4, 4);
  doc.setFontSize(10.5);
  doc.text('可燃气体系统 0% (0/0)', 85, 200);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 80, 201.5, 4, 4);
  doc.text('电气火灾系统 0% (0/2)', 85, 205);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 80, 206.5, 4, 4);
  doc.text('消防用水系统 0% (0/0)', 85, 210);

  // 在矩形内添加文本
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 145, 196.5, 4, 4);
  doc.setFontSize(10.5);
  doc.text('火灾报警系统 0% (0/0)', 150, 200);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 145, 201.5, 4, 4);
  doc.text('充电桩系统 0% (0/0)', 150, 205);
  doc.addImage(getAssetsFile('images/overview.png'), 'PNG', 145, 206.5, 4, 4);
  doc.text('其他系统 0% (0/0)', 150, 210);

  // 在第二页添加echarts图表
  onePdfEchartsCfg.value.forEach(async ({ option, x, y, width, height, data }, index) => {
    // 插入图表图片到 jsPDF 文档
    await generateOnePDF(option, doc, x, y, width, height, data, index);
  });

  // 延迟两秒后获取图表数据
  setTimeout(() => {
    loading.close();
    if (type === 'download') {
      doc.save(name + '.pdf');
      return doc;
    } else {
      // 将生成的 PDF 转换为数据 URL
      const dataURL = doc.output('dataurl', { filename: name + '.pdf' });
      // 创建一个新窗口进行预览
      debugger;
      const previewWindow = window.open();
      previewWindow.document.write(`<iframe width='100%' height='100%' src='${dataURL}'></iframe>`);
      return doc;
    }
  }, 5000); // 2000 毫秒即 2 秒
}

function generateOnePDF(option, doc, x, y, width, height, data, index) {
  // 创建一个包含 ECharts 图表的 div 元素
  const chartContainer = document.createElement('div');
  chartContainer.style.width = '700px';
  chartContainer.style.height = '300px';
  chartContainer.style.marginLeft = '50px'; // 设置左边距
  document.body.appendChild(chartContainer);

  // 使用 ECharts 在 chartContainer 中生成图表
  const chart = echarts.init(chartContainer);
  chart.setOption(option);

  // 监听图表渲染完成事件
  chart.on('finished', () => {
    // 延迟两秒后获取图表数据
    // 获取 ECharts 图表的数据 URL
    const dataURL = chart.getDataURL({ type: 'png' });

    // 移除图表容器
    document.body.removeChild(chartContainer);

    // 将图表数据 URL 转换为图像
    const img = new Image();
    img.src = dataURL;

    doc.setPage(2);
    // 在 PDF 中添加图表图像
    doc.addImage(img, 'JPEG', x, y, width, height);

    // 如果是最后一个图表,生成下一页, 解决图表错页的问题
    if (index === onePdfEchartsCfg.value.length - 1) {
      generateTwoPage(doc);
    }
  });
}
  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值