前端生成指定模板的word文件

需求:前端生成word文件

实现步骤:

  1. 安装依赖
cmd 复制代码
npm install jszip-utils docxtemplater file-saver pizzip --save
npm install angular-expressions --save
npm install docxtemplater-image-module-free --save

docxtemplater:这个插件可以通过预先写好的word,excel等文件模板生成对应带数据的文件。

file-saver:适合在客户端生成文件的工具,它提供的接口saveAs(blob, "1.docx")将会使用到,方便我们保存文件。

pizzip:这个插件用来创建,读取或编辑.zip的文件,同步的(还有一个插件是jszip,异步的)。

jszip-utils:与jszip/pizzip一起使用,jszip-utils 提供一个getBinaryContent(path, data)接口,path即是文件的路径,支持AJAX get请求,data为读取的文件内容。

docxtemplater-image-module-free:需要导出图片的话需要这个插件

docxtemplater:不支持jszip,会有报错,因此要使用PizZip

  1. 编辑Word模板

    例如:
  • 后台出参:
js 复制代码
{
   tableData:[{name:'张三',age:18},{name:'李四',age:19}],
   school:'实验中学',
   logo:'图片的base64格式'
}
  • 模板内容:
  1. 保存模板

  2. 代码实现

  • ①生成Word文档的触发按钮
html 复制代码
<bt-button type="secondary" @click="handleGetCheckReport">生成报告</bt-button>
  • ②定义公共生成文件方法(固定内容,可直接复制),路径:src/utils/doc.js
js 复制代码
/**
 * 导出word文档(带图片)
 */
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'
// import * as ImageModule from 'docxtemplater-image-module-free'

import * as expressions from 'angular-expressions'
/**
 * 将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 exportWord = (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()
    }

    import('docxtemplater-image-module-free')
        .then(({ default: ImageModule }) => {
            // 使用 ImageModule
            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 [80, 40]
                    } else {
                        return [400, 500]
                    }
                }

                // 创建一个JSZip实例,内容为模板的内容
                const zip = new PizZip(content)
                // 创建并加载 Docxtemplater 实例对象
                // 设置模板变量的值
                let doc = new Docxtemplater()
                doc.attachModule(new ImageModule(opts))
                doc.loadZip(zip)
                // doc.setOptions({ parser: angularParser })
                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',
                })
                // 将目标文件对象保存为目标类型的文件,并命名
                saveAs(out, fileName)
            })
        })
        .catch(error => {
            console.error('Cannot load the module', error)
        })
}

/**
 * 将图片的url路径转为base64路径
 * 可以用await等待Promise的异步返回
 * @param {Object} imgUrl 图片路径
 */
export function getBase64Sync(imgUrl) {
    return new Promise(function (resolve, reject) {
        console.log(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)
            //返回
            console.log(dataurl)
            resolve(dataurl)
        }
    })
}
  • ③调用接口获取模板内容数据,生成word文件
js 复制代码
import { createFile } from '@/src/utils/doc.js'//引用公共方法(即步骤②中的文件)

// 生成检测记录
const handleGetCheckRecord = () => {
    // 1. 调用接口返回数据
    let query = {
        recordId: 'b97d3f05d0d9d4d3607956057a999d8c',
    }
    const loadingInstance = ElLoading.service({
        target: '.rankingListBtTable',
        text: '',
    })
    QmsNdeOtherRecordMtApi.checkDetails(query)
        .then(res => {
            let resData = res.data.ndeMtDataDto
            nextTick(() => {
                // 2. 整理后端返回数据,用于生成word文件(后台返回数据格式如下)
                let data = {
                    //常规文本数据
                    title: '检测规程/版本',
                    // 附件图片文字数据
                    imgList: [
                        {
                            remark: '这是一条备注信息111',
                            img: 'http://gateway-develop.ever-ghsb-develop.svc.cluster.local:8888/system22/app/fileObject/preview?id=4d86dd9a6f84d984b78775a0de891efb',
                        },
                        {
                            remark: '这是一条备注信息2222',
                            img: 'https://images.pexels.com/photos/3291250/pexels-photo-3291250.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load',
                        },
                    ],
                    // 表格数据
                    tableData: [
                        { index: '1', materialNo: '检测部位编号1', texture: '' },
                        { index: '2', materialNo: '检测部位编号2', texture: '' },
                    ],
                }
                // 3. 图片转base64
                exportWordFile('./docxs/VTRecords.docx', data, '检查记录.docx')
            })
        })
        .catch(err => {
            E_Msg.warn(err.msg || err)
        })
        .finally(() => {
            loadingInstance.close()
        })
}

// 多个图片遍历转base64
const exportWordFile = async (path, datas, fileName) => {
    //多个图片遍历转base64
    for (let i in datas.imgList) {
        datas.imgList[i].fileUrl = await getBase64Sync(datas.imgList[i].fileUrl)
    }
    let imgSize = {
        //控制导出的word图片大小
        imgurl: [400, 500],
    }
    nextTick(() => {
        // 4. 生成文档
        exportWord(path, datas, fileName, imgSize)
    })
}

注:
1. 后台数据格式说明

2. 多个图片显示的情况

3. 勾选框的实现
传入的变量category1 为 true 时,才会渲染打√的效果。此时要传入另一个变量category_1 ,值为 category1 取反。

相关推荐
GISer_Jing7 分钟前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪1 小时前
CSS复习
前端·css
咖啡の猫3 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲5 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5816 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路6 小时前
GeoTools 读取影像元数据
前端
ssshooter7 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry7 小时前
Jetpack Compose 中的状态
前端
dae bal8 小时前
关于RSA和AES加密
前端·vue.js
柳杉8 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化