【总结记录】Superset中dashboard图表进行html的编辑并转为pdf下载

需求:使用superset搭建的BI项目,需要在dashboard模块中,可以编辑所选的图表,并且将html下载为pdf格式的文件。BI页面展示大体如下:

 功能分析

1.首先,我们需要一个预览功能,那就点击右上角三个点的按钮,弹出弹窗展示范围内的图表即可,这个实现的话主要就是把图表拿过来的过程,这个具体看代码如何实现;

2.然后我们需要在弹窗对html进行编辑操作,我们看到可操作性的不知图表还有text、table以及input输入框等内容,我们需要一个专业的插件的开完成,于是我们找到了jodit-react插件(因为代码使用了react框架,所以我们选择这个);

3.将修改后的页面保存并且下载为pdf格式,网上百度了一圈,我只找到两个插件,但实现的原理都是一样,我们选择大家基本都在用的插件,html2canvas和jspdf来实现。

 实现过程以及遇到的问题

1.实现预览

我们需要先拿到这个需要展示的部分,看到页面上有一个下载图表为image的功能,直接操作了dom元素去拿页面,并且考虑到,使用jodit的时候需要传入的是string格式的html,所以,我们也直接去操作它的dom吧(不推荐,但是如过获取组件数据太麻烦。。);

那么我们就遇到了第一个问题,右边menu菜单,再打开弹窗的时候出现的延迟,预览的画面展示出了menu,这时候我们用了cloneNode,并删掉了对应的节点,所以现在我们展示的就是克隆后的节点html:

然后我们又遇到了canvas实现的图表无法正常显示的问题,我想到的方法就是toDataURL方法拿到url,并替换canvas为img,将url赋值给src属性,下面是具体的方法,我是用递归,因为可能下载的是一整页,就有很多canvas标签的情况,如下:

2.下面我们对预览的内容进行修改

之后也得到了html,这个是克隆并处理过的数据,我们用这个数据直接去进行下载pdf,网上的代码基本如下所示(这个是我拿来测试的):

<!DOCTYPE HTML>
<html>
  <head>
    <title>HTML转PDF测试</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style type="text/css">
      /* Basic styling for root. */
      #root {
        width: 500px;
        height: 700px;
        background-color: yellow;
      }
      .dragdroppable {
        position: relative;
      }
      .dragdroppable-row {
        width: 100%;
      }
    </style>
  </head>
  <body>
    <button onclick="test()">生成PDF</button>
    <div id="app" style="height: 500px;border: 1px solid;">app</div>
    <div class="dragdroppable dragdroppable-row">
      <div id="root">
        <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
          <polyline points="20,20 40,25 60,40 80,120 120,140 200,180"
          style="fill:none;stroke:black;stroke-width:3" />
        </svg>
      </div>
    </div>
    <script src="https://cdn.bootcss.com/html2canvas/0.5.0-beta4/html2canvas.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.0.272/jspdf.debug.js"></script>
    <script>
      function test() {
        // 获取DIV元素
        var element1 = document.getElementById('root');
        var element = element1.cloneNode(true);
        var w = element.offsetWidth;    // 获得该容器的宽
        var h = element.offsetHeight;    // 获得该容器的高
        var offsetTop = element.offsetTop;    // 获得该容器到文档顶部的距离
        var offsetLeft = element.offsetLeft;    // 获得该容器到文档最左的距离
        var canvas = document.createElement("canvas");
        var abs = 0;
        var win_i = document.body.clientWidth;    // 获得当前可视窗口的宽度(不包含滚动条)
        var win_o = window.innerWidth;    // 获得当前窗口的宽度(包含滚动条)
        if (win_o > win_i) {
            abs = (win_o - win_i) / 2;    // 获得滚动条长度的一半
        }
        canvas.width = w * 2;    // 将画布宽&&高放大两倍
        canvas.height = h * 2;
        var context = canvas.getContext("2d");
        context.scale(2, 2);
        context.translate(-offsetLeft - abs, -offsetTop);
        // 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
        // translate的时候,要把这个差值去掉
        document.body.appendChild(element);
        html2canvas(element, {
            allowTaint: true,
            scale: 2, // 提升画面质量,但是会增加文件大小
        }).then(function (canvas) {
            document.body.removeChild(element);
            var contentWidth = canvas.width;
            var contentHeight = canvas.height;
            //一页pdf显示html页面生成的canvas高度;
            var pageHeight = contentWidth / 592.28 * 841.89;
            //未生成pdf的html页面高度
            var leftHeight = contentHeight;
            //页面偏移
            var position = 0;
            //a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
            var imgWidth = 595.28;
            var imgHeight = 592.28 / contentWidth * contentHeight;

            var pageData = canvas.toDataURL('image/jpeg', 1.0);

            var pdf = new jsPDF('', 'pt', 'a4');
            //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
            //当内容未超过pdf一页显示的范围,无需分页
            if (leftHeight < pageHeight) {
                pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
            } else {    // 分页
                while (leftHeight > 0) {
                    pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
                    leftHeight -= pageHeight;
                    position -= 841.89;
                    //避免添加空白页
                    if (leftHeight > 0) {
                        pdf.addPage();
                    }
                }
            }
            pdf.save(`${name}.pdf`);
        });
      }
    </script>
  </body>
</html>

结果是可以正常展示的,下面我们运用起来。

可以正常下载,但是如果按照上面的配置,那么下载出的pdf内容,就可能无法显示,原因是因为它继承了图表在页面上的位置,pdf显示为空白,由于已经提前测试了插件,可以排除插件的问题,并且我去掉了预览功能直接下载进行测试,结果也不正常,这就证明,superset的图表和插件有冲突。

3.尝试直接操作canvas图表的样式

1)尝试着给它添加绝对定位的样式;

2)尝试在html2canvas上添加高度;

3)最后又想到了,替换canvas变成替换为img,实现的代码如下所示:

 let element = document.getElementById('copyDom');
    domToImage
    .toJpeg(element, {
      quality: 0.95,
      bgcolor: '#fff',
      // Mapbox controls are loaded from different origin, causing CORS error
      // See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL#exceptions
      filter: (node) =>
        node.className !== 'mapboxgl-control-container',
      ...{},
    })
    .then(dataUrl => {
        var imgWidth = 595.28;
        var imgHeight = 592.28 / element.offsetWidth * element.offsetHeight;
        var pageHeight = element.offsetWidth / 592.28 * 841.89;
        var leftHeight = element.offsetHeight;
        var pdf = new jsPDF('', 'pt', _type ? [imgWidth, imgHeight] : 'a4');
        var p = 0;
        pdf.addImage(dataUrl, 'JPEG', 0, 0, imgWidth, imgHeight);
        pdf.save(`${name}.pdf`);
        document.body.removeChild(element);
    })

实现的原理都是一样的,方式变了而已,这个方法可以实现下载pdf并且样式正常,我没有分页,分页的代码会出现内容被分割,因为这些图表没有规律,也不是自己写的,没法具体到每一个观察高度,中间还出现了克隆的html没法使用的问题,我想到它可能不在整个页面中,于是使用appendChild将html加载body的后面,然后再下载完成之后,remove掉,成功解决问题。。

总结问题及解决方法

1.canvas画出的图表无法显示的问题

这个可以使用方法将canvas转为img标签;

2.superset的dashboard图表无法使用html2canvas正常显示

插件没有问题,但是却和superset产生了冲突,换一种方式,转为图片再下载;

3.克隆html时

要注意如果无法显示或者操作,就将html写在body上,然后记得remove,就可以正常显示和操作属性了;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值