vue +echarts+ docxtemplater导出word和多张图表图片

vue +echarts+ docxtemplater导出word和多张图表图片

前期准备

1、按照依赖的插件docxtemplater、pizzip、jszip-utils、jszip、FileSaver、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、代码-父组件
我是把table通过插槽的方式注入到子组件的

<template>
  <div class="box">
<word
      exportFileName="测试666"
      templateFileName="templateFile"
      :wordData="wordData"
      :chartOptions="chartOptions"
    >
      <template v-slot:table>
        <table cellspacing="0">
          <caption>
            标题
          </caption>
          <tr>
            <td class="key-name">标题1</td>
            <td colspan="3">{{ wordData.a }}</td>
          </tr>
          <tr>
            <td class="key-name">标题2</td>
            <td colspan="3">{{ wordData.b }}</td>
          </tr>
          <tr>
            <td class="key-name">标题3</td>
            <td colspan="3">{{ wordData.c }}</td>
          </tr>
          <tr>
            <td class="key-name">标题4</td>
            <td>{{ wordData.d }}</td>
            <td class="key-name">标题6</td>
            <td>{{ wordData.f }}</td>
          </tr>
          <tr>
            <td class="key-name">标题5</td>
            <td>{{ wordData.e }}</td>
            <td class="key-name">标题7</td>
            <td>{{ wordData.g }}</td>
          </tr>
          <tr>
            <td colspan="4" class="key-name options">审批意见</td>
          </tr>
          <tr>
            <td colspan="4" class="options">
              {{ wordData.h }}
            </td>
          </tr>
          <tr v-for="(item, index) in wordData.list" :key="index">
            <td class="key-name">姓名</td>
            <td>{{ item.name }}</td>
            <td class="key-name">电话</td>
            <td>{{ item.phone }}</td>
          </tr>
        </table>
      </template>
    </word>
</div>
</template>
<script>
// 子组件
import word from "@/components/exportWord.vue";
export default {
  components: { word },
  data() {
    return {
      // 导出word
      wordData: {
        a: "这是标题1内容",
        b: "这是标题2内容",
        c: "这是标题3内容",
        d: "这是标题4内容",
        e: "这是标题5内容",
        f: "这是标题6内容",
        g: "这是标题7内容",
        h: "同意",
        list: [
          { name: "王五", phone: "18888888888" },
          { name: "王五", phone: "18888888888" },
          { name: "王五", phone: "18888888888" },
          { name: "王五", phone: "18888888888" },
        ],
      },
      // 图表
      chartOptions: [
      // echarts图表的option
      // 第一个图表
        {
          title: {
            text: "某站点用户访问来源",
            subtext: "纯属虚构",
            left: "center",
          },
          tooltip: {
            trigger: "item",
          },
          legend: {
            orient: "vertical",
            left: "left",
          },
          series: [
            {
              name: "访问来源",
              type: "pie",
              radius: "50%",
              data: [
                { value: 1048, name: "搜索引擎" },
                { value: 735, name: "直接访问" },
                { value: 580, name: "邮件营销" },
                { value: 484, name: "联盟广告" },
                { value: 300, name: "视频广告" },
              ],
              emphasis: {
                itemStyle: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: "rgba(0, 0, 0, 0.5)",
                },
              },
            },
          ],
        },
        // 第二个图表
        {
          title: {
            text: "未来一周气温变化",
            subtext: "纯属虚构",
          },
          tooltip: {
            trigger: "axis",
          },
          legend: {
            data: ["最高气温", "最低气温"],
          },
          xAxis: {
            type: "category",
            boundaryGap: false,
            data: ["周一", "周二", "周三", "周四", "周五", "周六", "周日"],
          },
          yAxis: {
            type: "value",
            axisLabel: {
              formatter: "{value} °C",
            },
          },
          series: [
            {
              name: "最高气温",
              type: "line",
              data: [10, 11, 13, 11, 12, 12, 9],
              markPoint: {
                data: [
                  { type: "max", name: "最大值" },
                  { type: "min", name: "最小值" },
                ],
              },
              markLine: {
                data: [{ type: "average", name: "平均值" }],
              },
            },
            {
              name: "最低气温",
              type: "line",
              data: [1, -2, 2, 5, 3, 2, 0],
              markPoint: {
                data: [{ name: "周最低", value: -2, xAxis: 1, yAxis: -1.5 }],
              },
              markLine: {
                data: [
                  { type: "average", name: "平均值" },
                  [
                    {
                      symbol: "none",
                      x: "90%",
                      yAxis: "max",
                    },
                    {
                      symbol: "circle",
                      label: {
                        position: "start",
                        formatter: "最大值",
                      },
                      type: "max",
                      name: "最高点",
                    },
                  ],
                ],
              },
            },
          ],
        },
      ],
    };
  },
  };
</script>

4、代码-子组件

<template>
  <!-- 导出word模版 -->
  <div class="approvalNo-or-opinionNo-list">
    <el-button type="primary" size="small" @click="exportWord"
      >点击下载</el-button
    >
    <div id="pdfDom">
      <slot name="table"></slot>
    </div>
    <div
      style="width: 600px; height: 300px; margin: 0 auto"
      v-for="(item, index) in chartOptions"
      :key="index"
    >
      <Echart :options="item" ref="chart"></Echart>
    </div>
  </div>
</template>

<script>
import docxtemplater from "docxtemplater";
import JSZipUtils from "jszip-utils";
import { saveAs } from "file-saver";
import JSZip from "jszip";
import PizZip from "pizzip";
import Echart from "@/components/Echarts.vue";
export default {
  components: { Echart },
  data() {
    return {};
  },
  props: {
    // 文档数据
    wordData: {
      type: Object,
      default: {},
    },
    // 导出文件名字
    exportFileName: {
      type: String,
      default: "word",
    },
    // 模版文件名字(文件需放在public目录下,文件格式为docx)
    templateFileName: {
      type: String,
      default: "templateFile",
    },
    // 图表option 数组,可多个图表
    chartOptions: {
      type: Array,
      default: [],
    },
  },
  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() {
      const ImageModule = require("docxtemplater-image-module-free");
      const that = this;
      // 存放echarts 图表 base64图片数组
      const str = [];
      // 读取并获得模板文件的二进制内容
      JSZipUtils.getBinaryContent(
        `${that.templateFileName}.docx`,
        (error, content) => {
          if (error) {
            // 抛出异常
            throw error;
          }
          const zip = new PizZip(content); // 创建一个JSZip实例,内容为模板的内容
          const doc = new docxtemplater();
          doc.loadZip(zip); // 创建并加载docxtemplater实例对象
          //   如果有echarts图表
          if (that.chartOptions.length > 0) {
            for (const iterator of that.$refs["chart"]) {
            // 这个getChartImg方法是我的echart组件中获取echarts图表图片的base64的dataurl
              iterator.getChartImg((chartImg) => {
                str.push({ image: chartImg });
              });
            }
            // 图片处理
            const opts = {};
            opts.centered = true; // 图片居中,在word模板中定义方式为{%%image}
            opts.fileType = "docx";
            opts.getImage = (chartId) => {
              return that.base64DataURLToArrayBuffer(chartId);
            };
            opts.getSize = () => {
              return [600, 300];
            };
            const imageModule = new ImageModule(opts);
            doc.attachModule(imageModule);
          }
          doc.setData({ ...that.wordData, chartlist: str }); // 设置模板变量的值
          try {
            doc.render(); // 用模板变量的值替换所有模板变量
          } catch (error) {
            const e = {
              message: error.message,
              name: error.name,
              stack: error.stack,
              properties: error.properties,
            };
            console.log(JSON.stringify({ error: e }));
            throw error; // 抛出异常
          }
          // 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
          const out = doc.getZip().generate({
            type: "blob",
            mimeType:
              "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
          });
          // 将目标文件对象保存为目标类型的文件,并命名
          saveAs(out, `${that.exportFileName}.docx`);
        }
      );
    },
  },
};
</script>

<style lang="less">
.approvalNo-or-opinionNo-list {
  height: 350px;
  overflow-y: auto;
  padding-bottom: 20px;
  border-bottom: 1px solid #ccc;
  > #pdfDom {
    table {
      text-align: center;
      border-bottom: 1px solid #ccc;
      width: 93%;
      margin: 0 auto;
      font-family: "楷体", "楷体_GB2312";
      caption {
        font-size: 16px;
        text-align: center;
        line-height: 46px;
        color: #333;
        font-weight: bold;
      }
      td {
        width: 25%;
        height: 32px;
        color: #666;
        border-left: 1px solid #ccc;
        border-top: 1px solid #ccc;
        padding: 0 6px;
      }
      td:last-child {
        border-right: 1px solid #ccc;
      }
      .key-name {
        color: #333;
        font-weight: 600;
      }
      .options {
        padding: 10px;
        text-align: justify;
        text-indent: 2em;
      }
    }
  }
}
</style>

5、getChartImg方法

// 获取图表base64图
    getChartImg(callback: any) {
      const that = this as any;
      callback(that.myChart.getDataURL());
    },

5、word模版
在这里插入图片描述
6、页面
在这里插入图片描述

7、成功导出后的word
在这里插入图片描述

  • 10
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
要实现动态绘制图表,可以使用Vue.js做前端框架,使用Echarts.js做图表库。下面是一个简单的实现过程: 1. 在Vue.js中安装Echarts.js。可以通过npm或者cdn的方式来安装。 2. 在Vue.js组件中引入Echarts.js,并初始化一个echarts实例。 3. 在Vue.js的data中定义一个数组,用来存储图表的数据。在mounted生命周期钩子中,设置一个定时器,每隔一段时间就往数据数组中添加一些数据。 4. 在Vue.js的methods中定义一个函数,用来更新图表。在这个函数中,使用echarts实例的setOption方法来更新图表数据。 5. 在Vue.jstemplate中,添加一个div元素作为图表容器,并设置宽高。在mounted生命周期钩子中,使用echarts实例的init方法初始化图表,并在更新图表数据时调用更新函数。 下面是一个简单的示例代码: ``` <template> <div id="chart" style="width: 600px; height: 400px;"></div> </template> <script> import echarts from 'echarts' export default { data () { return { chartData: [] } }, mounted () { this.chart = echarts.init(document.getElementById('chart')) this.timer = setInterval(() => { this.addData() this.updateChart() }, 1000) }, methods: { addData () { this.chartData.push(Math.random() * 100) }, updateChart () { this.chart.setOption({ xAxis: { type: 'category', data: Array.from({ length: this.chartData.length }, (_, i) => i) }, yAxis: { type: 'value' }, series: [{ data: this.chartData, type: 'line' }] }) } }, beforeDestroy () { clearInterval(this.timer) this.chart.dispose() } } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值