react+html-docx-js将页面导出为docx

1.主要使用:html-docx-js进行前端导出
2.只兼容到word,wps兼容不太好
3.处理分页换行
4.处理页眉

index.tsx

import { saveAs } from 'file-saver';
import htmlToDocxGenerate from './HtmlToDocx';

  const handleExportByHtml = async () => {
    const exportConfig = getSaveParams({
      currentTemplate,
      formConfigParams: formParams
    });
    const filename = getFileName(exportConfig, formParams);

    try {
      const content = document.getElementById('docx-container');
      if (!content) {
        message.error('未找到导出内容');
        return;
      }

      const docxBlob = await htmlToDocxGenerate(content.outerHTML, {
        containerId: 'docx-container',
        pageHeaderId: 'page-header',
        pageBreakClassName: 'page-break'
      });

      saveAs(docxBlob, `${filename ?? '分析报告'}.docx`);
      message.success('材料已下载成功,请查看下载文件夹');
    } catch (err) {
      message.error('导出失败:' + (err as Error).message);
    }
  };

HtmlToDocx/index.ts

import HtmlDocx from 'html-docx-js/dist/html-docx';  // 使用浏览器版本
import _ from 'lodash';
import docxHtml from './pageHtml';

interface DocumentOptions {
  orientation: 'portrait' | 'landscape';
  margins: {
    top: number;
    right: number;
    bottom: number;
    left: number;
    header: number;
    footer: number;
    gutter: number;
  };
}

interface HtmlToDocxOptions {
  containerId?: string;
  chartClassName?: string;
  pageBreakClassName?: string;
  pageHeaderId?: string;
  document?: Partial<DocumentOptions>;
}

const htmlToDocxGenerate = async (originalHtml: string, options: HtmlToDocxOptions = {}): Promise<Blob> => {
  const defaultDocument: DocumentOptions = {
    orientation: 'portrait',
    margins: {
      top: 1440,
      right: 1440,
      bottom: 1440,
      left: 1440,
      header: 720,
      footer: 720,
      gutter: 0,
    },
  };

  const defaultOptions = {
    containerId: 'docx-container',
    chartClassName: 'docx-chart',
    pageBreakClassName: 'page-break',
    pageHeaderId: 'page-header',
  };
  const pageBreakReplaceSymbol = '
'
; const finalOptions = _.merge(defaultOptions, options); const { containerId, pageBreakClassName, pageHeaderId } = finalOptions; // 创建一个临时的 div 来解析 HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = originalHtml; // 处理页眉 const pageHeaderElem = tempDiv.querySelector(`#${pageHeaderId}`); const pageHeaderHtml = pageHeaderElem?.innerHTML || ''; pageHeaderElem?.remove(); // 处理图片尺寸 const images = tempDiv.querySelectorAll('img'); images.forEach((img) => { const originalWidth = Number(img.getAttribute('width')?.replace('px', '') || 0); const originalHeight = Number(img.getAttribute('height')?.replace('px', '') || 0); const docxWidth = originalWidth * 0.73; img.setAttribute('width', docxWidth.toString()); img.setAttribute('height', (docxWidth * (originalHeight / originalWidth)).toString()); }); // 处理分页符 const pageBreaks = tempDiv.querySelectorAll(`.${pageBreakClassName}`); pageBreaks.forEach((elem) => { elem.innerHTML = pageBreakReplaceSymbol; }); // 获取最终的 HTML const container = tempDiv.querySelector(`#${containerId}`); const html = container?.innerHTML || ''; const pageBreak = "
"
; const finalHtml = _.replace( _.replace(docxHtml, '{{pageHeaderHtml}}', pageHeaderHtml), '{{docxHtml}}', _.replace(html, pageBreakReplaceSymbol, pageBreak) ); const blob = HtmlDocx.asBlob(finalHtml, _.defaultsDeep(options.document || {}, defaultDocument)); return blob; }; export default htmlToDocxGenerate;

pageHtml.ts。纯样式,视情况修改,因为我用了富文本,所以还引入了quillCoreCss和quillSnowCss,不需要的可不加。

'use strict';
import quillCoreCss from './quill-core-css';
import quillSnowCss from './quill-snow-css';

const pageHtml = `



    
    



    
{{pageHeaderHtml}}
{{docxHtml}}
`
; export default pageHtml;

你可能感兴趣的:(react+html-docx-js将页面导出为docx)