html-docx-js和file-saver实现html导出word

依赖html-docx-js,file-saver,html2canvas

javascript 复制代码
import { asBlob } from 'html-docx-js/dist/html-docx';
import { saveAs } from 'file-saver';
import html2Canvas from 'html2canvas';

const handleImageToBase64 = (cloneEle) => {
  let imgElements = cloneEle.getElementsByTagName('img');
  let canvas = document.createElement('canvas');
  let ctx = canvas.getContext('2d');
  const promises = Array.from(imgElements).map((item) => {
    return new Promise((resolve) => {
      item.onload = () => {
        ctx.clearRect(0, 0, item.width, item.height);
        canvas.width = item.width;
        canvas.height = item.height;
        ctx.drawImage(item, 0, 0, item.width, item.height);
        let ext = '';
        if (item.src.indexOf('data:image/svg+xml;base64') === 0) {
          ext = 'png';
        } else {
          ext = item.src.substring(item.src.lastIndexOf('.') + 1).toLowerCase();
        }
        let dataURL = canvas.toDataURL('image/' + ext);
        item.setAttribute('src', dataURL);
        resolve();
      };
    });
  });
  canvas.remove();
  return promises;
};

const handleCanvasToImage = (cloneEle) => {
  const canvasElements = cloneEle.getElementsByTagName('canvas');
  const promises = Array.from(canvasElements).map((ca, index) => {
    return new Promise((resolve) => {
      const url = ca.toDataURL('image/png', 1);
      const img = new Image();
      img.onload = () => {
        URL.revokeObjectURL(url);
        resolve();
      };
      img.src = url;
      // 生成img插入clone的dom的canvas之前
      canvasElements[index].parentNode.insertBefore(img, canvasElements[index]);
    });
  });
  // 移除原来的canvas
  Array.from(canvasElements).forEach((ca) => ca.parentNode.removeChild(ca));
  return promises;
};

const handleSvgToImage = (cloneEle) => {
  const svgElements = cloneEle.getElementsByTagName('svg');
  Array.from(svgElements).forEach((svg) => {
    // 将SVG元素转换为PNG图片
    const img = new Image();
    img.src = 'data:image/svg+xml;base64,' + window.btoa(encodeURIComponent(svg.outerHTML).replace(/%([0-9A-F]{2})/g, function(match, p1) {
      return String.fromCharCode('0x' + p1);
    }));
    // 将图片插入到SVG元素的位置
    svg.parentNode.insertBefore(img, svg);
    // 移除原来的SVG元素
    svg.parentNode.removeChild(svg);
  });
};

const handleCodeToImage = (ele, cloneEle) => {
  let codeElements = ele.querySelectorAll('pre code');
  let cloneCodeElements = cloneEle.querySelectorAll('pre code');
  const promises = Array.from(codeElements).map((item, index) => {
    return new Promise((resolve) => {
      const pre = item.parentNode;
      html2Canvas(pre, {
        imageTimeout: 2000,
        logging: false,
        scrollY: 0,
        scrollX: 0,
        scale: window.devicePixelRatio * 1.2, // 添加的scale 参数
        width: item.offsetWidth + 32,
        allowTaint: false,
        useCORS: true, // 开启跨域
      }).then(canvas => {
        // 将 Canvas 转换为图片
        let dataURL = canvas.toDataURL('image/png');
        // 创建一个新的 <img> 元素并设置其 src 属性
        const img = new Image();
        img.src = dataURL;
        // 将图片插入到SVG元素的位置
        const clonePre = cloneCodeElements[index].parentNode;
        clonePre.parentNode.insertBefore(img, clonePre);
        // 移除原来的SVG元素
        clonePre.parentNode.removeChild(clonePre);
        resolve();
      });
    });
  });
  return promises;
};

// 表格虚线 向右偏移10px左右
const handleTableStyle = (cloneEle) => {
  let tableElements = cloneEle.getElementsByTagName('table');
  Array.from(tableElements).forEach((table) => {
    table.style.borderCollapse = table.style.borderCollapse || 'collapse';
    table.border = table.border || '1';
    table.style.marginLeft = '10px';
  });
  let thElements = cloneEle.getElementsByTagName('th');
  Array.from(thElements).forEach((th) => {
    th.style.backgroundColor = '#f0f0f0';
  });
};

const getMarkdownCss = () => {
  return `
  body {
    color: #383c4a;
    font-family: -apple-system, blinkmacsystemfont, "Segoe UI", helvetica, arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
    font-size: 12px;
    word-wrap: break-word;
  }
  `;
};

const handleHtml = async (ele, cloneEle, type) => {
  const canvasPromises = handleCanvasToImage(cloneEle);
  await Promise.all(canvasPromises);
  handleSvgToImage(cloneEle);
  const imagePromises = handleImageToBase64(cloneEle);
  const codePromises = handleCodeToImage(ele, cloneEle);
  await Promise.all(codePromises);
  await Promise.all(imagePromises);
  handleTableStyle(cloneEle);
  let cssString = '';
  if (type === 'markdown') {
    cssString = getMarkdownCss();
  }
  const innerHtml = cloneEle.outerHTML
    // strong在word中不生效问题
    .replace(/<strong class="(.*?)">(.*?)<\/strong>/g, '<b class="$1">$2</b>')
    .replace(/<mark/g, '<span')
    .replace(/<\/mark>/g, '</span>');
  const htmlString = `
  <html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40">
  <head>
  <style type="text/css">${cssString}</style> 
  </head>
  <body>
  ${innerHtml}
  </body>
  </html>`;
  return htmlString;
};

export default function exportWord({ selector, name = 'export', type = 'markdown' }) {
  if (!selector) return Promise.reject();
  // eslint-disable-next-line no-async-promise-executor
  return new Promise(async (resolve) => {
    const ele = document.querySelector(selector);
    const cloneEle = ele.cloneNode(true);
    const htmlString = await handleHtml(ele, cloneEle, type);
    const converted = asBlob(htmlString);
    saveAs(converted, `${name}.docx`);
    resolve();
  });

}


}

页面调用

javascript 复制代码
exportWord({
        selector: '.report-container'
      });
相关推荐
并不会40 分钟前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
衣乌安、43 分钟前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜43 分钟前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师1 小时前
CSS的三个重点
前端·css
耶啵奶膘2 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^4 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie4 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic5 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿5 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具6 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端