简介: docx是用于前端自己去渲染word内容并下载word的使用,这是根据项目无法固定word内容,如果有用到这个插件,我的笔记无法达到所需还请去参考官网,
再次补充下:我所做的笔记可能会有问题或者不够深入,也希望用此插件的用户进行补充和指导。在此非常感谢!
- new Document() // 创建一个文档,里面可以设置文档的样式,文档的作者、标题、描述等
import { Document} from "docx"
所有的样式都是在new Document中创建的
import { AlignmentType, Document, Paragraph, Border, UnderlineType } from 'docx'
export const document = {
creator: "Summy", // 作者
title: "Sample Document", // 标题
description: "A brief example of using docx", // 描述
// 以下是设置全局颜色的地方
styles: {
paragraphStyles: [ // 段落样式
{
id: "Heading1",
name: "Heading 1",
basedOn: "Normal",
next: "Normal",
quickFormat: true,
run: {
size: 36,
bold: true,
italics: true,
color: "#F87654",
margin: {
top: 500,
bottom: 300
}
},
paragraph: { // 段落
spacing: { // 字间距
after: 120
},
}
},
{
id: "Heading2",
name: "Heading 2",
basedOn: "Normal",
next: "Normal",
quickFormat: true, // 快速格式化
run: {
size: 18, // 字号
bold: true, // 加粗
color: '#CB0000'
// underline: { // 设置下划线 DOUBLE是双下划线
// type: UnderlineType.DOUBLE,
// color: "FF0000"
// }
font: {
name: "Garamond" // 设置字体
}
},
paragraph: {
spacing: {
before: 240,
after: 120
}
},
},
{
id: "aside",
name: "Aside",
basedOn: "Normal",
next: "Normal",
run: {
color: "999999",
italics: true
},
paragraph: {
indent: {
left: 720
},
spacing: {
line: 276
}
},
},
{
id: "Foote",
name: "Foote",
basedOn: "Normal",
quickFormat: true,
run: {
size: 14, // 字号
bold: true, // 加粗
color: '#CB0000'
}
}
]
},
numbering: { // 设置项目编号
config: [ // 配置
{
reference: "my-crazy-numbering", // 参考
levels: [ // 水平
{
level: 0,
format: "lowerLetter",
text: "%1)",
alignment: AlignmentType.LEFT // 左右居中 AlignmentType.LEFT(RIGHT CENTER)
}
]
}
]
}
}
在styles中的各个参数:
paragraph: {
indent: {
left: 720
},
spacing: {
line: Number,
before: Number,
after: Number,
lineRule: Number
}
},
注: 在Document中设置的样式,我们除了HEADING_[1-6] 、TITLE
使用方式: heading: HeadingLevel.HEADING_1 这样的形式去读取
设置其他命名的方式,我们不能通过HeadingLevel的形式去使用,而是直接通过样式中的id名去设置样式,字体大小等等。
例:
{
id: "Foote",
name: "Foote",
basedOn: "Normal",
quickFormat: true,
run: {
size: 14, // 字号
bold: true, // 加粗
color: '#CB0000'
}
}
// 在使用时:
new Paragraph({
text: '我是word',
heading: 'Foote',
thematicBreak: true, // 设置这个会有下划线
})
- new Paragraph 是设置段落的,而newParagraph的使用方式:
import { Paragraph } from "docx"
(1)
new Paragraph("我是word")
(2)
new Paragraph({
text: "",
children: [
new TextRun("我是name") // new TextRun单独设置是不生效的,必须放在Paragraph的children属性中使用才生效
]
})
- new TextRun 是放在Paragraph中使用的
import { TextRun } from "docx"
new Paragraph 和 new TextRun的区别:
通俗来讲:newParagraph属于块级块级元素,它占用一整行,而new TextRun属于行内元素,有多个new TextRun就是占用在一条水平线上。
- 设置页眉页脚
import { Header, Footer } from 'docx'
const doc = new Document(document)
doc.addSection({
headers: { // 设置页眉
default: new Header({
children: [
new TextRun({
text: '信用报告分析'
})
]
})
},
footers: { // 设置页脚
new Footer({
children: [
new TextRun({
text: '信用报告分析'
style: 'Foote'
// 设置样式,Foote是在Document中设置的样式,
// 直接拿过来id名就可以生效,new Document的设
// 置样式在第一条总结中已经有实例了
})
]
})
},
children: children
})
- 插入图片 引入Medio
import { Media } from "docx"
使用介绍:
下面的this.generatoreFromUrl函数的内容:
// 把图片转换为二进制
generaterFromUrl(){
const blob = fetch(url).then(res => res.blob())
return blob
}
const doc = new Document(document) // document是已经封装的参数
const images = Media.addImage(doc, this.generaterFromUrl(require('图片路径')),200,200,{
floating: {
horizontalPosition: {
offset: 1014400
},
verticalPosition: {
offset: 507300
}
}
})
Media.addImage()方法的参数介绍:
参数一: doc —— new Document()
参数二: 引入图片路径,把图片转换为blob二进制
参数三: 图片的宽 number类型
参数四: 图片的高 number类型
参数五: 图片的定位 (水平和垂直定位) 是一个对象
const aa = async function generateFromUrl(url) {
const blob = await fetch(url).then(r => r.blob())
return blob
}
// 引入img 使用:Media.addImage
// 参数说明:
// 参数1: new Document
// 参数2: 把图片路径,转换为blob二进制流 function封装
// 参数3: 图片的宽度 number
// 参数4: 图片的高度 number
// 参数5: 图片的定位 对象形式 {floating:{horizontalPosition: 222,//(水平定位) verticalPosition: 222,//(垂直定位)}}
const image2 = Media.addImage(doc, this.generateFromUrl(require('@/assets/logo_img.png')), 50, 30, {
// 注意 : 引入图片路径的时候必须采用require引入,否则不会生效。
floating: {
horizontalPosition: {
offset: 1014400
},
verticalPosition: {
offset: 507300
}
}
});
- 设置项目编号
在new Document()中设置:
levels中对象的属性介绍
level: 这是定义堆栈中从0开始的索引
numberFormat: 这只是应如何生成项目符号或编号。
选项包括bullet(意为不计)、decimal(阿拉伯数字)、upperRoman、lowerRoman、hex等
样式描述:
decimal —— 1、2…
hex —— 1、2…
upperRoman —— l、 ll…
lowerRoman —— i、ii…
upperLetter —— A、B、…
lowerLetter —— a、b、…
这个就是代码所示的:format: “”
levelText: 这是一个格式字符串,使用numberFormat函数输入,生成要插入列表中每个项目之前的字符串。
例: text: “%1)” // 这个是根据在format中设置的内容而变的,例如代码所示中format: “lowerLetter”, text: “%1)” 生成的序列为a)…
要显示的序列:
%1) 1) 2) …
%2. 1. 2. …
%3、 1、 2、 …
alignment: AlignmentType.LEFT // 这个就是左右居中的问题了
注意:在使用是要想1-2的排序下去,在
new Paragraph({
text: "",
numbering:{
reference: "my-crazy-numbering" // 是document中的reference属性值
level: 1
}
})
中 numbering中的level必须是使用相同的序列号才能排序,否则他们会认为是新一轮的排序
在numbering:{}中只是设置项目编号,要想设置圆点点或者方块样式的序列需要使用bullet:{},这两个的区别在于一个设置项目编号,一个设置的类似与无序列表,而且还有一个区别是: bullet不需要在document中配置。
new Paragraph({
text: "Hey you",
// numbering: { // 设置序号
// reference: "my-crazy-numbering",
// level: 2,
// },
bullet: { // 设置圆点点
level: 0,
}
}),
如下图所示:
使用代码所示:
document中配置
const doc = new Document({
numbering: { // 设置项目编号
config: [ // 配置
{
reference: "my-crazy-numbering", // 参考
levels: [ // 水平
{
level: 0,
format: "lowerLetter",
text: "%1)",
alignment: AlignmentType.LEFT, // 左右居中 AlignmentType.LEFT(RIGHT CENTER)
style: {
run: {
size: 32, // 字号
bold: true, // 加粗
color: '#000000'
}
}
}
]
}
]
}
})
使用:
new Paragraph({
text: "Hey you",
numbering: {
reference: "my-crazy-numbering",
level: 1,
},
})
注:在new Paragraph中如果设置了编号numbering,那style就不生效了
例:
// 基本简介
new Paragraph({
text: "我是标题",
style: HeadingLevel.TITLE, // 不过heading: HeadingLevel.TITLE也不可以
numbering: {
reference: "serial_number",
level: 0,
},
})
- 表格的使用
import { Table, TableRow, TableCell } from 'docx'
const table = new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("Hello")],
}),
new TableCell({
children: [],
})
]
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [new Paragraph("World")],
})
]
})
]
})
const doc = new Document(table)
..... // 省略
单元格的属性:
new TableCell({
children: [new Paragraph("World")],
// width: { // 设置表格的宽度
// size: 24,
// type: WidthType, // type 类型: AUTO DXA NIL PCT NUMBER
// },
// cantSplit: true, // 防止分页
// verticalAlign: VerticalAlign.CENTER, // 单元格中文本垂直居中
// textDirection: TextDirection.BOTTOM_TO_TOP_LEFT_TO_RIGHT, // 单元格中的文本方向对齐
// textDirection: TextDirection.TOP_TO_BOTTOM_RIGHT_TO_LEFT, // 单元格中的文本方向对齐
// margins: { // 单元格中的距离
// top: convertInchesToTwip(0.69),
// bottom: convertInchesToTwip(0.69),
// left: convertInchesToTwip(0.69),
// right: convertInchesToTwip(0.69),
// },
// shading: { // 阴影设置
// fill: "b79c2f",
// val: ShadingType.REVERSE_DIAGONAL_STRIPE, // ShadingType.PERCENT_95 ShadingType.PERCENT_10 ShadingType.CLEAR
// color: "auto",
// },
// float: { // 浮动设置
// horizontalAnchor: TableAnchorType.MARGIN,
// verticalAnchor: TableAnchorType.MARGIN,
// relativeHorizontalPosition: RelativeHorizontalPosition.RIGHT,
// relativeVerticalPosition: RelativeVerticalPosition.BOTTOM,
// overlap: OverlapType.NEVER,
// },
// rowSpan: [NUMBER_OF_CELLS_TO_MERGE], // 行合并单元格 rowSpan:3 (合并行三个单元格)
// columnSpan: [NUMBER_OF_CELLS_TO_MERGE], // 列合并单元格 columnSpan: 3(合并列三个单元格) // 经实践,其实在docx插件中columnSpan这是行合并
/SSSborders: { // 自定义边框
// top: {
// style: BorderStyle.DASH_DOT_STROKED,
// size: 3,
// color: "red"
// },
// bottom: {
// style: BorderStyle.DOUBLE,
// size: 3,
// color: "blue"
// },
// left: {
// style: BorderStyle.DASH_DOT_STROKED,
// size: 3,
// color: "green"
// },
// right: {
// style: BorderStyle.DASH_DOT_STROKED,
// size: 3,
// color: "#ff8000"
// }
// }
}),
// new TableCell({ // 添加图像
// children: [new Paragraph(image)],
// })
const table = new Table({
rows: [
new TableRow({
})
],
// import { TableLayoutType } from 'docx'
layout: TableLayoutType.FIXED, // 布局 TableLayoutType有两个属性,一个是FIXED 一个是AUTOFIT
})
设置表格背景和字体颜色
在DOCX中设置表格背景是通过shading来设置的,而字体的颜色和样式还是document中通过id来设置
代码示例:
new TableCell({
children: [
new Paragraph({ // 字体还是在document中设置
text: 'World',
style: 'table',
shading: { // 设置背景颜色或者背景样式
// type: ShadingType.REVERSE_DIAGONAL_STRIPE,
// type: 'nomal',
color: "#FFFFFF", // 设置字体颜色
fill: "#1AA034", // 设置背景颜色
}
})
]
})
注:
给表格设置宽度:
要想给表格整体设置宽度它不会生效,而且给表格设置了layout布局这个属性,这个属性一共有两个参数:一个是FIXED 另一个是AUTOFIT(默认属性)。结果宽度不会生效,而表格的layout属性会导致表格成,下图所示:
所以我们要给表格中的单元格单独设置:
注:单独只设置width是不生效的,所以得带上layout属性,值为FIXED的才能生效,如下代码:
new TableCell({
children: [
new Paragraph({
text: 'World',
style: 'table',
size: 80,
shading: { // 这个不能通过style样式中去设置颜色,需要通过shading这个属性来设置颜色
// type: ShadingType.REVERSE_DIAGONAL_STRIPE,
// type: 'nomal',
color: "#FFFFFF", // 设置字体颜色
fill: "#1AA034", // 设置背景颜色
},
})
],
width: {
size: 4535,
type: WidthType.DXA,
},
layout: TableLayoutType.FIXED,
})
效果展示:
注:要想给单元格背景颜色,就要给到new TableCell中
例:
new TableCell({
children: [
new Paragraph({
text: item,
style: 'table',
shading: { // 在new Paragraph中设置shading就是给文字设置背景颜色了
color: "#FFFFFF", // 设置字体颜色
fill: "#CB0000", // 设置背景颜色
}
})
],
margins: {
top: 800,
bottom: 800
},
shading: { // 在new TableCell中设置shading是给表格的单元格设置背景颜色了
color: "#FFFFFF", // 设置字体颜色
fill: "#CB0000", // 设置背景颜色
},
width: {
size: index === 0 || index === '0' ? 4535 : 2535,
type: WidthType.DXA,
},
layout: TableLayoutType.FIXED,
})
// 但给new TableRow设置shading没有生效哦
知识延伸: 如果想要单元格内有距离(margin) 不能在new TableCell()中设置margins属性,因为不会生效,只能在new Paragraph()中的style样式中设置,记住,也不能直接在new Paragraph()直接设置margins属性,必须在style中的paragraph这个属性下设置spacing设置单元格的距离:
例:
- 如果想要不在word文档中写入信息,想要跳过这一页就使用:
new PageBreak()
知识延伸:设置边框的style样式的各个参数:
DASHED: "dashed"
DASH_DOT_STROKED: "dashDotStroked"
DASH_SMALL_GAP: "dashSmallGap"
DOTTED: "dotted"
DOT_DASH: "dotDash"
DOT_DOT_DASH: "dotDotDash"
// 以上为虚线
DOUBLE: "double"
DOUBLE_WAVE: "doubleWave"
// 以上为双线
INSET: "inset" // 插图
NIL: "nil" // 零
NONE: "none" // 不要
OUTSET: "outset" // 开始
SINGLE: "single" // 单 这个是设置单线的
// 以下加厚
THICK: "thick"
THICK_THIN_LARGE_GAP: "thickThinLargeGap"
THICK_THIN_MEDIUM_GAP: "thickThinMediumGap"
THICK_THIN_SMALL_GAP: "thickThinSmallGap"
THIN_THICK_LARGE_GAP: "thinThickLargeGap"
THIN_THICK_MEDIUM_GAP: "thinThickMediumGap"
THIN_THICK_SMALL_GAP: "thinThickSmallGap"
THIN_THICK_THIN_LARGE_GAP: "thinThickThinLargeGap"
THIN_THICK_THIN_MEDIUM_GAP: "thinThickThinMediumGap"
THIN_THICK_THIN_SMALL_GAP: "thinThickThinSmallGap"
THREE_D_EMBOSS: "threeDEmboss"
THREE_D_ENGRAVE: "threeDEngrave"
// 以上都是加厚的
TRIPLE: "triple" // 三倍
WAVE: "wave" // 波浪
使用方式:
borders: {
top: {
style: BorderStyle.SINGLE,
size: 3,
color: "#F4B083"
},
.
.
.
}
知识延伸:
在我使用的时候发现margins中的left和right属性不生效,所以在docx中要想有左右边距,则需要使用header和footer就可以设置左右边距了
合并单元格示例:
new Table({
rows: [
new TableRow({
children: [
new TableCell({
children: [new Paragraph("4,0")],
columnSpan: 2,
}),
new TableCell({
children: [new Paragraph("4,5")],
}),
],
}),
new TableRow({
children: [
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
new TableCell({
children: [],
}),
],
}),
]
})
- docx导出echarts生成的图片
在main.js中生成全局的$echarts后:
我在其它组件中使用:
代码示例:
// 这是echarts图表的组件
<template>
<div>
我是echarts
<div ref="Echarts" style="width: 600px;height:400px;"></div>
</div>
</template>
<script>
export default {
name: "EchartsImage",
data(){
return {
}
},
mounted() {
this.myEcharts()
},
methods: {
// 这个是把绘制成图片的路径全部全换成可以下载的blob格式
blobHandler(dataurl) {
var arr = dataurl.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
},
myEcharts() {
const myCharts = this.$echarts.init(this.$refs.Echarts)
// 简单的数据,我这个option只是示例:在实际使用中我们要把
// option封装成公共的,方便后期生成不同的echarts图片插入到docx中
const option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20],
animation: false
}],
}
myCharts.setOption(option)
const picInfo = myCharts.getDataURL() // getDataURL把图片获取到二进制流
const images = this.blobHandler(picInfo)
this.$emit('downloadImage', {value: images})
}
}
}
</script>
// 这是引入以上echarts组件的组件代码:
<template>
<div>
<el-button @click="downLoadHandler">下载文档</el-button>
<echarts-image @downloadImage="downloadImage"/>
</div>
</template>
<script>
import About from "./About";
import { saveAs } from "file-saver";
import { AlignmentType, Document, HeadingLevel, Packer, TextRun, Paragraph, Header, Footer, Media, BorderStyle, Table, TableRow, TableCell } from 'docx'
import { document } from './download/style'
import { headData, footData } from './download/headAndFoot'
import children from './download/children'
import EchartsImage from './EchartsImage'
export default {
name: "Mine",
data() {
return {
count: 0,
doc: null,
part2Datas: [
{
type: 'charts',
loadDone: 0,
data: [
{
name: '中期票据',
percent: '40.00',
value: 2,
index: 0,
title: '新发产业债债券品种分布',
data: [
{
name: '中期票据',
percent: '40.00',
value: 2,
index: 0
},
{
name: '定向工具',
percent: '20.00',
value: 1,
index: 1
},
{
name: '政府支持机构债',
percent: '20.00',
value: 3,
index: 2
},
{
name: '资产支持证券',
percent: '20.00',
value: 4,
index: 4
}
]
},
{
name: '定向工具',
percent: '20.00',
value: 1,
index: 1,
title: '新发产业债债券品种分布',
data: [
{
name: '中期票据',
percent: '40.00',
value: 2,
index: 0
},
{
name: '定向工具',
percent: '20.00',
value: 1,
index: 1
},
{
name: '政府支持机构债',
percent: '20.00',
value: 3,
index: 2
},
{
name: '资产支持证券',
percent: '20.00',
value: 4,
index: 4
}
]
},
{
name: '政府支持机构债',
percent: '20.00',
value: 3,
index: 2,
title: '新发产业债债券品种分布',
data: [
{
name: '中期票据',
percent: '40.00',
value: 2,
index: 0
},
{
name: '定向工具',
percent: '20.00',
value: 1,
index: 1
},
{
name: '政府支持机构债',
percent: '20.00',
value: 3,
index: 2
},
{
name: '资产支持证券',
percent: '20.00',
value: 4,
index: 4
}
]
}
],
index: 0,
title: '新发产业债债券品种分布',
width: 3
}
],
};
},
components: {
EchartsImage,
About
},
mounted(){
this.doc = new Document(document);
},
watch: {
loadDone: {
// 企业剖析获取错误时,债券概览部分数据接收完毕就进行导出
handler: function(val) {
if (val === 2) {
this.downLoadHandler()
}
}
}
},
methods: {
generateFromUrl(url) {
const blob = fetch(url).then(r => r.blob())
return blob
},
downLoadHandler() {
const image1 = Media.addImage(this.doc, this.images, 700, 700)
const image2 = Media.addImage(this.doc, this.generateFromUrl(require('@/assets/logo_img.png')), 50, 30, {
floating: {
horizontalPosition: {
offset: 1014400
},
verticalPosition: {
offset: 507300
}
}
});
const head = [
new Paragraph(image2),
new Paragraph({
children: [
new TextRun({
text: '信用报告分析'
})
],
alignment: AlignmentType.RIGHT
})
]
const foot = [
new Paragraph({
text: '本报告违约概率评估数据来自CreditMatrix系统,欲了解详细分析过程请访问(https://cm.mszq.com)',
style: 'Foote',
alignment: AlignmentType.RIGHT
})
]
this.doc.addSection({
headers: headData(head),
footers: footData(foot),
children: [
// ...children,
new Paragraph(image1)
// this.images
]
})
Packer.toBase64String(this.doc)
Packer.toBlob(this.doc).then((buffer) => {
saveAs(buffer, '文档.docx')
}).catch(err => {
this.$message.error('下载失败');
})
},
downloadImage(val) {
const { value } = val;
// 在我拿过来的数据中我必须要用docx中的Media.addImage()过一下才能在docx中插入文档,否则不会在docx中生效
this.images = value
}
},
}
</script>
导出图片的代码这块儿只是示例,所以很粗糙,所以在实际使用是,自己做封装
遇坑:
在我导出echarts图表会发生一个问题,是因为echarts这个组件默认是有动画效果的,而我没有设置为false则导致我导出的word文档是坐标轴,但是却没有图形的各个数据。
解决:
在option这个对象中的series内设置animation: false 就完成了
10. 点下“按钮”,第一次是正常的,在不刷新的情况下,docx会累计数据,创建重复的页面,所以我设置在点击后,清空一下addSection,
清空方法:
this.doc.addSection = () => {
return {}
}
注:在使用docx插件时,可以创建多个addSection,但是我为了方便不用重复写页眉页脚,所以我只写了一个addSection,所以这个到时候的清空方法也是不同
exportDoc() {
const logo = Media.addImage(
this.doc,
generateUrl(require('@/assets/logo.png')),
110,
28,
{
floating: {
horizontalPosition: {
offset: 500000
},
verticalPosition: {
offset: 520000
}
}
}
)
// 页头
const headData = [
new Paragraph(logo),
new Paragraph({
children: [
new TextRun({
text: '信用报告分析'
})
],
thematicBreak: true,
style: 'Head',
alignment: AlignmentType.RIGHT
})
]
// 页尾
const footData = {
text:
'本报告违约概率评估数据来自CreditMatrix系统,欲了解详细分析过程请访问(https://cm.mszq.com)',
style: 'Foote',
alignment: AlignmentType.RIGHT
}
this.doc.addSection({
size: {
// PROTRAIT(纵向)、LANDSCAPE(横向)
orientation: PageOrientation.PROTRAIT // 纸张方向
},
margins: {
top: 720,
bottom: 720,
header: 720,
footer: 720,
left: 720,
right: 720
},
properties: {},
headers: headAndFoot.headersFun(headData),
footers: headAndFoot.footersFun(footData),
children: [
...this.introductionHandler(), // 基本简介
...this.creditHandler(), // 信用全览
...this.serviceHandler(), // 偿债能力
...this.financialHandler(), // 财务指标
...this.ratingHandler(), // 外部评级
...this.publicHandler(), // 公司近期负面舆情
...this.images2,
...this.liabilityHandler(), // 免责声明
...this.appendixHandler(), // 附录
...this.images
]
})
// 下载
Packer.toBase64String(this.doc)
Packer.toBlob(this.doc).then((buffer) => {
saveAs(buffer, '贵人鸟股份有限公司信用分析报告.docx')
})
// 清除docx文档
this.doc.addSection = () => {
return {}
}
}
参考:
API: https://docx.js.org/#/?id=welcome
GitHub: https://github.com/dolanmiu/docx