Skip to content

前端导出word(docx格式)

安装依赖

bash
-- 安装 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 docxtemplater-image-module-free --save
-- 安装 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 docxtemplater-image-module-free --save

先定义一个word处理方法

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'
/**
 * 
 * @param {*object} data 模板变量字段数据
 * @param {*string} name 生成word文件名字
 * @param {*string} template 加载的word模板文件
 */
//图片解析配置项
const imageOptions = {
  getImage: (tagValue, tagName)=> {
    const base64Value = base64Parser(tagValue)
    return base64Value
  },
  getSize: (img, tagValue, tagName) => {
    const maxWidth = 200
    const maxHeight = 200
    return new Promise((resolve, reject) => {
      const image = new Image()
      image.src = tagValue
      image.onload = () => {
        const sizeObj = {
          width: image.width,
          height: image.height
        }
        const widthRatio = sizeObj.width / maxWidth;
        const heightRatio = sizeObj.height / maxHeight;
        if (widthRatio < 1 && heightRatio < 1) {
          resolve([sizeObj.width, sizeObj.height])
        }
        let finalWidth, finalHeight;
        if (widthRatio > heightRatio) {
          finalWidth = maxWidth;
          finalHeight = sizeObj.height / widthRatio;
        } else {
          finalHeight = maxHeight;
          finalWidth = sizeObj.width / heightRatio;
        }
        resolve([Math.round(finalWidth), Math.round(finalHeight)])
      }
      image.onerror = (error) => {
        console.log(error)
        reject(error)
      }  
    })
    // return [maxWidth,maxHeight]
  },
  getProps: (img,tagValue,tagName) => {
    return {
      align:'center'
    }
  }
}
//base64解析为二进制
const base64Parser = (dataURL) => {
  const base64Regex =/^data:image\/(png|jpg|svg|svg\+xml);base64,/
  if (typeof dataURL !== "string" ||!base64Regex.test(dataURL)) return false
  const stringBase64 = dataURL.replace(base64Regex, "")
  const binaryString = window.atob(stringBase64)
  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
}
//读取模板
export const creatWord = (data,name,template) => {
  JSZipUtils.getBinaryContent(template,async(error,content)=>{
    if(error){
      throw error
    }
    const zip = new PizZip(content)
    const doc = new Docxtemplater(zip, {
      modules:[new ImageModule(imageOptions)]
    })
    try {
      //异步获取文件
      await doc.renderAsync(data)
      const outFile = doc.getZip().generate({
        type:'blob',
        mimeType:'application/vnd.openxmlformats-officedocment.wordprocessingml.document'
      })    
      saveAs(outFile,name)
    } catch (error) {
      const e = {
        message:error.message,
        name:error.name,
        stack:error.stack,
        properties:error.properties
      }
      console.log(e);
      throw error
    }
  })
}
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'
/**
 * 
 * @param {*object} data 模板变量字段数据
 * @param {*string} name 生成word文件名字
 * @param {*string} template 加载的word模板文件
 */
//图片解析配置项
const imageOptions = {
  getImage: (tagValue, tagName)=> {
    const base64Value = base64Parser(tagValue)
    return base64Value
  },
  getSize: (img, tagValue, tagName) => {
    const maxWidth = 200
    const maxHeight = 200
    return new Promise((resolve, reject) => {
      const image = new Image()
      image.src = tagValue
      image.onload = () => {
        const sizeObj = {
          width: image.width,
          height: image.height
        }
        const widthRatio = sizeObj.width / maxWidth;
        const heightRatio = sizeObj.height / maxHeight;
        if (widthRatio < 1 && heightRatio < 1) {
          resolve([sizeObj.width, sizeObj.height])
        }
        let finalWidth, finalHeight;
        if (widthRatio > heightRatio) {
          finalWidth = maxWidth;
          finalHeight = sizeObj.height / widthRatio;
        } else {
          finalHeight = maxHeight;
          finalWidth = sizeObj.width / heightRatio;
        }
        resolve([Math.round(finalWidth), Math.round(finalHeight)])
      }
      image.onerror = (error) => {
        console.log(error)
        reject(error)
      }  
    })
    // return [maxWidth,maxHeight]
  },
  getProps: (img,tagValue,tagName) => {
    return {
      align:'center'
    }
  }
}
//base64解析为二进制
const base64Parser = (dataURL) => {
  const base64Regex =/^data:image\/(png|jpg|svg|svg\+xml);base64,/
  if (typeof dataURL !== "string" ||!base64Regex.test(dataURL)) return false
  const stringBase64 = dataURL.replace(base64Regex, "")
  const binaryString = window.atob(stringBase64)
  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
}
//读取模板
export const creatWord = (data,name,template) => {
  JSZipUtils.getBinaryContent(template,async(error,content)=>{
    if(error){
      throw error
    }
    const zip = new PizZip(content)
    const doc = new Docxtemplater(zip, {
      modules:[new ImageModule(imageOptions)]
    })
    try {
      //异步获取文件
      await doc.renderAsync(data)
      const outFile = doc.getZip().generate({
        type:'blob',
        mimeType:'application/vnd.openxmlformats-officedocment.wordprocessingml.document'
      })    
      saveAs(outFile,name)
    } catch (error) {
      const e = {
        message:error.message,
        name:error.name,
        stack:error.stack,
        properties:error.properties
      }
      console.log(e);
      throw error
    }
  })
}

在vue中使用

vue

<script>
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'

// 点击导出word
const exportWord = () => {
  // 读取并获得模板文件的二进制内容,模板路径读取public或者static目录下
  JSZipUtils.getBinaryContent('word模板路径.docx', function (error, content) {
    // model.docx是模板。我们在导出的时候,会根据此模板来导出对应的数据
    // 抛出异常
    if (error) {
      throw error
    }
    // 创建一个PizZip实例,内容为模板的内容
    const zip = new PizZip(content)
    // 创建并加载docxtemplater实例对象
    const doc = new Docxtemplater()
    doc.loadZip(zip)
    // 设置模板变量的值
    doc.setData({
      table: this.videoParam.data
    })

    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, '输出文件名字.docx')
  })
}
</script>

<script>
import Docxtemplater from 'docxtemplater'
import PizZip from 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs } from 'file-saver'

// 点击导出word
const exportWord = () => {
  // 读取并获得模板文件的二进制内容,模板路径读取public或者static目录下
  JSZipUtils.getBinaryContent('word模板路径.docx', function (error, content) {
    // model.docx是模板。我们在导出的时候,会根据此模板来导出对应的数据
    // 抛出异常
    if (error) {
      throw error
    }
    // 创建一个PizZip实例,内容为模板的内容
    const zip = new PizZip(content)
    // 创建并加载docxtemplater实例对象
    const doc = new Docxtemplater()
    doc.loadZip(zip)
    // 设置模板变量的值
    doc.setData({
      table: this.videoParam.data
    })

    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, '输出文件名字.docx')
  })
}
</script>

也可使用docx-templates

docx-templates既可以用在browers也可以用在nodejs中
参考博客 https://juejin.cn/post/7170695319004315679

前端预览docx

vue2/vue3框架预览

安装依赖

bash
#docx文档预览组件
npm install @vue-office/docx vue-demi

#excel文档预览组件
npm install @vue-office/excel vue-demi

#pdf文档预览组件
npm install @vue-office/pdf vue-demi
#docx文档预览组件
npm install @vue-office/docx vue-demi

#excel文档预览组件
npm install @vue-office/excel vue-demi

#pdf文档预览组件
npm install @vue-office/pdf vue-demi

如果是vue2.6版本或以下还需要额外安装 @vue/composition-api

bash
npm install @vue/composition-api
npm install @vue/composition-api

使用

vue
<template>
    <div id="docx-demo">
        <el-upload
            :limit="1"
            :file-list="fileList"
            accept=".docx"
            :beforeUpload="beforeUpload"
            action=""
        >
            <el-button size="small" type="warning">点击上传</el-button>
        </el-upload>
        <vue-office-docx :src="src"/>
    </div>
</template>

<script>
import VueOfficeDocx from '@vue-office/docx'
import '@vue-office/docx/lib/index.css'

export default {
    components: {
        VueOfficeDocx
    },
    data() {
        return {
            src: '',
            fileList: []
        }
    },
    methods: {
        //在beforeUpload中读取文件内容
        beforeUpload(file) {
            let reader = new FileReader();
            reader.readAsArrayBuffer(file);
            reader.onload = (loadEvent) => {
                let arrayBuffer = loadEvent.target.result;
                this.src = arrayBuffer
            };
            return false
        }
    }
}
</script>
<template>
    <div id="docx-demo">
        <el-upload
            :limit="1"
            :file-list="fileList"
            accept=".docx"
            :beforeUpload="beforeUpload"
            action=""
        >
            <el-button size="small" type="warning">点击上传</el-button>
        </el-upload>
        <vue-office-docx :src="src"/>
    </div>
</template>

<script>
import VueOfficeDocx from '@vue-office/docx'
import '@vue-office/docx/lib/index.css'

export default {
    components: {
        VueOfficeDocx
    },
    data() {
        return {
            src: '',
            fileList: []
        }
    },
    methods: {
        //在beforeUpload中读取文件内容
        beforeUpload(file) {
            let reader = new FileReader();
            reader.readAsArrayBuffer(file);
            reader.onload = (loadEvent) => {
                let arrayBuffer = loadEvent.target.result;
                this.src = arrayBuffer
            };
            return false
        }
    }
}
</script>

非Vue框架文件预览

使用docx-preview

安装依赖

bash
npm i docx-preview
npm i docx-preview

使用

html
<script src="https://unpkg.com/promise-polyfill/dist/polyfill.min.js"></script>
<!--lib uses jszip-->
<script src="https://unpkg.com/jszip/dist/jszip.min.js"></script>
<script src="docx-preview.min.js"></script>
<script>
    var docData = <document Blob>;

    docx.renderAsync(docData, document.getElementById("container"))
        .then(x => console.log("docx: finished"));
</script>
<body>
    ...
    <div id="container"></div>
    ...
</body>
<script src="https://unpkg.com/promise-polyfill/dist/polyfill.min.js"></script>
<!--lib uses jszip-->
<script src="https://unpkg.com/jszip/dist/jszip.min.js"></script>
<script src="docx-preview.min.js"></script>
<script>
    var docData = <document Blob>;

    docx.renderAsync(docData, document.getElementById("container"))
        .then(x => console.log("docx: finished"));
</script>
<body>
    ...
    <div id="container"></div>
    ...
</body>

使用@js-preview/docx

安装依赖

bash
npm i @js-preview/docx
npm i @js-preview/docx

使用

ts
import jsPreviewDocx from "@js-preview/docx";
import '@js-preview/docx/lib/index.css'

//初始化时指明要挂载的父元素Dom节点
const myDocxPreviewer = jsPreviewDocx.init(document.getElementById('docx'));

//传递要预览的文件地址即可
myDocxPreviewer.preview('https://501351981.github.io/vue-office/examples/dist/static/test-files/test.docx').then(res=>{
    console.log('预览完成');
}).catch(e=>{
    console.log('预览失败', e);
})
import jsPreviewDocx from "@js-preview/docx";
import '@js-preview/docx/lib/index.css'

//初始化时指明要挂载的父元素Dom节点
const myDocxPreviewer = jsPreviewDocx.init(document.getElementById('docx'));

//传递要预览的文件地址即可
myDocxPreviewer.preview('https://501351981.github.io/vue-office/examples/dist/static/test-files/test.docx').then(res=>{
    console.log('预览完成');
}).catch(e=>{
    console.log('预览失败', e);
})

参考文档地址
https://501351981.github.io/vue-office/examples/docs/guide/js-preview.html
word预览演示demo
https://501351981.github.io/vue-office/examples/dist/#/docx

努力成为全干型人才