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'
      });
相关推荐
森叶2 分钟前
Electron 安装包 asar 解压定位问题实战
前端·javascript·electron
drebander5 分钟前
ubuntu 安装 chrome 及 版本匹配的 chromedriver
前端·chrome
小ᶻᶻᶻ13 分钟前
HTML 和 CSS
html
软件技术NINI14 分钟前
html知识点框架
前端·html
深情废杨杨18 分钟前
前端vue-插值表达式和v-html的区别
前端·javascript·vue.js
GHUIJS18 分钟前
【vue3】vue3.3新特性真香
前端·javascript·vue.js
众生回避24 分钟前
鸿蒙ms参考
前端·javascript·vue.js
洛千陨25 分钟前
Vue + element-ui实现动态表单项以及动态校验规则
前端·vue.js
GHUIJS1 小时前
【vue3】vue3.5
前端·javascript·vue.js
&白帝&2 小时前
uniapp中使用picker-view选择时间
前端·uni-app