vue html转word

网页转word,一般都是将网页标签内容复制到word中,存在主要的两个问题:1. 页面样式丢失 2. 页面图片丢失,如果页面中有图表就更悲剧了; 如果是网页转pdf,现在技术比较成熟,html2Canvas就搞定了。
对于网页中的图片与图标资源,要想展示在word中,只有一种方案,那就是通过转换为图片的base64形式。

进入正题,直接上vue代码:
定义word导出组件

//导入js文件
import saveAs from "file-saver"
import $ from "jquery"
 
if (typeof jQuery !== "undefined" && typeof saveAs !== "undefined") {
    (function() {
        $.fn.wordExport = function(fileName,rules) {
            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');
        console.log(img.length)
        for (var i = 0; i < img.length; i++) {
            // calc word show size
            var w = Math.min(img[i].width, options.maxWidth);
            var h = img[i].height * (w / img[i].width);
            // $(img[i]).attr("src", uri);
            img[i].width = w;
            img[i].height = h;
            // base64 直接显示即可
            var uri = img[i].src;
            // 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].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
        var styles = rules;

        // 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.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)");
    }
}

测试页面

<template>
  <div>
    <button v-on:click="downLoad">downLoad</button>
    <button v-on:click="showImg">showImg</button>
    <div id="word" class="hello">
      <h1 style="text-align:center;">{{ msg }}</h1>
      <h2>我是柱状图</h2>
      <div>
        <img
          id="showImg"
          v-if="imgBase64 != ''"
          :src="imgBase64"
          style="width:600px; height:300px;"
        />
      </div>
      <div
        ref="charts"
        id="charts"
        style="width:600px; height:300px;margin: 0 auto;"
      ></div>

      <h2>我是表格</h2>
      <table style="width:100%; border-collapse:collapse;" border="1px">
        <tr>
          <td>列头1</td>
          <td>列头2</td>
          <td>列头3</td>
          <td>列头4</td>
          <td>列头5</td>
        </tr>
        <tr>
          <td>列头1</td>
          <td>列头2</td>
          <td>列头3</td>
          <td>列头4</td>
          <td>列头5</td>
        </tr>
        <tr>
          <td>列头1</td>
          <td>列头2</td>
          <td>列头3</td>
          <td>列头4</td>
          <td>列头5</td>
        </tr>
        <tr>
          <td>列头1</td>
          <td>列头2</td>
          <td>列头3</td>
          <td>列头4</td>
          <td>列头5</td>
        </tr>
      </table>

      <h2>我是饼图</h2>
      <div>
        <img
          id="showImg2"
          v-if="imgBase642 != ''"
          :src="imgBase642"
          style="width:600px; height:300px;"
        />
      </div>
      <div
        ref="charts2"
        id="charts2"
        style="width:600px; height:300px;margin: 0 auto;"
      ></div>
    </div>
  </div>
</template>

<script>
import $ from "jquery";
import saveAs from "file-saver";
import wordExport from "@/utils/word.export";
import echarts from "echarts";
export default {
  name: "HelloWorld",
  data() {
    return {
      option2: {
        title: {
          text: "某站点用户访问来源",
          subtext: "纯属虚构",
          left: "center"
        },
        tooltip: {
          trigger: "item"
        },
        legend: {
          orient: "vertical",
          left: "left"
        },
        series: [
          {
            name: "访问来源",
            type: "pie",
            radius: "50%",
            data: [
              { value: 1048, name: "搜索引擎" },
              { value: 735, name: "直接访问" },
              { value: 580, name: "邮件营销" },
              { value: 484, name: "联盟广告" },
              { value: 300, name: "视频广告" }
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: "rgba(0, 0, 0, 0.5)"
              }
            }
          }
        ]
      },
      msg: "Welcome to Your Vue.js App",
      imgBase64: "",
      imgBase642: "",
      myChart2: null,
      myChart: null
    };
  },
  mounted() {
    console.log("mounted");
    this.mycharts();
  },
  methods: {
    downLoad() {
      this.showImg();
      setTimeout(function() {
        $("#word").wordExport("网页导出"); //参数是下载的word文件名
      }, 1000);
    },
    showImg() {
      var dataURL = this.myChart.getConnectedDataURL({
        type: "png",
        pixelRatio: 2, //放大n倍
        backgroundColor: "#fff"
      });
      $("#charts").hide();
      this.imgBase64 = dataURL;

      var dataURL2 = this.myChart2.getConnectedDataURL({
        type: "png",
        pixelRatio: 2, //放大n倍
        backgroundColor: "#fff"
      });
      $("#charts2").hide();
      this.imgBase642 = dataURL2;
    },
    mycharts() {
      let myChart = echarts.init(this.$refs.charts, "macarons");
      myChart.setOption({
        color: [
          "rgb(8,252,7)",
          "rgb(255,168,0)",
          "rgb(0,121,254)",
          "rgb(0,255,251)"
        ],
        tooltip: {
          trigger: "axis",
          axisPointer: {
            type: "shadow",
            crossStyle: {
              color: "#999"
            }
          },

          position: function(pt) {
            //提示框的位置
            return [pt[0], 30];
          }
        },
        grid: {
          //图表和父盒子之间的距离
          left: "3%",
          right: "4%",
          bottom: "3%",
          top: "2%",
          containLabel: true
        },
        xAxis: {
          //x轴
          type: "category",
          // boundaryGap: false,
          data: [
            "供电煤耗",
            "综合厂用电率",
            "发电厂用电率",
            "发电水耗率",
            "发电油耗率"
          ],
          axisLabel: {
            interval: 0,
            textStyle: {
              color: "#000",
              fontSize: 10
            },
            margin: 8
          },
          axisLine: {
            show: true,
            lineStyle: {
              color: "rgb(2,121,253)"
            }
          },
          axisTick: {
            show: false
          }
        },
        yAxis: {
          type: "value",
          axisLabel: {
            //x轴的坐标文字
            show: true,
            textStyle: {
              color: "#000" //文字的颜色
            }
          },
          max: 100,
          axisLine: {
            show: true,
            lineStyle: {
              color: "rgb(2,121,253)"
            }
          },
          axisTick: {
            show: false
          },
          splitLine: {
            //坐标在grid区域的分割线
            lineStyle: {
              //设置分割线的样式(图表横线颜色)
              color: ["#153a8a"]
            }
          }
        },
        series: [
          {
            name: "完成值",
            type: "bar", //柱状图
            data: [30, 40, 50, 60, 70],
            barWidth: "8%" //柱状体的宽度
          },
          {
            name: "计划值",
            type: "bar",
            data: [20, 50, 60, 40, 40],
            barWidth: "8%"
          },
          {
            name: "同期值",
            type: "bar",
            data: [60, 50, 40, 30, 20],
            barWidth: "8%"
          },
          {
            name: "上期值",
            type: "bar",
            data: [50, 70, 60, 30, 40],
            barWidth: "8%"
          }
        ]
      });
      this.myChart = myChart;

      let myChart2 = echarts.init(this.$refs.charts2, "macarons");
      myChart2.setOption(this.option2);
      this.myChart2 = myChart2;
    }
  }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1,
h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
.righttop {
  width: 100%;
  height: 100%;
}
</style>
导出word效果.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,992评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,212评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,535评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,197评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,310评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,383评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,409评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,191评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,621评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,910评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,084评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,763评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,403评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,083评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,318评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,946评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,967评论 2 351

推荐阅读更多精彩内容