前端实现导出word文档docx格式

发布时间 2023-06-05 17:37:35作者: 天宁哦

说明

前端实现导出word文档,我们需要用到docxtemplater这个库
使用的是vue2.6和vue-cli5
还需要准备一个word模板,更多模板变量请去docxtemplater官网获取

准备

word模板
image

安装需要用到的库

// 安装 docxtemplater
npm install docxtemplater pizzip  --save

// 安装 jszip-utils
npm install jszip-utils --save

// 安装 FileSaver
npm install file-saver --save

// 安装 angular-expressions
npm install angular-expressions --save

// 图片模块,没有图片需求可以不装
npm install docxtemplater-image-module-free

代码

<template>
    <div>
        <button @click="generateDocx">Generate Docx</button>
    </div>
</template>
<script>
// 如果有图片渲染的需求,要调用getBase64Sync 方法
import { exportWord, getBase64Sync } from './index.js'
export default {
    data() {
        return {
            content: {
                title: "fdf辅导费",
                name:'小新',
                age:5,
                imageOpts: "./logo.png",
                custName:'custName',
                phoneNumber:'458888883',
                projectRequirement:'projectRequirement',
                remark:'备注哦哦哦哦',
                checkReason:'审核checkReason',
                company:'abc公司',
                table:[
                    {id:1,name:'空调',number:15,salePrice:50,saleTotal:30,remark:'肯德基肯德基',src:require('../../assets/list/进行中.png')},
                    {id:2,name:'冰箱',number:15,salePrice:50,saleTotal:30,remark:'经济金融',src:'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'},
                    {id:3,name:'电风扇',number:15,salePrice:50,saleTotal:30,remark:'会更好',src:'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'},
                    {id:4,name:'彩电',number:15,salePrice:50,saleTotal:30,remark:'韩国进口',src:'./logo.png'},
                ]
            }
        };
    },
    methods: {
        async generateDocx() {
	//把图片转成base64
            this.content.imageOpts = await getBase64Sync(this.content.imageOpts)
            for (let i = 0; i < this.content.table.length; i++) {
                this.content.table[i].src = await getBase64Sync(this.content.table[i].src);
            }
            console.log(this.content);
	//这里的模版放到pubilc目录下
            exportWord('./test.docx', this.content, '测试.docx',{src:[50,50]})
        }
    }
};
</script>

调用的index.js文件

import PizZip from 'pizzip'
import docxtemplater from 'docxtemplater'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'
// import ImageModule from 'docxtemplater-image-module-free';
// import expressions from 'angular-expressions';

/**
 * 把连接或者本地图片转成base64
 * @param {string} imgUrl 
 * @returns 
 */
export function getBase64Sync(imgUrl) {
    return new Promise(function (resolve, reject) {
        // 一定要设置为let,不然图片不显示
        let image = new Image();
        // 解决跨域问题
        image.crossOrigin = "anonymous";
        //图片地址
        image.src = imgUrl;
        // 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);
            //返回
            resolve(dataurl);
        };
    });
}
/**
 * 将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;
}

/**
 * 导出word,支持图片
 * @param {Object} tempDocxPath 模板文件路径
 * @param {Object} wordData 导出数据
 * @param {Object} fileName 导出文件名
 * @param {Object} imgSize 自定义图片尺寸,{src:[50,50]}
 */
export const exportWord = (tempDocxPath, wordData, fileName, imgSize) => {
    // 这里要引入处理图片的插件
    var ImageModule = require('docxtemplater-image-module-free');

    const expressions = require("angular-expressions");

    // 读取并获得模板文件的二进制内容
    JSZipUtils.getBinaryContent(tempDocxPath, function (error, content) {
        // console.log('tempDocxPath', tempDocxPath);
        if (error) {
            throw error;
        }

        expressions.filters.size = function (input, width, height) {
            return {
                data: input,
                size: [width, height],
            };
        };

        // function angularParser (tag) {
        //   const expr = expressions.compile(tag.replace(/’/g, "'"));
        //   return {
        //     get (scope) {
        //       return expr(scope);
        //     },
        //   };
        // }

        // 图片处理
        let opts = {}

        opts = {
            // 图像是否居中
            centered: false
        };

        opts.getImage = (chartId) => {
            // console.log(chartId);//base64数据
            // 将base64的数据转为ArrayBuffer
            return base64DataURLToArrayBuffer(chartId);
        }

        opts.getSize = function (img, tagValue, tagName) {
            // console.log(img);//ArrayBuffer数据
            // console.log(tagValue);//base64数据
            // console.log(tagName);//wordData对象的图像属性名
            // 自定义指定图像大小
            if (imgSize.hasOwnProperty(tagName)) {
                return imgSize[tagName];
            } else {
                return [150, 150];
            }
        }

        // 创建一个PizZip实例,内容为模板的内容
        let zip = new PizZip(content);
        // 创建并加载docxtemplater实例对象
        let doc = new docxtemplater();
        doc.attachModule(new ImageModule(opts));
        doc.loadZip(zip);

        doc.setData(wordData);

        try {
            // 用模板变量的值替换所有模板变量
            doc.render()
            
        } catch (error) {
    // 抛出异常
    let e = {
        message: error.message,
        name: error.name,
        stack: error.stack,
        properties: error.properties
    };
    console.log(JSON.stringify({
        error: e
    }));
    throw error;
}

// 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
let out = doc.getZip().generate({
    type: "blob",
    mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
// 将目标文件对象保存为目标类型的文件,并命名
saveAs(out, fileName);
    });
}