需要用的到插件
- jquery.wordexport.js(源码地址:jquery.worldexport.js)
- FileSaver.js
- https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.js
jquery.worldexport.js导出world最主要的问题是:
1.图片的导出。
2.源代码仅支持内联样式而页级css样式并不能生效。
3.复杂的dom样式以及效果如何导出。
针对问题一,二的解决方案就是:修改jquery.wordexport的源码。让他支持页级Css生效以及图片src是http或者https链接的导出。修改之后的代码如下:
第一个修改点就是传入style字符串集,对页级css样式的支持(似乎就是原作者还未TODO的项)。
第二个点就是对img的处理。注释掉对应的代码添加上新的代码,可以对照着源码查看(代码来自:https://blog.youkuaiyun.com/weixin_43639981/article/details/107492220)。
针对问题三解决方案就是:利用html2canvas.js将复杂的dom节点样式转换成canvas然后在取base64添加到img标签上在转world。
function Dom2Img(el, elImgName) {
html2canvas(el, {
// 页面高度
height: el.height,
// 页面宽度
width: el.width,
}).then((canvas) => {
//图片src赋值
document.getElementById(elImgName).src = canvas.toDataURL()
//图片下载
var download = document.createElement("a");
download.href = canvas.toDataURL()
download.setAttribute('download', 'download.png');
//download.click();
//console.log(download);
})
}
// 将html转换成img
Dom2Img(document.getElementById("el-timeline-box"), 'el-timeline-img')
Dom2Img(document.getElementById("el-descriptions-box"), 'el-descriptions-img')
最后附上源码如下:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>JS导出Word文档</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<!-- 引入组件库 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.5.0-beta4/html2canvas.js"></script>
<script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.js"></script>
<script type="text/javascript" src="FileSaver.js"></script>
<script type="text/javascript" src="jquery.wordexport.js"></script>
</head>
<style type="text/css" id="style">
.myh1 {
color: red;
font-size: 50px;
}
.myp {
font-size: 20px;
font-weight: bold;
color: orange;
}
.my-table,
.my-tr,
.my-td,
.my-th {
padding: 5px;
border: 1px solid black;
border-collapse: collapse;
border-spacing: 0px;
}
.my-th {
background-color: orange;
}
.my-div {
width: 200px;
height: 200px;
}
.my-img {
width: 100%;
height: 100%;
}
</style>
<body>
<div id="app">
<h1 class="myh1">JS导出Word文档</h1>
<p class="myp">1.JS导出Word文档含https图片</p>
<div class="my-div">
<img src="https://alifei02.cfp.cn/creative/vcg/800/version23/VCG2154e83874f.jpg" class="my-img">
</div>
<p class="myp">2.JS导出Word文档含复杂样式先转换成图片在导出</p>
<div class="block" id="el-timeline-box" style="width: 50%">
<el-timeline>
<el-timeline-item timestamp="2018/4/12" placement="top">
<el-card>
<h4>更新 Github 模板</h4>
<p>王小虎 提交于 2018/4/12 20:46</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="2018/4/3" placement="top">
<el-card>
<h4>更新 Github 模板</h4>
<p>王小虎 提交于 2018/4/3 20:46</p>
</el-card>
</el-timeline-item>
<el-timeline-item timestamp="2018/4/2" placement="top">
<el-card>
<h4>更新 Github 模板</h4>
<p>王小虎 提交于 2018/4/2 20:46</p>
</el-card>
</el-timeline-item>
</el-timeline>
</div>
<p class="myp">转换成图片如下:</p>
<div>
<img id="el-timeline-img">
</div>
<p class="myp">在转换成图片的过程中跟实际的样式会有出入的:</p>
<div id="el-descriptions-box">
<el-descriptions class="margin-top" title="带边框列表" :column="3" :size="size" border>
<template slot="extra">
<el-button type="primary" size="small">操作</el-button>
</template>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-user"></i>
用户名
</template>
kooriookami
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-mobile-phone"></i>
手机号
</template>
18100000000
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-location-outline"></i>
居住地
</template>
苏州市
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-tickets"></i>
备注
</template>
<el-tag size="small">学校</el-tag>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<i class="el-icon-office-building"></i>
联系地址
</template>
江苏省苏州市吴中区吴中大道 1188 号
</el-descriptions-item>
</el-descriptions>
</div>
<div>
<img id="el-descriptions-img">
</div>
<p class="myp">3.JS导出Word文档简单表格</p>
<p>Table with colgroup</p>
<table class="my-table">
<tr class="my-tr">
<th class="my-th">Countries</th>
<th class="my-th">Capitals</th>
<th class="my-th">Population</th>
<th class="my-th">Language</th>
</tr>
<tr class="my-tr">
<td class="my-td">USA</td>
<td class="my-td">Washington D.C.</td>
<td class="my-td">309 million</td>
<td class="my-td">English</td>
</tr>
<tr class="my-tr">
<td class="my-td">Sweden</td>
<td class="my-td">Stockholm</td>
<td class="my-td">9 million</td>
<td class="my-td">Swedish</td>
</tr>
</table>
</div>
<div>
<input type="button" value="导出word">
</div>
<script>
var vm = new Vue({
el: '#app',
data() {
return {
size: ''
}
},
methods: {
exportWorld() {
$("#app").wordExport('word文档', document.getElementById('style').innerText);
}
}
})
function Dom2Img(el, elImgName) {
html2canvas(el, {
// 页面高度
height: el.height,
// 页面宽度
width: el.width,
}).then((canvas) => {
//图片src赋值
document.getElementById(elImgName).src = canvas.toDataURL()
//图片下载
var download = document.createElement("a");
download.href = canvas.toDataURL()
download.setAttribute('download', 'download.png');
//download.click();
//console.log(download);
})
}
// 将html转换成img
Dom2Img(document.getElementById("el-timeline-box"), 'el-timeline-img')
Dom2Img(document.getElementById("el-descriptions-box"), 'el-descriptions-img')
console.log(document.getElementById('style').innerText)
$(function () {
$("input[type='button']").click(function (event) {
$("#app").wordExport('word文档', document.getElementById('style').innerText);
});
});
</script>
</body>
</html>
jquery.wordexport.js
if (typeof jQuery !== "undefined" && typeof saveAs !== "undefined") {
(function ($) {
$.fn.wordExport = function (fileName, style) {
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
//start
var img_id = '#' + img[i].id
img[i].src = img[i].src.replace("https", "http"); //处理导出不显示的问题,如果在某些代码中出现了图片无法显示问题,可以加入该代码
$(canvas).attr("id", "test_word_img_" + i).width(w).height(h).insertAfter(img_id); //注释掉img处理的其他代码
//end
// 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/jpg");
// $(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
var styles = style;
// 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)");
}
}
FileSaver.js
/* FileSaver.js
* A saveAs() FileSaver implementation.
* 1.3.2
* 2016-06-16 18:25:19
*
* By Eli Grey, http://eligrey.com
* License: MIT
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
*/
/*global self */
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs = saveAs || (function (view) {
"use strict";
// IE <10 is explicitly unsupported
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
return;
}
var
doc = view.document
// only get URL when necessary in case Blob.js hasn't overridden it yet
, get_URL = function () {
return view.URL || view.webkitURL || view;
}
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
, can_use_save_link = "download" in save_link
, click = function (node) {
var event = new MouseEvent("click");
node.dispatchEvent(event);
}
, is_safari = /constructor/i.test(view.HTMLElement)
, is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent)
, throw_outside = function (ex) {
(view.setImmediate || view.setTimeout)(function () {
throw ex;
}, 0);
}
, force_saveable_type = "application/octet-stream"
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
, arbitrary_revoke_timeout = 1000 * 40 // in ms
, revoke = function (file) {
var 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);
}
, dispatch = function (filesaver, event_types, event) {
event_types = [].concat(event_types);
var i = event_types.length;
while (i--) {
var listener = filesaver["on" + event_types[i]];
if (typeof listener === "function") {
try {
listener.call(filesaver, event || filesaver);
} catch (ex) {
throw_outside(ex);
}
}
}
}
, auto_bom = function (blob) {
// prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
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;
}
, 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
var
filesaver = this
, type = blob.type
, force = type === force_saveable_type
, object_url
, dispatch_all = function () {
dispatch(filesaver, "writestart progress write writeend".split(" "));
}
// on any filesys errors revert to saving with object URLs
, fs_error = function () {
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
// Safari doesn't allow downloading of blob urls
var reader = new FileReader();
reader.onloadend = function () {
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
var 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 {
var opened = view.open(object_url, "_blank");
if (!opened) {
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
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(function () {
save_link.href = object_url;
save_link.download = name;
click(save_link);
dispatch_all();
revoke(object_url);
filesaver.readyState = filesaver.DONE;
});
return;
}
fs_error();
}
, FS_proto = FileSaver.prototype
, 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
));
// `self` is undefined in Firefox for Android content script context
// while `this` is nsIContentFrameMessageManager
// with an attribute `content` that corresponds to the window
if (typeof module !== "undefined" && module.exports) {
module.exports.saveAs = saveAs;
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
define([], function () {
return saveAs;
});
}
Demo演示地址: jquery.wordexpor.js导出world文档
参考地址:
- https://blog.youkuaiyun.com/weixin_43639981/article/details/107492220( jquery.wordexport.js源文件修改)
- https://www.jianshu.com/p/e74dab30ea2c(html2canvas使用注意项)