openXML是什么
OpenXML 是一种开放标准的文件格式,用于表示和存储电子文档。它是由 Microsoft 开发的,用于 Microsoft Office 系列软件(如 Word、Excel 和 PowerPoint)中的文档格式。OpenXML 是一种基于 XML(eXtensible Markup Language)的文件格式,它使用可读的文本格式存储文档内容,使得文档更容易理解和处理。
主要的 OpenXML 文件格式包括:
Office Open XML (OOXML) 文档格式:
.docx: 用于存储 Microsoft Word 文档。
.xlsx: 用于存储 Microsoft Excel 电子表格。
.pptx: 用于存储 Microsoft PowerPoint 演示文稿。
这些文件格式使用 ZIP 压缩技术进行存储,实际上是一组 XML 文件和其他资源的归档。通过使用 OpenXML,用户和开发人员可以更容易地创建、修改和解析 Office 文档,而无需直接处理二进制文件格式。
开放标准的特性使得其他软件开发者能够理解和实现对这些文档格式的支持,而不必依赖于专有的二进制格式。这有助于实现更好的互操作性和数据可移植性。
相关依赖
"dependencies": {
"angular-expressions": "^1.1.9",
"docxtemplater": "^3.40.2",
"docxtemplater-image-module-free": "^1.1.1",
"file-saver": "2.0.5",
"pizzip": "^3.1.4",
"jszip": "^3.10.1",
"jszip-utils": "^0.1.0",
}
动态不确定列数的表格
genReport.vue
<template>
<div class="app-container home">
<!-- 预览文档的放置容器 -->
<!-- <div ref="file"></div> -->
<el-button size="mini" type="primary" @click="daochu(message)">生成文档</el-button>
</div>
</template>
<script>
import * as fs from 'fs'
import Docxtemplater from 'docxtemplater'
import { exportWordDocx } from '@/utils/exportWordDocx.js'
import { exportWord } from '@/utils/exportFile.js'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { renderAsync } from 'docx-preview'
import {saveAs} from 'file-saver'
export default {
name: "Index",
data() {
return {
message :{
companyName: "公司", //公司名称 单位名称
companyAddress: "306号", //单位地址
companyEmail:'zj@rmtc.org.cn',// 电子邮件
companyTel:'0552',// 电话
companyTax:'0252',// 传真
companyECode:'3112',// 邮政编码
zihao:'浙6号', //字号什么的
projectNames:'广东样品', //字号什么的
subProjectName:'中性分析', //字号什么的
entrustedUnit:'广东限司',
detectionCategory:'测',
compilationDate:'2023年3月3日', //编制日期
sampleName:'气',
testItem:'空qi',
entrustedUnitName:'广东核公司',//委托单位名称
entrustedUnitAddress:'广1房',// 委托单位地址
entrustedUnitContact:'柳3',// 委托单位联系人
contactWay:'18876',// 联系方式
detectionWay:'实验析',
sampleSource:[
{ name:'自cai',checked:false },
{ name:'送jian',checked:true },
],
sampleTime:'207',// 日期
recvTime:'200',// 日期
status:'固体',// 状态
sampleCount:'1个',// 个数
sampleAmount:'重:0.5120g',// 量
anaDate:'2022.2.21-2.22',// 分析日期
fileCode:[
{ name:'水质1' },
{ name:'水质7' },
{ name:'水质7' },
],
detectionResult:'结论,改报告没啥问题',// 结论
instrumentInfo:{
instName:'数器', //名称
specifications:'M604',// 规格
instNum:'12-2019',// 编号
checkUnit:'研究院',// 单位
checkDate:'2023.4.27',// 有效期至
},
technical:{
relativeEff:[
{ name:'源(241或210)≥40%' },
{ name:'源(90/90)≥50%' }
],
backCountRate:'a<0.06,b<0.5'
}, //技术指标
envConditions:{
temp:'15~20℃',
hum:'20~40%'
},
detectionPlace:'杭号', // 检测地点
remark:'/', // 备注
tableHeader:["表头字段1","表头字段2","表头字段3","表头字段4","表头字段5","表头字段6"], // 学习表格
tableData:[
{ name:'数字1' },
{ name:'数字2' },
{ name:'数字3' },
{ name:'数字4' },
{ name:'数字5' },
{ name:'数字6' },
],
rawXml: `
<w:tbl xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
<w:tblPr>
<w:tblBorders>
<w:top w:val="single" w:sz="4" w:color="000000" /> <!-- 顶部边框 -->
<w:left w:val="single" w:sz="4" w:color="000000" /> <!-- 左侧边框 -->
<w:bottom w:val="single" w:sz="4" w:color="000000" /> <!-- 底部边框 -->
<w:right w:val="single" w:sz="4" w:color="000000" /> <!-- 右侧边框 -->
<w:insideH w:val="single" w:sz="4" w:color="000000" /> <!-- 水平内部边框 -->
<w:insideV w:val="single" w:sz="4" w:color="000000" /> <!-- 垂直内部边框 -->
</w:tblBorders>
<w:jc w:val="center"/> <!-- 居中整个表格 -->
</w:tblPr>
<!-- 表头行 -->
<w:tr>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:rPr>
<w:b /> <!-- 文字加粗 -->
</w:rPr>
<w:t>列1</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:rPr>
<w:b /> <!-- 文字加粗 -->
</w:rPr>
<w:t>列2</w:t>
</w:r>
</w:p>
</w:tc>
<!-- 更多表头列... -->
</w:tr>
<!-- 数据行 1 -->
<w:tr>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:t>数据1, 列1</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:t>数据1, 列2</w:t>
</w:r>
</w:p>
</w:tc>
<!-- 更多数据列... -->
</w:tr>
<!-- 数据行 2 -->
<w:tr>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:t>数据2, 列1</w:t>
</w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr>
<w:jc w:val="center" /> <!-- 文字居中 -->
</w:pPr>
<w:r>
<w:t>数据2, 列2</w:t>
</w:r>
</w:p>
</w:tc>
<!-- 更多数据列... -->
</w:tr>
<!-- 更多数据行... -->
</w:tbl>
`,
}
}
},
mounted(){
},
methods: {
daochu(e) {
let docxsrc = "/template.docx"; //模板文件的位置
let docxname = "工作日志.docx"; //导出文件的名字
// 读取并获得模板文件的二进制内容
JSZipUtils.getBinaryContent(docxsrc, function(error, content) {
// docxsrc是模板。我们在导出的时候,会根据此模板来导出对应的数据
// 抛出异常
if (error) {
throw error;
}
// 创建一个PizZip实例,内容为模板的内容
let zip = new PizZip(content);
// 创建并加载docx templater实例对象
let doc = new Docxtemplater().loadZip(zip);
// 设置模板变量的值
doc.setData({
...e, // e中的数据可以再模板中直接使用
...e.instrumentInfo,
...e.technical,
...e.envConditions
});
try {
// 用模板变量的值替换所有模板变量
doc.render();
} catch (error) {
// 抛出异常
let e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
};
console.log(JSON.stringify({ error: e }));
throw error;
}
// 生成一个代表Docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
let out = doc.getZip().generate({
type: "blob",
mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
// 将目标文件对象保存为目标类型的文件,并命名
saveAs(out, docxname);
});
}
},
}
</script>
<style scoped lang="scss">
</style>
template.docx存放的位置
效果图
导出表格带图片 图片请求失败提示
outword.js
/**
* 导出word文档(带图片)
*
*/
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'
/**
* 将base64格式的数据转为ArrayBuffer
* @param {Object} dataURL base64格式的数据
*/
function base64DataURLToArrayBuffer(dataURL) {
const base64Regex = /^data:image\/(png|jpg|jpeg|svg|svg\+xml);base64,/;
if (!base64Regex.test(dataURL)) {
return false;
}
const stringBase64 = dataURL.replace(base64Regex, "");
let binaryString;
if (typeof window !== "undefined") {
binaryString = window.atob(stringBase64);
} else {
binaryString = Buffer.from(stringBase64, "base64").toString("binary");
}
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
const ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes.buffer;
}
export const ExportBriefDataDocx = (tempDocxPath, data, fileName, imgSize) => {
console.log(111, tempDocxPath, data, fileName, imgSize)
//这里要引入处理图片的插件
var ImageModule = require('docxtemplater-image-module-free');
var expressions = require('angular-expressions')
var assign = require('lodash/assign')
var last = require("lodash/last")
expressions.filters.lower = function (input) {
// This condition should be used to make sure that if your input is
// undefined, your output will be undefined as well and will not
// throw an error
if (!input) return input
// toLowerCase() 方法用于把字符串转换为小写。
return input.toLowerCase()
}
function angularParser(tag) {
tag = tag
.replace(/^\.$/, 'this')
.replace(/(’|‘)/g, "'")
.replace(/(“|”)/g, '"')
const expr = expressions.compile(tag)
return {
get: function (scope, context) {
let obj = {}
const index = last(context.scopePathItem)
const scopeList = context.scopeList
const num = context.num
for (let i = 0, len = num + 1; i < len; i++) {
obj = assign(obj, scopeList[i])
}
//word模板中使用 $index+1 创建递增序号
obj = assign(obj, { $index: index })
return expr(scope, obj)
}
}
}
JSZipUtils.getBinaryContent(tempDocxPath, (error, content) => {
if (error) {
console.log(error)
}
expressions.filters.size = function (input, width, height) {
return {
data: input,
size: [width, height],
};
};
let opts = {}
opts = {
//图像是否居中
centered: true
};
opts.getImage = (chartId) => {
//将base64的数据转为ArrayBuffer
return base64DataURLToArrayBuffer(chartId);
}
opts.getSize = function (img, tagValue, tagName) {
//自定义指定图像大小
if (tagName=='signature') {
return [10,40];
} else {
return [75, 20];
}
}
// 创建一个JSZip实例,内容为模板的内容
const zip = new PizZip(content)
// 创建并加载 Docxtemplater 实例对象
// 设置模板变量的值
let doc = new Docxtemplater();
doc.attachModule(new ImageModule(opts));
doc.loadZip(zip);
doc.setOptions({parser:angularParser,nullGetter: function () { return '' }});
doc.setData(data)
try {
// 呈现文档,会将内部所有变量替换成值,
doc.render()
} catch (error) {
const e = {
message: error.message,
name: error.name,
stack: error.stack,
properties: error.properties
}
console.log('err',{ error: e })
// 当使用json记录时,此处抛出错误信息
throw error
}
// 生成一个代表Docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
const out = doc.getZip().generate({
type: 'blob',
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
})
let file = new File([out],`${fileName}.docx`,{ type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' })
// 将目标文件对象保存为目标类型的文件,并命名
saveAs(out, fileName)
})
}
/**
* 将图片的url路径转为base64路径
* 可以用await等待Promise的异步返回
* @param {Object} imgUrl 图片路径
*/
export function getBase64Sync(imgUrl) {
return new Promise(function (resolve, reject) {
// 一定要设置为let,不然图片不显示
let image = new Image();
//图片地址
image.src = imgUrl;
// 解决跨域问题
image.setAttribute("crossOrigin", '*'); // 支持跨域图片
// image.onload为异步加载
image.onload = function () {
let canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
let context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
//图片后缀名
let ext = image.src.substring(image.src.lastIndexOf(".") + 1).toLowerCase();
//图片质量
let quality = 0.8;
//转成base64
let dataurl = canvas.toDataURL("image/" + ext, quality);
//返回
resolve(dataurl);
};
// 图片加载失败时触发reject
image.onerror = function () {
reject(new Error('Failed to load image: ' + imgUrl));
};
})
}
使用