vue使用jquery.wordexport.js导出word,并支持分页和页眉页脚


前言

由于近期工作需要,把前端界面生成word文档导出。还需要自动分页和页眉页脚。后来发现可以用过jQuery的方法来实现。


效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

一、安装插件jquery、html2canvas、file-saver

npm install jquery
npm install html2canvas
npm install file-saver

二、使用方法

这里分页需要注意一下,文档分页是通过再标签上添加style='page-break-before:always;'来进行分页的,但是在vue中直接添加style='page-break-before:always;的属性会转换成break-before:page,会出现分页不成功。这个时候采用在下载的时候往标签里面插入的方法进行分页。

<template>
  <div>
    <el-button type="primary"
      @click="downLoad">下载word</el-button>
    <div class="download">
      <div class="pages">
        我是首页
      </div>
      <div class="pages">
        我是第二页
      </div>
      <div>
        我是第三页
      </div>

    </div>
  </div>

</template>

<script>
import $ from './jqueryexport';
import './jquery.wordexport';

export default {
  data() {
    return {
      data: '',
    };
  },

  methods: {
    downLoad() {
      console.log('下载');
      const pages = document.getElementsByClassName("pages");
      for (let i = 0; i < pages.length; i++) {
        const br = document.createElement("div");
        br.setAttribute("class", "fenye");
        br.innerHTML = "<span><br style='page-break-before:always;' /></span>";
        pages[i].appendChild(br);
      }
      const data = {
        fileName: '测试文件', // 文档名
        fileType: ".doc", // 文档类型  经测试  可以doc xls html 其他的自己去试
        isHeader: true, // 是否显示页眉  *xls  不要设置页眉页脚  改为false
        isFooter: true, // 是否显示页脚  *xls  不要设置页眉页脚  改为false
        header: '我是页眉', // 页眉标题
        footer: '', // 页脚标题
      };
      $('.download').wordExport(data);
    },
  },
};
</script>

<style scoped  lang="less">
.download {
  height: auto;
  border: 1px solid #000;
  padding: 0 40px;
  background: #fff;
}
</style>

三、创建相关文件

大家可以把下面文件创建出来就可以使用了。

1.jqueryexport.js文件

import $ from 'jquery';
window.$ = $;
window.jQuery = $;
export default $;

2.jquery.wordexport.js

import { saveAs } from 'file-saver';
if (typeof jQuery !== "undefined" && typeof saveAs !== "undefined") {
  (function ($) {
    $.fn.wordExport = function (exPortData) {
      _initData(exPortData);
      const options = {
        maxWidth: 550,
      };
      // Clone selected element before manipulating it
      const markup = $(this).clone();

      // Remove hidden elements from the output
      markup.each(function () {
        const self = $(this);
        if (self.is(':hidden')) { self.remove(); }
      });

      // Embed all images using Data URLs
      const images = Array();
      const img = markup.find('img');
      for (var i = 0; i < img.length; i++) {
        // Calculate dimensions of output image
        const w = Math.min(img[i].width, options.maxWidth);
        const h = img[i].height * (w / img[i].width);
        // Create canvas for converting image to data URL
        const canvas = document.createElement("CANVAS");
        canvas.width = w;
        canvas.height = h;
        // Draw image to canvas
        const context = canvas.getContext('2d');
        context.drawImage(img[i], 0, 0, w, h);
        // Get data URL encoding of image
        const 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
      let 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--";
      const fileContent = getModelHtml(markup.html(), mhtmlBottom, exPortData);
      const blob = new Blob([fileContent], {
        type: "application/msword;charset=utf-8",
      });
      saveAs(blob, exPortData.fileName + exPortData.fileType);
    };
    function _initData(exPortData) {
      exPortData.fileName = typeof (exPortData.fileName) !== 'undefined' ? exPortData.fileName : "jQuery-Word-Export";
      exPortData.fileType = typeof (exPortData.fileType) !== "undefined" ? exPortData.fileType : ".doc";
    }
    function getModelHtml(mhtml, mhtmlBottom, exPortData) {
      let updateStyles; let header; let footer = "";
      if (exPortData.styles) {
        styles = exPortData.styles.html();
      }
      if (exPortData.updateExportCss) {
        updateStyles = exPortData.updateExportCss.html();
      }
      if (exPortData.isHeader) {
        header = `${"<div style=\"mso-element:header;padding-bottom:20px;\" id=\"h1\">\n" +
                  "<p class=\"MsoHeader\">"}${exPortData.header}</p>\n` +
                  `</div>`;
      }
      if (exPortData.isFooter) {
        footer = `${"<div style=\"mso-element:footer;text-align: center;\" id=\"f1\">\n" +
        "<p class=\"MsoHeader\">"}${exPortData.footer}</p>\n` +
        // `<p class="MsoNum"><span class="num" style="mso-field-code: PAGE "></span>/<span style="mso-field-code:NUMPAGES"></span>\n</p>\n` +
        `<p class="MsoNum"><span class="num" style="mso-field-code: PAGE "></span></p>\n` +
                  `</div>`;
      }

      /*
      html标签中,和meta标签下面的判断,用来默认显示页面视图模式
     */
      return `
              <!DOCTYPE html>
              <html xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\" xmlns:w=\"urn:schemas-microsoft-com:office:word\" xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\" xmlns=\"http://www.w3.org/TR/REC-html40\">
              <head>
                  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:TrackMoves>false</w:TrackMoves><w:TrackFormatting/><w:ValidateAgainstSchemas/><w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid><w:IgnoreMixedContent>false</w:IgnoreMixedContent><w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText><w:DoNotPromoteQF/><w:LidThemeOther>EN-US</w:LidThemeOther><w:LidThemeAsian>ZH-CN</w:LidThemeAsian><w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript><w:Compatibility><w:BreakWrappedTables/><w:SnapToGridInCell/><w:WrapTextWithPunct/><w:UseAsianBreakRules/><w:DontGrowAutofit/><w:SplitPgBreakAndParaMark/><w:DontVertAlignCellWithSp/><w:DontBreakConstrainedForcedTables/><w:DontVertAlignInTxbx/><w:Word11KerningPairs/><w:CachedColBalance/><w:UseFELayout/></w:Compatibility><w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel><m:mathPr><m:mathFont m:val=\"Cambria Math\"/><m:brkBin m:val=\"before\"/><m:brkBinSub m:val=\"--\"/><m:smallFrac m:val=\"off\"/><m:dispDef/><m:lMargin m:val=\"0\"/> <m:rMargin m:val=\"0\"/><m:defJc m:val=\"centerGroup\"/><m:wrapIndent m:val=\"1440\"/><m:intLim m:val=\"subSup\"/><m:naryLim m:val=\"undOvr\"/></m:mathPr></w:WordDocument></xml><![endif]-->
                  <style type="text/css">
                      table#hrdftrtbl {
                          width: 1px;
                          height: 1px;
                          overflow: hidden;
                      }

                      p.MsoHeader {
                          font-size: 12px;
                          font-weight: bold;
                          text-align: right;
                          margin-top:30px;
                      }

                      p.MsoFooter, li.MsoFooter, div.MsoFooter {
                          margin: 0in;
                          mso-pagination: widow-orphan;
                          tab-stops: center 3.0in right 6.0in;
                          font-size: 20pt;
                          font-weight: bold
                      }

                      p.MsoNum{
                        text-align: center;
                      }

                      <!-- /*FontDefinitions*/
                      @page WordSection {
                          mso-title-page:yes; //首页不显示页眉页脚
                          mso-page-numbers:1; //起始页码从0开始
                          mso-header-margin: 20px;
                          mso-footer-margin: 20px;
                          mso-header: h1;
                          mso-footer: f1;
                      }

                      div.WordSection {
                          page: WordSection;
                      }

                      -->
                      ${styles}
                      ${updateStyles}
                  </style>
              </head>
              <body lang=ZH-CN style="tab-interval:21.0pt">
              <div class="WordSection">
                  ${mhtml}
                  ${footer}
                  ${header}
              </div>
              </body>
              </html>
           ${mhtmlBottom}
  `;
    }
  }(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)");
  }
}

3.FileSaver.js

var saveAs = saveAs || (function (view) {
  // IE <10 is explicitly unsupported
  if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
    return;
  }
  const
    doc = view.document;
  // only get URL when necessary in case Blob.js hasn't overridden it yet
  const get_URL = function () {
    return view.URL || view.webkitURL || view;
  };
  const save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a");
  const can_use_save_link = "download" in save_link;
  const click = function (node) {
    const event = new MouseEvent("click");
    node.dispatchEvent(event);
  };
  const is_safari = /constructor/i.test(view.HTMLElement);
  const is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent);
  const throw_outside = function (ex) {
    (view.setImmediate || view.setTimeout)(() => {
      throw ex;
    }, 0);
  };
  const force_saveable_type = "application/octet-stream";
  const arbitrary_revoke_timeout = 1000 * 40; // in ms
  const revoke = function (file) {
    const revoker = function () {
      if (typeof file === "string") { // file is an object URL
        get_URL().revokeObjectURL(file);
      } else { // file is a File
        file.remove();
      }
    };
    setTimeout(revoker, arbitrary_revoke_timeout);
  };
  const dispatch = function (filesaver, event_types, event) {
    event_types = [].concat(event_types);
    let i = event_types.length;
    while (i--) {
      const listener = filesaver[`on${event_types[i]}`];
      if (typeof listener === "function") {
        try {
          listener.call(filesaver, event || filesaver);
        } catch (ex) {
          throw_outside(ex);
        }
      }
    }
  };
  const auto_bom = function (blob) {
    if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
      return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type });
    }
    return blob;
  };
  const FileSaver = function (blob, name, no_auto_bom) {
    if (!no_auto_bom) {
      blob = auto_bom(blob);
    }
    // First try a.download, then web filesystem, then object URLs
    const
      filesaver = this;
    const type = blob.type;
    const force = type === force_saveable_type;
    let object_url;
    const dispatch_all = function () {
      dispatch(filesaver, "writestart progress write writeend".split(" "));
    };
    // on any filesys errors revert to saving with object URLs
    const fs_error = function () {
      if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
        // Safari doesn't allow downloading of blob urls
        const reader = new FileReader();
        reader.onloadend = function () {
          let url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
          const popup = view.open(url, '_blank');
          if (!popup) view.location.href = url;
          url = undefined; // release reference before dispatching
          filesaver.readyState = filesaver.DONE;
          dispatch_all();
        };
        reader.readAsDataURL(blob);
        filesaver.readyState = filesaver.INIT;
        return;
      }
      // don't create more object URLs than needed
      if (!object_url) {
        object_url = get_URL().createObjectURL(blob);
      }
      if (force) {
        view.location.href = object_url;
      } else {
        const opened = view.open(object_url, "_blank");
        if (!opened) {
          view.location.href = object_url;
        }
      }
      filesaver.readyState = filesaver.DONE;
      dispatch_all();
      revoke(object_url);
    };
    filesaver.readyState = filesaver.INIT;

    if (can_use_save_link) {
      object_url = get_URL().createObjectURL(blob);
      setTimeout(() => {
        save_link.href = object_url;
        save_link.download = name;
        click(save_link);
        dispatch_all();
        revoke(object_url);
        filesaver.readyState = filesaver.DONE;
      });
      return;
    }

    fs_error();
  };
  const FS_proto = FileSaver.prototype;
  const saveAs = function (blob, name, no_auto_bom) {
    return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
  }
  ;
  // IE 10+ (native saveAs)
  if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
    return function (blob, name, no_auto_bom) {
      name = name || blob.name || "download";

      if (!no_auto_bom) {
        blob = auto_bom(blob);
      }
      return navigator.msSaveOrOpenBlob(blob, name);
    };
  }

  FS_proto.abort = function () {
  };
  FS_proto.readyState = FS_proto.INIT = 0;
  FS_proto.WRITING = 1;
  FS_proto.DONE = 2;

  FS_proto.error =
      FS_proto.onwritestart =
          FS_proto.onprogress =
              FS_proto.onwrite =
                  FS_proto.onabort =
                      FS_proto.onerror =
                          FS_proto.onwriteend =
                              null;

  return saveAs;
}(
  typeof self !== "undefined" && self
  || typeof window !== "undefined" && window
  || this.content,
));

if (typeof module !== "undefined" && module.exports) {
  module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
  define([], () => saveAs);
}

总结

本方法采用的是jquery.wordexport.js导出word,需要注意的就是page-break-before:always属性的转换问题。如果需要设置字体大小需要在jquery.wordexport.js文件中去定义样式,这样在导出的时候才会生效。

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的仿QQ聊天记录展示页面的HTML代码,使用Vue.jsjQuery.scrollbar插件实现滚动条: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>仿QQ聊天记录展示页面</title> <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/jquery.scrollbar/0.2.11/jquery.scrollbar.min.css"> </head> <body> <div id="app"> <div class="chat-box"> <div class="chat-item" v-for="(item, index) in chatList" :key="index"> <div class="avatar"> <img :src="item.avatar" alt=""> </div> <div class="content"> <div class="name">{{ item.name }}</div> <div class="message">{{ item.message }}</div> <div class="time">{{ item.time }}</div> </div> </div> </div> </div> <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://cdn.bootcdn.net/ajax/libs/jquery.scrollbar/0.2.11/jquery.scrollbar.min.js"></script> <script> var app = new Vue({ el: '#app', data: { chatList: [ { avatar: 'https://via.placeholder.com/50', name: 'Alice', message: 'Hello, how are you?', time: '2021-09-01 10:00:00' }, { avatar: 'https://via.placeholder.com/50', name: 'Bob', message: 'I\'m fine, thanks. And you?', time: '2021-09-01 10:01:00' }, { avatar: 'https://via.placeholder.com/50', name: 'Alice', message: 'I\'m good too.', time: '2021-09-01 10:02:00' }, { avatar: 'https://via.placeholder.com/50', name: 'Bob', message: 'That\'s great.', time: '2021-09-01 10:03:00' } ] }, mounted: function() { // 初始化滚动条 $('.chat-box').scrollbar(); } }); </script> </body> </html> ``` 这个示例中,我们使用Vue.js的v-for指令来循环渲染聊天记录列表,并使用jQuery.scrollbar插件来实现滚动条。在Vue实例的mounted钩子函数中,我们对.chat-box元素进行了滚动条初始化。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟很沉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值