找回密码
 立即注册
首页 业界区 安全 《vue实现动态生成并导出world功能》

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

损注 7 天前
1. 安装需要的插件并引入
  1. import PizZip from 'pizzip'
  2. import Docxtemplater from 'docxtemplater'
  3. import JSZipUtils from 'jszip-utils'
  4. import { saveAs } from 'file-saver'
  5. import ImageModule from 'docxtemplater-image-module-free'
  6. import { Buffer } from 'buffer'
复制代码
2. 工具utils中新建 exportFile.js 文件,用于将图片转换为base64格式
  1. // getBase64Sync
  2. export function getBase64Sync(imgUrl) {
  3.   return new Promise((resolve, reject) => {
  4.     const image = new Image();
  5.     image.crossOrigin = 'anonymous';
  6.     image.onload = () => {
  7.       const canvas = document.createElement('canvas');
  8.       // 固定画布尺寸为90x90
  9.       canvas.width = 90;
  10.       canvas.height = 90;
  11.       const ctx = canvas.getContext('2d');
  12.       // 计算缩放比例后居中绘制
  13.       const scale = Math.min(90 / image.width, 90 / image.height);
  14.       const scaledWidth = image.width * scale;
  15.       const scaledHeight = image.height * scale;
  16.       const dx = (90 - scaledWidth) / 2;
  17.       const dy = (90 - scaledHeight) / 2;
  18.       ctx.drawImage(image, dx, dy, scaledWidth, scaledHeight);
  19.       let ext = imgUrl.split('.').pop().toLowerCase();
  20.       if (ext === 'jpg') ext = 'jpeg';
  21.       const mime = `image/${ext}`;
  22.       try {
  23.         const dataurl = canvas.toDataURL(mime, 0.8);
  24.         resolve(dataurl);
  25.       } catch (e) {
  26.         reject(e);
  27.       }
  28.     };
  29.     image.onerror = () => reject(new Error('图片加载失败'));
  30.     image.src = imgUrl + (imgUrl.includes('?') ? '&' : '?') + 't=' + Date.now();
  31.   });
  32. }
  33. // base64DataURLToArrayBuffer
  34. export function base64DataURLToArrayBuffer(dataURL) {
  35.   const base64 = dataURL.split(',')[1];
  36.   const binaryString = window.atob(base64);
  37.   const len = binaryString.length;
  38.   const bytes = new Uint8Array(len);
  39.   for (let i = 0; i < len; i++) {
  40.     bytes[i] = binaryString.charCodeAt(i);
  41.   }
  42.   return bytes.buffer;
  43. }
复制代码
3.引入 exportFile中的函数
  1. import { getBase64Sync,base64DataURLToArrayBuffer } from '@/utils/exportFile.js'
复制代码
4.设置.docx模板文件,位置放在public文件下
数组使用{#data}{/data}包裹起来
展示的字段使用{item}
图片赋值用到{%img}
模板例如:
1.png

 
5.导出按钮具体功能实现
  1. /** 方案1,不考虑图片直接导出 world */
  2. function submitForm() {
  3.   // const htmlContent = document.getElementById('htmlcontent');
  4.   // const blob = new Blob([htmlContent.innerHTML], { type: 'application/msword' });
  5.   // const url = window.URL.createObjectURL(blob);
  6.   // const a = document.createElement('a');
  7.   // a.href = url;
  8.   // a.download = '包装码.docx';
  9.   // a.click();
  10.   // window.URL.revokeObjectURL(url);
  11. }
复制代码
  1. /** 方案2,考虑图片,导出world */
  2. async function exportWord() {
  3.   try {
  4.     const content = await new Promise((resolve, reject) => {<br>      //world模板'/boxCode.docx'
  5.       JSZipUtils.getBinaryContent('/boxCode.docx?random=' + Math.random(), (error, data) => {
  6.         error ? reject(error) : resolve(data);
  7.       });
  8.     });
  9.     const zip = new PizZip(content);
  10.    
  11.     let doc = new Docxtemplater();
  12.    
  13.     // 注册图片模块
  14.     const imageOptions = {
  15.       getImage: (tag) => {
  16.         const base64Data = tag.split(',')[1];
  17.         return Buffer.from(base64Data, 'base64');
  18.       },
  19.       // 设置Word中图片显示尺寸为90x90
  20.       getSize: () => [90, 90]
  21.     };
  22.     doc.attachModule(new ImageModule(imageOptions));
  23.     doc.loadZip(zip);
  24.     const processedData = await Promise.all(
  25.       currentData.value.map(async (item) => {
  26.         const base64 = await getBase64Sync(item.boxCodeUrl);
  27.         const matches = base64.match(/^data:image\/(\w+);base64,/);
  28.         const validBase64 = matches ? base64 : `data:image/png;base64,${base64}`; //图片转码
  29.         return {
  30.           itemCode:item.itemCode?item.itemCode:'',
  31.           batchNumber:item.batchNumber?item.batchNumber:'',
  32.           itemName:item.itemName?item.itemName:'',
  33.           itemModel:item.itemModel?item.itemModel:'',
  34.           itemNumber:item.itemNumber?item.itemNumber:'',
  35.           projectName:item.projectName?item.projectName:'',
  36.           specification:item.specification?item.specification:'',
  37.           vendorName:item.vendorName?item.vendorName:'',
  38.           boxCodeUrl: validBase64 || ''
  39.         };
  40.       })
  41.     )
  42.     doc.setData({ currentData: processedData });//数据赋值
  43.     try {
  44.       doc.render();
  45.     } catch (error) {
  46.       console.error('模板渲染错误:', error.properties?.errors);
  47.       throw error;
  48.     }
  49.    
  50.     const out = doc.getZip().generate({
  51.       type: 'blob',
  52.       mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  53.     });
  54.     saveAs(out, currentItemName.value + '包装码.docx');
  55.   } catch (error) {
  56.     console.error('导出失败:', error);
  57.     throw error;
  58.   }finally{
  59.    visible.value = false
  60.   }
  61. }
复制代码
6.导出的world页面中若需要分页,在需要分页的光标位置设置分页符
 注:因为我用到了标签打印,生成的world打印时纵向横向样式展示没办法解决,所以将生成的world通过接口转为pdf输出进行打印
  1. // 生成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;    }); 
  

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册