前端生成指定模板的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 取反。

相关推荐
Alan_Wdd20 分钟前
解决谷歌人机验证 (reCAPTCHA) 无法加载问题
前端·chrome·浏览器·插件·人机验证·recaptcha
ndjnddjxn21 分钟前
比赛的复现(2024isctf)
java·linux·前端
gqkmiss1 小时前
Chrome 132 版本开发者工具(DevTools)更新内容
前端·chrome·chrome devtools
喵个咪1 小时前
无头内容管理系统 Headless CMS
前端·后端·cms
网络点点滴1 小时前
第一个AJAX调用XMLHttpRequest
前端·javascript·ajax
恋猫de小郭1 小时前
Android 16 Baklava 来了,来看看开发者预览版给我们带来了什么
android·前端·flutter
匹马夕阳1 小时前
前端自动化部署之ssh2和ssh2-sftp-client
前端·javascript·自动化
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_084】第 14 章:CSS 蒙版、形状与剪切概述 + 14.1:CSS 滤镜
前端·css·css3·html5·滤镜·css滤镜·滤镜特效
落日弥漫的橘_2 小时前
vue项目 中 asstes文件夹 与 static文件夹 的联系与区别
前端·javascript·vue.js·前端框架
我码玄黄2 小时前
在Cesium中加载OD线
前端·javascript·cesium