Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

预览

必须是 .docx 文档,别的类型的文档请自行研究实现

  • 通过 vue-office/docx 插件预览 docx 文档
  • 通过 vue-office/excel 插件预览 excel 文档
  • 通过 vue-office/pdf 插件预览 pdf 文档

安装插件

js 复制代码
npm install @vue-office/docx vue-demi

示例代码

html 复制代码
<template>
  <VueOfficeDocx :src="docx" @rendered="rendered"></VueOfficeDocx>
</template>

<script setup lang="ts">
import VueOfficeDocx from "@vue-office/docx";
import "@vue-office/docx/lib/index.css";
import { ref } from "vue";
const docx = ref("../../public/01.docx");

const rendered = () => {
  console.log("加载完毕...");
};
</script>

<style lang="scss" scoped></style>

项目目录结构截图

实际效果截图

动态渲染 .docx 文档(带图片),预览、导出

安装插件

js 复制代码
npm install docxtemplater
npm install docxtemplater-image-module-free
npm install pizzip
npm install file-saver

docx 模板文件内容

  • vue3项目把该模板 docx 文件放 public 文件夹里面

完整代码

html 复制代码
<template>
  <div class="common-layout">
    <el-container style="height: 100vh">
      <el-aside
        style="
          width: 200px;
          background: rgb(216.8, 235.6, 255);
          padding: 80px 0 0 30px;
        "
      >
        <el-button type="danger" @click="wordWrite(content)">写入</el-button>
        <el-button type="danger" @click="wordExport(content)">导出</el-button>
        <div></div>
      </el-aside>
      <el-container>
        <el-header style="background: rgb(197.7, 225.9, 255)">
          <H2>
            Vue3 项目通过 docxtemplater 插件动态渲染 .docx
            文档(带图片)预览,并导出</H2
          >
        </el-header>
        <el-main>
          <template v-if="!isShow">
            <h3>准备写入 docx 文件内容</h3>
            <div>
              <p>姓名:{{ content.name }}</p>
              <p>年龄:{{ content.age }}</p>
              <p>性别:{{ content.gender }}</p>
              <p>学历:{{ content.education }}</p>
              <p>个人简介:{{ content.introduction }}</p>
              <p>
                头像:
                <img :src="content.avatar" alt="头像" />
              </p>
            </div>
          </template>
          <div class="m-4">
            <!-- docx 文件预览插件 -->
            <VueOfficeDocx :src="docx" v-if="isShow"></VueOfficeDocx>
          </div>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script setup lang="ts">
import { ref } from "vue";
import Docxtemplater from "docxtemplater";
import ImageModule from "docxtemplater-image-module-free";
import PizZip from "pizzip";
import PizZipUtils from "pizzip/utils";
import { saveAs } from "file-saver";
import VueOfficeDocx from "@vue-office/docx"; // 导入预览插件
import "@vue-office/docx/lib/index.css"; // 导入预览插件样式
const isShow = ref(false); // 预览组件状态

const docx = ref("../../public/03.docx"); // 导入 docx 模板文件
import img from "@/assets/01.jpg"; // 导入本地图片

// 准备写入内容
const content = ref({
  name: "张三",
  age: 25,
  gender: "男",
  education: "小学",
  introduction:
    "我是一个热爱学习的学生,喜欢阅读各种类型的书籍,同时也喜欢参加各种活动,比如参加比赛、组织活动等等。",
  avatar: img,
});

/**
 * 生成文档内容
 * @param docData 准备写入内容
 */
const GenerateADocument = (docData) => {
  return new Promise((resole, reject) => {
    const loadFile = function loadFile(url: any, callback: any) {
      PizZipUtils.getBinaryContent(url, callback);
    };

    loadFile(docx.value, function (error: any, content: any) {
      if (error) {
        reject(new Error("模板文件未找到"));
      }
      // 处理图片
      let opts: any = {};
      opts.centered = false;
      opts.fileType = "docx";
      opts.getImage = function (tagValue: any) {
        return new Promise(function (resolve, reject) {
          PizZipUtils.getBinaryContent(
            tagValue,
            function (error: any, content: any) {
              if (error) {
                return reject(error);
              }
              return resolve(content);
            }
          );
        });
      };
      opts.getSize = function () {
        //这里是生成的word文件里图片的宽和高
        // 可以根据自己的需要进行修改,获取图片实际的宽和高,然后返回
        return [255, 195];
      };
      let imageModule = new ImageModule(opts);
      var zip = new PizZip(content);
      let doc = new Docxtemplater()
        .loadZip(zip)
        .attachModule(imageModule)
        .compile();
      doc.resolveData(docData).then(function () {
        doc.render();
        let out = doc.getZip().generate({
          type: "blob",
          mimeType:
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        });

        if (out) {
          resole(out);
        } else {
          reject(new Error("文件生成失败"));
        }
      });
    });
  });
};

/**
 * 写入方法
 * @param docData 准备写入内容
 */
const wordWrite = (docData) => {
  GenerateADocument(docData)
    .then((res) => {
      docx.value = URL.createObjectURL(res);
      isShow.value = true;
    })
    .catch((err) => {
      console.log(err, "err");
    });
};

/**
 * 导出方法
 * @param docData 准备写入内容
 */
const wordExport = (docData) => {
  GenerateADocument(docData)
    .then((res) => {
      saveAs(res, "测试文件" + ".docx");
    })
    .catch((err) => {
      console.log(err, "err");
    });
};
</script>

vue-office 插件预览的时候文件中表格样式被破坏的问题正在研究。

相关推荐
鱼樱前端41 分钟前
今天介绍下最新更新的Vite7
前端·vue.js
炒毛豆3 小时前
vue3.4中的v-model的用法~
前端·vue.js
阳火锅3 小时前
都2025年了,来看看前端如何给刘亦菲加个水印吧!
前端·vue.js·面试
夕水4 小时前
ew-vue-component:Vue 3 动态组件渲染解决方案的使用介绍
前端·vue.js
我麻烦大了4 小时前
实现一个简单的Vue响应式
前端·vue.js
aklry5 小时前
uniapp三步完成一维码的生成
前端·vue.js
用户26124583401617 小时前
vue学习路线(11.watch对比computed)
前端·vue.js
阑梦清川7 小时前
Java后端项目前端基础Vue(二)
vue.js
雪碧聊技术8 小时前
深入解析Vue中v-model的双向绑定实现原理
前端·javascript·vue.js·v-model
百锦再9 小时前
重新学习Vue中的按键监听和鼠标监听
javascript·vue.js·vue·计算机外设·click·up·down