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

相关推荐
树上有只程序猿10 分钟前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼1 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下1 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox1 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
inxunoffice1 小时前
批量将文本文件转换为 Word/PDF/Excel/图片等其它格式
pdf·word·excel
一张假钞1 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行1 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758101 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox
掘金一周1 小时前
金石焕新程 >> 瓜分万元现金大奖征文活动即将回归 | 掘金一周 4.3
前端·人工智能·后端
三翼鸟数字化技术团队1 小时前
Vue自定义指令最佳实践教程
前端·vue.js