文章目录
前言
由于近期工作需要,把前端界面生成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文件中去定义样式,这样在导出的时候才会生效。