JS导出word文件(Html to word)

作者: Nanayai | 来源:发表于2018-06-21 16:33 被阅读2116次
    image

    本次使用插件:jquery.wordexport.js;

    Dependencies: jQuery and FileSaver.js ;

    遇到了需要将页面内容导出为word的需求,本以为是个简单的需求,却折腾了一天,主要难点在css样式和echarts图表的导出,wordexport.js插件的使用非常简单:

    <script src="js/jquery-2.1.0.js" type="text/javascript" charset="utf-8"></script>
    <script src="js/FileSaver.js"></script>
    <script src="js/jquery.wordexport.js"></script>
    <div id="content" class="content">
        导出内容
    </div>
    <script type="text/javascript">
        $("#content").wordExport("文件名");
    </script>
    

    这样就可以导出content 里的内容,但是你会发现页面上呈现的样式并没有展示在word里,这里需要改动wordexport.js的部分代码来实现样式表的导入,下面是源码(改动后):

    if (typeof jQuery !== "undefined" && typeof saveAs !== "undefined") {
        (function($) {
            $.fn.wordExport = function(fileName,rule) {
                fileName = typeof fileName !== 'undefined' ? fileName : "jQuery-Word-Export";
                var static = {
                    mhtml: {
                        top: "Mime-Version: 1.0\nContent-Base: " + location.href + "\nContent-Type: Multipart/related; boundary=\"NEXT.ITEM-BOUNDARY\";type=\"text/html\"\n\n--NEXT.ITEM-BOUNDARY\nContent-Type: text/html; charset=\"utf-8\"\nContent-Location: " + location.href + "\n\n<!DOCTYPE html>\n<html>\n_html_</html>",
                        head: "<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n<style>\n_styles_\n</style>\n</head>\n",
                        body: "<body>_body_</body>"
                    }
                };
                var options = {
                    maxWidth: 624
                };
                // Clone selected element before manipulating it
                var markup = $(this).clone();
    
                // Remove hidden elements from the output
                markup.each(function() {
                    var self = $(this);
                    if (self.is(':hidden'))
                        self.remove();
                });
    
                // Embed all images using Data URLs
                var images = Array();
                var img = markup.find('img');
                for (var i = 0; i < img.length; i++) {
                    // Calculate dimensions of output image
                    var w = Math.min(img[i].width, options.maxWidth);
                    var h = img[i].height * (w / img[i].width);
                    // Create canvas for converting image to data URL
                    var canvas = document.createElement("CANVAS");
                    canvas.width = w;
                    canvas.height = h;
                    // Draw image to canvas
                    var context = canvas.getContext('2d');
                    context.drawImage(img[i], 0, 0, w, h);
                    // Get data URL encoding of image
                    var uri = canvas.toDataURL("image/png");
                    $(img[i]).attr("src", img[i].src);
                    img[i].width = w;
                    img[i].height = h;
                    // Save encoded image to array
                    images[i] = {
                        type: uri.substring(uri.indexOf(":") + 1, uri.indexOf(";")),
                        encoding: uri.substring(uri.indexOf(";") + 1, uri.indexOf(",")),
                        location: $(img[i]).attr("src"),
                        data: uri.substring(uri.indexOf(",") + 1)
                    };
                }
    
                // Prepare bottom of mhtml file with image data
                var mhtmlBottom = "\n";
                for (var i = 0; i < images.length; i++) {
                    mhtmlBottom += "--NEXT.ITEM-BOUNDARY\n";
                    mhtmlBottom += "Content-Location: " + images[i].location + "\n";
                    mhtmlBottom += "Content-Type: " + images[i].type + "\n";
                    mhtmlBottom += "Content-Transfer-Encoding: " + images[i].encoding + "\n\n";
                    mhtmlBottom += images[i].data + "\n\n";
                }
                mhtmlBottom += "--NEXT.ITEM-BOUNDARY--";
    
                //TODO: load css from included stylesheet
                //源码将styles置为"",这里我们只需改动成传入css,赋值给styles即可;
                var styles = rule || "";
                // Aggregate parts of the file together
                var fileContent = static.mhtml.top.replace("_html_", static.mhtml.head.replace("_styles_", styles) + static.mhtml.body.replace("_body_", markup.html())) + mhtmlBottom;
                // Create a Blob with the file contents
                var blob = new Blob([fileContent], {
                    type: "application/msword;charset=utf-8"
                });
                saveAs(blob, fileName + ".doc");
            };
        })(jQuery);
    } else {
        if (typeof jQuery === "undefined") {
            console.error("jQuery Word Export: missing dependency (jQuery)");
        }
        if (typeof saveAs === "undefined") {
            console.error("jQuery Word Export: missing dependency (FileSaver.js)");
        }
    }
    

    如文中注释部分所言:源码将styles置为"",这里我们只需改动成传入css,赋值给styles即可;在调用的时候先将html中使用的css拼接成一个字符串,再传入该插件中:

    var rules = "",
        ss = document.styleSheets;
    for (var i = 0; i < ss.length; ++i) {
        for (var x = 0; x < ss[i].cssRules.length; ++x) {
            rules += ss[i].cssRules[x].cssText;
        }
    }
    $("#content").wordExport(name, rules);
    

    如此一来,你将发现:一部分css样式生效了,比如文字的宽高和颜色等,然而还有很多样式没有生效:如大部分css3选择器~;这里是因为word上支持的样式有限导致的;

    这就非常难受了,最开始我的页面上的表格使用dhtmlx构建,导出后不仅样式完全没有,而且发现dhtmlxGrid的表头和表体是两个table,这样在word里非常不好调整,于是我更换了dataTables插件,问题依然是样式没有生效;

    于是我想到一个办法:既然是因为css3选择器不支持导致的,那将dom的样式取出来,写入html标签的style里是否可以解析:

    var node = $("#container")[0];
    var treeWalker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, null, false);
    if(node && node.nodeType === 1) {
        var rule = getComputedStyle(node);
        $(node).attr("style", rule.cssText)
    }
    var node = treeWalker.nextNode();
    while(node != null) {
        var rule = getComputedStyle(node);
        $(node).attr("style", rule.cssText)
        node = treeWalker.nextNode();
    }
    

    测试以后果然可以,但是若是页面结构复杂,这种方法会很耗时,而且因为rule.cssText 得到的样式字符串很长,生成的word体积就很庞大,

    解决思路:

    • 可以只取出部分需要的样式信息,先列出页面所需的样式名数组,然后通过rule.样式名 的方式获取,这样可以去掉rule.cssText 中绝大部分的无用样式;
    • 可以只对需要的部分结构进行以上操作,比如我的页面中只有表格样式加载不出,那我只需对所有表格追加style 即可;

    因为项目时间紧迫,我没有实践以上思路,而是直接在wordexport.js中写入了word可以解析的table样式:

    var rules = "table{border-collapse:collapse;margin:0 auto;text-align:center;width: 100%;}table td,table th{border:1px solid #cad9ea;color:#666;height:30px}table thead th{background-color:#F1F1F1;min-width:400px}table tr{background:#fff}",
            ss = document.styleSheets;
    for (var i = 0; i < ss.length; ++i) {
        for (var x = 0; x < ss[i].cssRules.length; ++x) {
                rules += ss[i].cssRules[x].cssText;
        }
    }
    

    关于echarts图表的导出:word的导出会忽略很多dom结构,echarts的canvas正是其中之一,于是在这里我们需要先使用curEchart.getConnectedDataURL()将图表转为base64的编码的图片,然后指定img标签的src为获取到的base64字符串,这样就可以顺利导出图片啦~,下面是我最终的代码:

    var rules = "table{border-collapse:collapse;margin:0 auto;text-align:center;width: 100%;}table td,table th{border:1px solid #cad9ea;color:#666;height:30px}table thead th{background-color:#F1F1F1;min-width:400px}table tr{background:#fff}",
        ss = document.styleSheets;
    for(var i = 0; i < ss.length; ++i) {
        for(var x = 0; x < ss[i].cssRules.length; ++x) {
            rules += ss[i].cssRules[x].cssText;
        }
    }
    
    //先clone来避免影响页面显示
    var clone = $("#container").clone();
    //找到所有echarts图表容器
    var charts = clone.find(".chart");
    //隐藏无需导出的dom
    clone.find(".input_div").hide();
    //简单控制流程
    var flag = charts.length;
    for(var i = 0; i < charts.length; i++) {
        //获取echarts对象
        var curEchart = echarts.getInstanceByDom(charts[i]);
        if(curEchart) {
            //将图表替换为图片
            var base = curEchart.getConnectedDataURL();
            var img = $('<img style="" src="' + base + '"/>');
            $(charts[i]).html(img);
            flag--;
        } else {
            flag--;
        }
    }
    var interval = setInterval(function() {
        if(!flag) {
            clearInterval(interval);
            //导出word
            clone.wordExport(name, rules);
        }
    }, 200)
    

    相关文章

      网友评论

        本文标题:JS导出word文件(Html to word)

        本文链接:https://www.haomeiwen.com/subject/qnccyftx.html