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 插件预览的时候文件中表格样式被破坏的问题正在研究。

相关推荐
Irene19916 小时前
Vue:useSlots 和 useAttrs 深度解析
vue.js·useslots·useattrs
五颜六色的黑6 小时前
vue3+elementPlus实现循环列表内容超出时展开收起功能
前端·javascript·vue.js
JIngJaneIL7 小时前
基于java+ vue医院管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
计算机学姐7 小时前
基于SpringBoot的高校论坛系统【2026最新】
java·vue.js·spring boot·后端·spring·java-ee·tomcat
SiYuanFeng8 小时前
【Word 排版】文本框/文字行间距异常偏大的解决方案
word
JIngJaneIL8 小时前
基于java + vue校园跑腿便利平台系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
越努力越幸运5089 小时前
vue学习二:
javascript·vue.js·学习
码途进化论10 小时前
Vue3 防重复点击指令 - clickOnce
前端·javascript·vue.js
幽络源小助理11 小时前
SpringBoot+Vue攀枝花水果在线销售系统源码 | Java项目免费下载 – 幽络源
java·vue.js·spring boot
雲墨款哥12 小时前
从一行好奇的代码说起:Vue怎么没有React的props.children
前端·vue.js·react.js