首页 > 其他分享 >记录--Vue中前端导出word文件

记录--Vue中前端导出word文件

时间:2023-10-16 19:55:25浏览次数:57  
标签:docx Vue word -- Paragraph new data children

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

很多时候在工作中会碰到完全由前端导出word文件的需求,因此特地记录一下比较常用的几种方式。

一、提供一个word模板

该方法提供一个word模板文件,数据通过参数替换的方式传入word文件中,灵活性较差,适用于简单的文件导出。需要依赖:docxtemplater、file-saver、jszip-utils、pizzip

 

import Docxtemplater from "docxtemplater";
import { saveAs } from "file-saver";
import JSZipUtils from "jszip-utils";
import PizZip from "pizzip";

export function downloadWithTemplate(path, data, fileName) {
  JSZipUtils.getBinaryContent(path, (error, content) => {
    if (error) throw error;

    const zip = new PizZip(content);
    const doc = new Docxtemplater().loadZip(zip);
    doc.setData({
      ...data.form,
      // 循环项参数
      list: data.list,
      outsideList: data.outsideList,
    });

    try {
      doc.render();
    } catch (error) {
      const e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties,
      };
      ElMessage.error("文件格式有误!");
      throw error;
    }
    const out = doc.getZip().generate({
      type: "blob",
      mimeType:
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    });
    saveAs(out, fileName);
  });
}

let data = {
    form: {
      title: "这是word标题",
      test: "这是表单1的数据",
      test1: "111",
      test2: 222,
      test3: 333,
    },
    outsideList: [
      {
        list: [
          {
            index: 0,
            table: "表格第一项",
            table1: "表格第二项",
            table2: "表格第三项",
          },
          {
            index: 1,
            table: "表格第一项",
            table1: "表格第二项",
            table2: "表格第三项",
          },
        ],
      },
      {
        list: [
          {
            index: 0,
            table: "表格第一项",
            table1: "表格第二项",
            table2: "表格第三项",
          },
          {
            index: 1,
            table: "表格第一项",
            table1: "表格第二项",
            table2: "表格第三项",
          },
        ],
      },
    ],
  };
  
  downloadWithTemplate("template.docx", data, "模板word.docx")
  

调用downloadWithTemplate方法即可导出如下文件:

注: 上述方法中的path参数为你在vue项目中存放公共文件的位置,在vue2中为static文件夹下,在vue3中为public文件夹下。

二、根据html代码转换为word文件(推荐)

顾名思义,这个方法就是将我们在页面上书写的html代码直接转换成word文件,这也是我最推荐的一种方法,因为大部分的样式可控,且毕竟是我们较为熟悉的方式。需要插件: html-docx-js-typescript、file-saver。

import { saveAs } from "file-saver";
import { asBlob } from "html-docx-js-typescript";

 export function downloadWordWithHtmlString(html, name) {
  let htmlString = `
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Document</title>
  </head>
  <body>
    ${html}
  </body>
  </html>
  `;
  asBlob(htmlString).then((data) => {
    saveAs(data, `${name}.docx`);
  });
}
  

使用案例:

<div ref="word">
  <h3 style="text-align: center">word标题</h3>
  <table
    border="1"
    cellspacing="0"
    width="600"
    style="font-size: 12px; color: #000; text-align: center"
  >
    <tr height="50">
      <td width="100">1111</td>
      <td widt="200" colspan="2">合并单元格</td>
      <td width="300">最长的一项</td>
    </tr>
    <tr height="100">
      <td width="100">222</td>
      <td width="100">222</td>
      <td width="100">222</td>
      <td width="100">222</td>
    </tr>
  </table>
  <table width="600" border="1" cellspacing="0">
    <tr height="50">
      <td width="100">1111</td>
      <td rowspan="3">合并包括此行在内的下面三行</td>
    </tr>
    <tr height="100">
      <td>222</td>
    </tr>
    <tr height="300">
      <td>3333</td>
    </tr>
    <tr>
      <td>50</td>
    </tr>
  </table>
</div>

let word = ref(null);
downloadWordWithHtmlString(word.value.innerHTML, 'html字符串word.docx');

生成的word文件可以看到效果和在网页中的html代码一样:

 另外需要注意的是,若是需要在word中添加分页符,在需要分页的内容处添加CSS属性page-break-before即可。此时在浏览器上打印出innerHTML值会发现:

mdn上介绍page-break-before属性已经被break-before属性替代,但是经过我实际测试发现当html字符串是page-break: always时生成的word文件没有分页效果,反而是将其替换回page-break-before后实现了分页效果。若有大神知道这是什么问题还望不吝赐教。 因此需要在downloadWordWithHtmlString方法中添加一句正则: htmlString = htmlString.replace( /break-(after|before): page/g, "page-break-$1: always;" );,此时就能实现分页效果。

三、使用docx插件

第二种方法有个很致命的问题就是它无法在生成的word文件中添加图片页眉,我搜遍了npm也只找到一个能添加文字页眉的插件: html-docx-ts。要想实现这个需求,就需要用到docx插件。 docx官网的介绍是"Easily generate and modify .docx files with JS/TS. Works for Node and on the Browser.",意味着是一个专门用于生成word和修改word的文件。该插件就需要一个一个去配置你要生成的项,然后组合成一个word。一个简单的案例是:

import {
  Document,
  Paragraph,
  Header,
  TextRun,
  Table,
  TableRow,
  TableCell,
  WidthType,
  Packer,
} from "docx";
import { saveAs } from "file-saver";

const document = new Document({
    sections: [
      {
        headers: {
          default: new Header({
            children: [new Paragraph("我是页眉")],
          }),
        },
        children: [
          new Paragraph({
            children: [
              new TextRun({
                text: "我是文字内容",
                size: 16,
                bold: true,
              }),
            ],
          }),
          new Table({
            columnWidths: [1500, 7500],
            rows: [
              new TableRow({
                children: [
                  new TableCell({
                    width: {
                      size: 1500,
                      type: WidthType.DXA,
                    },
                    children: [
                      new Paragraph({
                        alignment: "center",
                        children: [
                          new TextRun({
                            text: "测试",
                            size: 24,
                            font: {
                              name: "楷体",
                            },
                          }),
                        ],
                      }),
                    ],
                  }),
                ],
              }),
            ],
          }),
        ],
      },
    ],
  });
  
  Packer.toBlob(document).then((blob) => {
    saveAs(blob, "test.docx");
  });

导出的word文件形式为:

 面是我个人总结的比较常见能用到的功能和配置项:

// 导出文字
1.new Paragraph(text) -> 默认字体样式: 宋体,五号字
2.new Paragraph({
    children: [
      new TextRun({
        text: "我是文字内容",
        size: 16, // 对应word中的字体大小8
        bold: true, // 是否加粗
        underline: {
          type: UnderlineType.SINGLE,
          color: "#2e32ee",
        }, // 下划线类型及颜色
        font: {
          name: "仿宋", // 只要是word中有的字体类型都可以生效
        },
      }),
    ],
    indent: {
      left: 100,
    }, // 离左边距离 类似于margin-left
    spacing: {
      before: 150,
      after: 200,
    }, // 离上边和下边的距离 类似于margin-top/bottom
    alignment: "center", // 对齐方式
    pageBreakBefore: true, // 是否在这段文字前加入分页符
  })
  
 // 导出表格
new Table({
  columnWidths: [1500, 7500], // 表示单行有几项,总宽度是9000,对应宽度;
  rows: [
    new TableRow({
      children: [
        new TableCell({
          width: {
            size: 1500, // 需与columnWidths的第一项对应
            type: WidthType.DXA, // 官网的介绍是Value is in twentieths of a point
            // 因为表格的总宽度是以twips(每英寸的1/20)为单位进行计算的
          },
          children: [
            new Paragraph({
              alignment: "center",
              children: [
                new TextRun({
                  text: "测试",
                  size: 24,
                  font: {
                    name: "楷体",
                  },
                }),
              ],
            }),
          ],
        }),
        new TableCell({
          width: {
            size: 7500,
            type: WidthType.DXA,
          },
          children: [
            new Paragraph('ccc'),
          ],
          margins: {
            top: 500,
            bottom: 500,
            left: 500
          } // 类似于单元格内容的padding
        }),
      ],
    }),
  ],
})

// 导出图片
new Paragraph({
  children: [
    new ImageRun({
      data: "base64", // 图片需转成base64的形式
      transformation: {
        width: 100,
        height: 30,
      }, // 图片宽高
    }),
  ],
})

// 设置页眉页脚
headers: {
  default: new Header({
    children: [new Paragraph("我是页眉")],
  }),
},
footers: {
  default: new Footer({
    children: [new Paragraph("我是页脚")],
  }),
}

下面是一个完整的使用案例:

const document = new Document({
  sections: [
    {
      headers: {
        default: new Header({
          children: [
            new Paragraph({
              children: [
                new ImageRun({
                  data: "data:image/jpeg;base64,...",
                  transformation: {
                    width: 150,
                    height: 150,
                  },
                }),
              ],
            }),
          ],
        }),
      },
      footers: {
        default: new Footer({
          children: [new Paragraph("我是页脚")],
        }),
      },
      children: [
         new Paragraph("第一行直接默认形式"),
         new Paragraph({
           children: [
             new TextRun({
               text: "下一页",
             }),
           ],
           pageBreakBefore: true,
         }),
         new Table({
           columnWidths: [1500, 7500],
           rows: [
             new TableRow({
               children: [
                 new TableCell({
                   width: {
                     size: 1500,
                     type: WidthType.DXA,
                   },
                   children: [
                     new Paragraph({
                       alignment: "center",
                       children: [
                         new TextRun({
                           text: "测试",
                           size: 24,
                           font: {
                             name: "楷体",
                           },
                         }),
                       ],
                     }),
                   ],
                 }),
                 new TableCell({
                   width: {
                     size: 7500,
                     type: WidthType.DXA,
                   },
                   children: [
                     new Paragraph({
                       children: [
                         new ImageRun({
                           data: "data:image/jpeg;base64,...",
                           transformation: {
                             width: 150,
                             height: 150,
                           },
                         }),
                       ],
                     }),
                   ],
                   margins: {
                     top: 500,
                     bottom: 500,
                    left: 500,
                  },
                }),
              ],
            }),
          ],
        }),
      ],
    },
  ],
});

Packer.toBlob(document).then((blob) => {
  saveAs(blob, "test.docx");
});

此时导出的word文件如下:

 

本文转载于:

https://juejin.cn/post/7269022955471749131

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

标签:docx,Vue,word,--,Paragraph,new,data,children
From: https://www.cnblogs.com/smileZAZ/p/17768216.html

相关文章

  • 实验2 C语言分支与循环基础应用编程
    一、实验目的能正确使用if语句、switch语句实现分支结构能正确使用while语句、do...while语句、for语句实现循环结构能在具体问题场景中使用嵌套分支语句和嵌套循环语句能在具体问题场景中正确区分、使用continue和break能灵活、组合使用c语句编程解决简单应用问题二、实......
  • 实验2 C语言分支与循环基础应用编程
    练习1#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1374#defineN2465intmain(){intnumber;inti;srand(time(0));//以当前系统时间作为随机种子for(i=0;i<N;++i){number=rand()%(N2-N1......
  • 网络稳定之无线路由器的选择
    WI-FI的简介WI-FI的中文名又叫无线网络通信技术。基于IEEE802.11通信协议,允许带WI-FI功能的设备连接到无线局域网。WI-FI的信号传输距离传输距离和发射功率有很大的关系,商用设备(大型商场)一般在300米左右,家庭设备(家用无线路由器)在50米左右,小型迷你设备(随身WI-FI)在5米左右。无线......
  • 实验2_C语言分支与循环基础应用编程
    1.task_11#include<stdio.h>2#include<stdlib.h>3#include<time.h>45#defineN56#defineN13747#defineN246589intmain()10{11intnumber;12inti;1314srand(time(0));1516for......
  • 每日总结
    今天学习了算法的基础。(1)线性结构每个元素前、后最多都只能有一个节点,如:线性表、栈、队列、数组、串(2)非线性结构如:二维数组、多维数组、树、图等存储结构顺序存储  链接存储队列和栈:队列先进先出,栈先进后出。树:满二叉树,完全二叉树,非完全二叉树。以及二叉树......
  • 【gdb】为fork调用设置catchpoint
    为fork调用设置catchpoint1.例子:#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<unistd.h>intmain(void){pid_tpid;pid=fork();if(pid<0){exit(1);}elseif(pid>0){exit(......
  • 最近电脑系统更新,IE打开又是Microsoft Edge浏览器该怎么办?
    注:最近系统更新,大家都有可能遇到打开IE时,会默认弹到MicrosoftEdge浏览器,这样会导致有些网页不能下载插件使用,兼容性太差,今天特意记录下来如何设置,可以使用IE浏览器。第二种还原设置方法,按照第一种修改IE浏览器高级选项,第三方浏览器拓展,并不生效,还是无法打开IE,这边记录一下如何还......
  • pymysql模块
    pymysql模块是python3中用于操作mysql数据库的一个库,不是自带模块,需要运行pipinstallpymysql进行安装。数据库的操作无非连接、查询、增、删、改:示例:1importpymysql23#打开数据库连接4conn=pymysql.connect(5host="localhost",6user="sq_test......
  • 144-18 中序创建线索二叉树
    同理,先序创建线索二叉树只需要将InThread中的某部分调换位置死记硬背#include<stdio.h>#include<stdlib.h>typedefstructnode{intdata;structnode*lchild,*rchild;intlefttag,righttag;}TreeNode,*Tree;voidCreateTree(Tree&T)//先序......
  • 【Unity代码片段】Unity在Unity中显示帧率
    在Unity中显示帧率1.将脚本挂在于一个物体,2.为FPS赋值一个UnityEngine.UI.Texttext运行即可FrameUpdata是帧数的在UI上的更新速度。usingUnityEngine;usingUnityEngine.UI;///<summary>SimpleFPS-Counter.</summary>[DisallowMultipleComponent]public......