损注 发表于 2025-5-31 23:37:38

《vue实现动态生成并导出world功能》

1. 安装需要的插件并引入
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'
import { Buffer } from 'buffer'2. 工具utils中新建 exportFile.js 文件,用于将图片转换为base64格式
// getBase64Sync
export function getBase64Sync(imgUrl) {
return new Promise((resolve, reject) => {
    const image = new Image();
    image.crossOrigin = 'anonymous';
    image.onload = () => {
      const canvas = document.createElement('canvas');
      // 固定画布尺寸为90x90
      canvas.width = 90;
      canvas.height = 90;
      const ctx = canvas.getContext('2d');
      // 计算缩放比例后居中绘制
      const scale = Math.min(90 / image.width, 90 / image.height);
      const scaledWidth = image.width * scale;
      const scaledHeight = image.height * scale;
      const dx = (90 - scaledWidth) / 2;
      const dy = (90 - scaledHeight) / 2;
      ctx.drawImage(image, dx, dy, scaledWidth, scaledHeight);

      let ext = imgUrl.split('.').pop().toLowerCase();
      if (ext === 'jpg') ext = 'jpeg';
      const mime = `image/${ext}`;
      try {
      const dataurl = canvas.toDataURL(mime, 0.8);
      resolve(dataurl);
      } catch (e) {
      reject(e);
      }
    };
    image.onerror = () => reject(new Error('图片加载失败'));
    image.src = imgUrl + (imgUrl.includes('?') ? '&' : '?') + 't=' + Date.now();
});
}

// base64DataURLToArrayBuffer
export function base64DataURLToArrayBuffer(dataURL) {
const base64 = dataURL.split(',');
const binaryString = window.atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
    bytes = binaryString.charCodeAt(i);
}
return bytes.buffer;
}3.引入 exportFile中的函数
import { getBase64Sync,base64DataURLToArrayBuffer } from '@/utils/exportFile.js'4.设置.docx模板文件,位置放在public文件下
数组使用{#data}{/data}包裹起来
展示的字段使用{item}
图片赋值用到{%img}
模板例如:

 
5.导出按钮具体功能实现
/** 方案1,不考虑图片直接导出 world */
function submitForm() {
// const htmlContent = document.getElementById('htmlcontent');
// const blob = new Blob(, { type: 'application/msword' });
// const url = window.URL.createObjectURL(blob);
// const a = document.createElement('a');
// a.href = url;
// a.download = '包装码.docx';
// a.click();
// window.URL.revokeObjectURL(url);
}/** 方案2,考虑图片,导出world */
async function exportWord() {
try {
    const content = await new Promise((resolve, reject) => {<br>      //world模板'/boxCode.docx'
      JSZipUtils.getBinaryContent('/boxCode.docx?random=' + Math.random(), (error, data) => {
      error ? reject(error) : resolve(data);
      });
    });

    const zip = new PizZip(content);
   
    let doc = new Docxtemplater();
   
    // 注册图片模块
    const imageOptions = {
      getImage: (tag) => {
      const base64Data = tag.split(',');
      return Buffer.from(base64Data, 'base64');
      },
      // 设置Word中图片显示尺寸为90x90
      getSize: () =>
    };
    doc.attachModule(new ImageModule(imageOptions));

    doc.loadZip(zip);

    const processedData = await Promise.all(
      currentData.value.map(async (item) => {
      const base64 = await getBase64Sync(item.boxCodeUrl);
      const matches = base64.match(/^data:image\/(\w+);base64,/);
      const validBase64 = matches ? base64 : `data:image/png;base64,${base64}`; //图片转码
      return {
          itemCode:item.itemCode?item.itemCode:'',
          batchNumber:item.batchNumber?item.batchNumber:'',
          itemName:item.itemName?item.itemName:'',
          itemModel:item.itemModel?item.itemModel:'',
          itemNumber:item.itemNumber?item.itemNumber:'',
          projectName:item.projectName?item.projectName:'',
          specification:item.specification?item.specification:'',
          vendorName:item.vendorName?item.vendorName:'',
          boxCodeUrl: validBase64 || ''
      };
      })
    )

    doc.setData({ currentData: processedData });//数据赋值
    try {
      doc.render();
    } catch (error) {
      console.error('模板渲染错误:', error.properties?.errors);
      throw error;
    }
   
    const out = doc.getZip().generate({
      type: 'blob',
      mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    });
    saveAs(out, currentItemName.value + '包装码.docx');
} catch (error) {
    console.error('导出失败:', error);
    throw error;
}finally{
   visible.value = false
}
}6.导出的world页面中若需要分页,在需要分页的光标位置设置分页符
 注:因为我用到了标签打印,生成的world打印时纵向横向样式展示没办法解决,所以将生成的world通过接口转为pdf输出进行打印// 生成Word Blob 发送到后端转换服务    const selectedFile = doc.getZip().generate({       type: 'blob',      mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'    });    const formData = new FormData();    formData.append('file', selectedFile);    fetch(VITE_APP_BASE_API + VITE_AGENT_API+`/md/purchaseorder/convertWordToPDF`, {        method: 'POST',        headers: {            'Authorization': `Bearer ${getToken()}`        },        body: formData    })    .then(response => {        if (!response.ok) {            throw new Error(`服务器返回错误: ${response.status}`);        }        return response.blob();    })    .then(blob => {      const url = window.URL.createObjectURL(blob);      const link = document.createElement('a');      link.href = url;      link.setAttribute('download', `${currentItemName.value}` + '包装码.pdf' );      document.body.appendChild(link);      link.click();      document.body.removeChild(link);    })    .catch(error => {      console.error('导出失败:', error);      loadingSubmit.value = false      throw error;    }); 
  

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 《vue实现动态生成并导出world功能》