vue 组装下载 word文档

vue 组装下载 word文档

记录一下遇到的问题,要求生成合同信息文档,有word模板,只需要把固定的数据填充进去,进行字段替换,组装生成并下载文档。话不多说,开始吧

1、安装所需插件

npm install docxtemplater pizzip jszip-utils
  • docxtemplater: docxtemplater 是一个邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入图像html表格,这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件。
  • pizzip: 一个使用 Javascript 创建、读取和编辑 .zip 文件的库,具有可爱且简单的 API,这个插件用来创建,读取或编辑.zip的文件(同步的,还有一个插件是jszip,异步的)。
  • jszip-utils: 与jszip/pizzip一起使用,jszip-utils 提供一个getBinaryContent(path, data)接口,path即是文件的路径,支持AJAX get请求,data为读取的文件内容。

2、准备模板文件

准备一个word文档,在需要替换文字的地方添加标识:

在这里插入图片描述

如图,此处在红色方框中有两个标识{aname}{bname} ,运行时代码会替换掉此处的标识。

docxtemplater基本语法:

{%img} 图片

{#list}{/list} 循环、if判断

{#list}{/list}{^list}{/list} if else

 {str} 文字

但是如果遇到需要打对勾的地方,那就要进行判断了

在这里插入图片描述

实现如下:传入的变量allowedtrue时,才会渲染打的效果。此时使用if else语句,在后面加入{^allowed}{/allowed}

在这里插入图片描述

表格渲染的话就需要用到循环语句,可以在上面docxtemplater基本语法找到:

在这里插入图片描述

渲染这个表格的话,需要在每行的开头和结尾加入表格数据数组变量名称,比我把表格数组放在了table数组中所以循坏就要写成{#table} ... ... {/table}

在这里插入图片描述

3、准备代码

<script>
import Docxtemplater from 'docxtemplater';
import PizZip from 'pizzip';
import JSZipUtils from 'jszip-utils';

export default {
  methods: {
    async generateWordDocument() {
      // 导入模板数据
      const templateURL = process.env.BASE_URL + 'template.docx';
      const templateContent = await JSZipUtils.getBinaryContent(templateURL);
      const zip = new PizZip(templateContent);
      const doc = new Docxtemplater().loadZip(zip);
  
      // 准备数据
      const data = {
        aname: "这是第一个名字", // 用实际的数据替代
        bname: "这是第二个名字" // 用实际的数据替代
      };
  
      // 填充数据
      doc.setData(data);
      doc.render();
  
      // 生成并下载文档
      const outputBlob = doc.getZip().generate({ type: 'blob' });
      const downloadLink = document.createElement('a');
      downloadLink.href = URL.createObjectURL(outputBlob);
      downloadLink.download = '交易合同.docx';
      downloadLink.click();
    }
  }
};
</script>

先写一个Demo稍微测试一下,发现可以成功替换掉{aname}{bname}两个变量,那么咱们就继续改造一下,把他变成一个组件,随时取用(包裹buttonname变量的$t()函数是国际化多语言函数,使用时根据自己的情况去除或者修改)。

<template>
  <div>
    <!-- 生成Word文档按钮 -->
    <el-button :size="size" :style="styleobj" :type="type" @click="generateWordDocument">{{ $t(buttonname) }}</el-button>
  </div>
</template>

<script>
import Docxtemplater from 'docxtemplater';
import PizZip from 'pizzip';
import JSZipUtils from 'jszip-utils';
import { purchaseorderDetail } from '@/api/order';

export default {
  name: 'DownloadWord',
  props: {
    /**
     * 查询id
     */
    id: {
      type: [String, Number],
    },
    /**
     * 按钮大小尺寸
     */
    size: {
      type: String,
      default: 'small'
    },
    /**
     * word模板名字
     */
    docname: {
      type: String,
      default: 'template.docx'
    },
    /**
     * 生成下载文档名字
     */
    downloadname: {
      type: String,
      default: '交易合同.docx'
    },
    /**
     * 按钮style样式
     */
    styleobj: {
      type: Object,
      default: () => ({
        color: '#206266',
      })
    },
    /**
     * 按钮名称
     */
    buttonname: {
      type: String,
      default: '下载合同'
    },
    /**
     * 按钮类型
     */
    type: {
      type: String,
      default: 'text'
    }
  },
  data() {
    return {
      // 使用默认值初始化 wordData 对象
      wordData: {}
    }
  },
  methods: {
    /**
     * 生成 Word 文档并下载
     * @async
     * @author zhuzhihui
     * @date 2023/08/29
     */
    async generateWordDocument() {
      try {
        const res = await purchaseorderDetail(this.id);
        if (res.status === 200) {
          // 使用响应数据更新 wordData 属性
          this.wordData = this.prepareWordData(res.data);

          // 加载并渲染模板
          const doc = await this.loadAndRenderTemplate();

          // 生成并下载文档
          this.generateAndDownloadDocument(doc);
        } else {
          this.$message.error(res.message);
        }
      } catch (error) {
        console.error(error);
        this.$message.error('生成文档时出错。');
      }
    },

    /**
     * 准备填充到 Word 模板的数据
     * @param {Object} data - 响应数据
     * @returns {Object} - 填充数据
     * @author zhuzhihui
     * @date 2023/08/29
     */
    prepareWordData(data) {
      // 准备并返回 wordData 对象
      const {
        supplier_name,
        purchase_name,
        create_time,
        buy_address,
        departure,
        destination,
        documents,
        more_less,
        latest_ship_date,
        terms_delivery,
        commodity_inspection_agency,
        commodity_inspection_report,
        shipment_method,
        partial_shipment,
        commodity_inspection,
        product_info  // 商品信息
      } = data;
      const wordData = {
        supplier_name,  // 采购商名称
        purchase_name,  // 供应商名称
        total_price: 0,  // 总价
        sea: shipment_method == 1,  // 是否海运
        train: shipment_method == 2,  // 是否火车运输
        air: shipment_method == 3,  // 是否空运
        truck: shipment_method == 4,  // 是否卡车运输
        transport: shipment_method == 5,  // 是否联合运输
        buy_address,  // 收货地址
        allowed: partial_shipment == 1,  // 是否分批装运
        not_allowed: partial_shipment != 1,  // 是否不分批装运
        inspect: commodity_inspection == 1,  // 是否需要商检
        departureBool: terms_delivery == 'FOB' || terms_delivery == 'FCA',  // 是否[起运地/目的地]条款为FOB/FCA
        destinationBool: terms_delivery != 'FOB' && terms_delivery != 'FCA',  // 取反[起运地/目的地]条款为FOB/FCA
        departure,  // 起运地
        destination,  // 目的地
        documents,  // 单据
        create_time,  // 协议生成日期
        more_less,  // 短溢装比例
        latest_ship_date,  // 最迟到装运日期
        terms_delivery,  // 交货条条款
        commodity_inspection_agency,  // 商检机构
        commodity_inspection_report,  // 商检报告
        table: []  // 表格数据
      }
      
      // 准备表格数据
      wordData.total_price = 0
      wordData.table = []
      product_info.forEach(item => {
        let cost_total_price = ( Number(item.buy_num) * Number(item.cost_price) ).toFixed(2)
        wordData.total_price = (Number(wordData.total_price) + (Number(item.buy_num) * Number(item.cost_price))).toFixed(2)
        wordData.table.push({
          product_name: item.product_name,
          spec: item.spec,
          unit_name: item.unit_name,
          buy_num: item.buy_num,
          cost_price: item.cost_price,
          cost_total_price
        })
      })
      
      return wordData;
    },

    /**
     * 加载并渲染 Word 模板
     * @async
     * @returns {Docxtemplater} - 加载渲染后的文档模板
     * @author zhuzhihui
     * @date 2023/08/29
     */
    async loadAndRenderTemplate() {
      const templateURL = process.env.BASE_URL + this.docname;
      const templateContent = await JSZipUtils.getBinaryContent(templateURL);
      const zip = new PizZip(templateContent);
      return new Docxtemplater().loadZip(zip);
    },

    /**
     * 生成并下载文档
     * @param {Docxtemplater} doc - 加载渲染后的文档模板
     * @author zhuzhihui
     * @date 2023/08/29
     */
    generateAndDownloadDocument(doc) {
      // 填充并渲染模板
      doc.setData(this.wordData);
      doc.render();

      // 生成并下载文档
      const outputBlob = doc.getZip().generate({ type: 'blob' });

      const downloadLink = document.createElement('a');
      downloadLink.href = URL.createObjectURL(outputBlob);
      downloadLink.download = this.downloadname;
      downloadLink.click();

      // 重置 wordData 属性
      this.wordData = {};
    },
  }
};
</script>

到此为止,一个下载word文档的按钮组件就大功告成了,从请求接口到产出文档一气呵成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值