最近遇到一个将包含echarts图和Table的页面导出成word文档,研究了一下,决定使用docxtemplater 插件进行开发,整个思考的方向有以下几点:

  • 编写好word模板,具体的写法,可以参考https://docxtemplater.com/demo/#loop-table中demo,注意这个插件中很多的模板是要收费的(commercial),有一部分模板是免费的(带有free),简单的需求可以使用此插件。
  • 对于页面中的Table,只需要将要写入文档的数据整理成指定的样式,具体也可以参考https://docxtemplater.com/demo/#loop-table,注意word文档中所涵盖的字段名称要与传入的字段保持一致
  • 对于echarts图的写入,要借用开源的docxtemplater-image-module-free方法。首先,echarts图通过getDataURL()方法获取一个base64 的 URL,其次,利用docxtemplater-image-module-free官网中提供的base64DataURLToArrayBuffer()方法将base64的URL进行转换。

前期准备:

1、按照依赖的插件docxtemplaterjszip-utilsjszipFileSaver、docxtemplater-image-module-free

//-- 安装 docxtemplater
npm install docxtemplater pizzip  --save
//-- 安装 jszip-utils
npm install jszip-utils --save 
//-- 安装 jszip
npm install jszip --save
//-- 安装 FileSaver
npm install file-saver --save
//安装 docxtemplater-image-module-free
npm install --save docxtemplater-image-module-free 

2、引入依赖包

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

注,若引入docxtemplater-image-module-free 报错时,可尝试使用require方法引入。

let ImageModule = require('docxtemplater-image-module-free');

3、代码

// vue 页面
// echarts 图
<chart :options="option" :autoResize="true" ref="echart"></chart>
// table-iview
<Table ref='table' :columns="columns" :data="tableData"></Table>
// 导出按钮
<Button type="info" @click="exportWord">导出word文档</Button>

 

<script>
// 导入table、echarts 配置文件
import {echartConfig} from './config/echartConfig.js'
import {tableConfig} from './config/tableConfig.js'
import docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import {saveAs} from 'file-saver'
// import imageModule from 'open-docxtemplater-image-module'
export default {
    name:'exportPage',
    data(){
        return {
           echartData:{},
           tableData: []
        }
    },
    created(){
        this.echartData = {
            lenged:["小米","小张"],
            age:[18,22]
        }      
        this.tableData= [            
            {
               name:"小米",
               age: 18
            },
            {
               name:"小张",
               age:22
            }
        ];
    },
    computed:{
        option(){
            return echartConfig(this);
        },
        columns(){
            return tableConfig(this);
        }
    },

    methods:{
        // 导出echarts图片,格式转换
        base64DataURLToArrayBuffer(dataURL) {
            const base64Regex = /^data:image\/(png|jpg|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 = new Buffer(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;
        },
        // 导出文档
        exportWord(){
            var ImageModule = require('open-docxtemplater-image-module');
            // 点击导出word
            let _this = this;            
            // 读取并获得模板文件的二进制内容
                  JSZipUtils.getBinaryContent("../../../../static/docTemplate/exportTemplate.docx",function(error, content) {
                // exportTemplate.docx是模板。我们在导出的时候,会根据此模板来导出对应的数据
                // 抛出异常
                if (error) {
                    throw error;
                }
                // 图片处理
                let opts = {}
                opts.centered = true;  // 图片居中,在word模板中定义方式为{%%image}
                opts.fileType = "docx";
                opts.getImage = function(chartId){
                    return _this.base64DataURLToArrayBuffer(chartId);
                }
                opts.getSize = function(){
                    return [200,200]
                }
                let imageModule = new ImageModule(opts);
                // 创建一个PizZip实例,内容为模板的内容
                let zip = new PizZip(content);
                // 创建并加载docxtemplater实例对象
                let doc = new docxtemplater();
                doc.attachModule(imageModule);
                doc.loadZip(zip);   
                
                // 设置模板变量的值
                doc.setData({
                    table: _this.tableData,
                    image:_this.$refs.echart.getDataURL() // 获取echarts图片
                });
                
                try {
                    // 用模板变量的值替换所有模板变量
                    doc.render();
                    } catch (error) {
                    // 抛出异常
                    let e = {
                        message: error.message,
                        name: error.name,
                        stack: error.stack,
                        properties: error.properties
                    };
                    throw error;
                }                
                // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
                let out = doc.getZip().generate({
                    type: "blob",
                    mimeType:"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                });
                // 将目标文件对象保存为目标类型的文件,并命名
                saveAs(out, "exportDocx.docx");
            });
            }
        
    },
}
</script>

4、参考链接

https://www.npmjs.com/package/docxtemplater-image-module-free

https://docxtemplater.com/demo/#loop-table

https://www.echartsjs.com/zh/api.html#echartsInstance.getDataURL

https://www.jianshu.com/p/b3622d6f8d98

https://www.jianshu.com/p/0de31429b12a

https://docxtemplater.readthedocs.io/en/latest/api.html#methods

补充一个踩过的坑~

在定义数据是,尤其是table数据时,字符串一定要用双引号!!!不然导出的文件可能没有数据

Logo

前往低代码交流专区

更多推荐