使用@page 边距 at 规则 + modern-screenshot 定制打印输出内容

定制打印输出

📦核心

sh 复制代码
npm i modern-screenshot

🖥️ 代码

ts 复制代码
const printReport = async () => {
  // 浏览器版本检查
  const chromeVersion = navigator.userAgent.match(/Chrome\/(\d+)/)?.[1];
  if (!(chromeVersion && parseInt(chromeVersion) >= 131)) {
    await showVersionWarning();
  }

  // 创建打印iframe
  const printWindow = document.createElement('iframe');
  printWindow.style.display = 'none';
  
  printWindow.onload = async function () {
    // 处理logo
    const iconDataUrl = await domToDataUrl(logo.value!, { quality: 1 });
    
    // 克隆报表内容
    const reportClone = report.value.cloneNode(true) as HTMLDivElement;
    reportClone.querySelector('footer')!.remove();
    reportClone.querySelector('header')!.remove();
    
    // 克隆节点替换原节点
    document.querySelector('.report-container')!.replaceChild(reportClone, report.value);
    const foreignObjectSvgElement = await domToForeignObjectSvg(reportClone, {
      quality: 1,
      backgroundColor: '#fff'
    });
    // 重新替换回来
    document.querySelector('.report-container')!.replaceChild(report.value, reportClone);

    // 注入打印样式
    const linkElement = printWindow.contentWindow!.document.createElement('link');
    const styleElement = printWindow.contentWindow!.document.createElement('style');
    linkElement.type = 'text/css';
    linkElement.rel = 'stylesheet';
    linkElement.href = printCSS;
    
    // 注入动态样式
    styleElement.innerHTML = `
      :root {
        --file-no: "${documentNo}";
      }
      @page {
        @top-center {
          content: url("${iconDataUrl}") / "${companyName}";
        }
      }
    `;

    // 添加样式和内容
    printWindow.contentWindow!.document.head.appendChild(linkElement);
    printWindow.contentWindow!.document.head.appendChild(styleElement);
    printWindow.contentWindow!.document.body.appendChild(
      foreignObjectSvgElement.querySelector('.app-container')!
    );

    // 自动调整内容高度
    const content = printWindow.contentWindow!.document.querySelector('.test-data') as HTMLElement;
    content.style.height = 'auto';
    content.style.blockSize = 'auto';

    // 清理资源
    printWindow.contentWindow!.addEventListener('afterprint', () => {
      document.body.removeChild(printWindow);
      myChart?.off('finished');
    });

    // 等待样式加载完成后打印
    linkElement.addEventListener('load', () => {
      printWindow.contentWindow!.print();
    });
  };

  // 初始化iframe
  printWindow.src = 'about:blank';
  document.body.appendChild(printWindow);
};

printCSS.css

css 复制代码
@page {
  /* size: A4; */
  /* margin: 0; */
  margin-left: 0pt;
  margin-right: 0pt;
  margin-bottom: 3lh;
  margin-top: 3lh;

  @top-left {
    content: "\A \A Fender Test Report";
    font-family: 'Times New Roman';
    font-size: 12pt;
    font-weight: bold;
    white-space: pre;
    padding-left: 20px;
  }

  @top-right {
    content: "\A \A" var(--file-no);
    font-family: 'Times New Roman';
    font-size: 0.75em;
    font-weight: normal;
    white-space: pre;
    padding-right: 20px;
  }

  @bottom-center {
    content: "Footer Content";
    font-family: 'Times New Roman';
    font-size: 10pt;
    white-space: pre;
  }
}

✨优点

  1. 自定义页眉与页脚且不只限于文本
  2. 直接复用页面样式,可以使用框架组件快速构建页面

🔦注意

SVG擅长的是​​绘制几何对象​ ​,而非​​承载文档流​ ​,SVG 采用基于坐标的绝对定位(通过 xy 属性定义位置),无法像 HTML/CSS 那样实现自适应流式布局。元素无法根据容器大小自动调整位置或换行。

ts 复制代码
    // 克隆节点替换原节点
    document.querySelector('.report-container')!.replaceChild(reportClone, report.value);
    const foreignObjectSvgElement = await domToForeignObjectSvg(reportClone, {
      quality: 1,
      backgroundColor: '#fff'
    });
    // 重新替换回来
    document.querySelector('.report-container')!.replaceChild(report.value, reportClone);

此处就是通过HTML来处理文档流变化后的效果并通过svg固定下来

💡兼容性(至2025.03.30)

相关推荐
灵感__idea5 小时前
JavaScript高级程序设计(第5版):好的编程就是掌控感
前端·javascript·程序员
烛阴6 小时前
Mix
前端·webgl
代码续发7 小时前
前端组件梳理
前端
试图让你心动7 小时前
原生input添加删除图标类似vue里面移入显示删除[jquery]
前端·vue.js·jquery
陈不知代码8 小时前
uniapp创建vue3+ts+pinia+sass项目
前端·uni-app·sass
小王码农记8 小时前
sass中@mixin与 @include
前端·sass
陈琦鹏8 小时前
轻松管理 WebSocket 连接!easy-websocket-client
前端·vue.js·websocket
hui函数8 小时前
掌握JavaScript函数封装与作用域
前端·javascript
行板Andante9 小时前
前端设计中如何在鼠标悬浮时同步修改块内样式
前端
Carlos_sam9 小时前
Opnelayers:ol-wind之Field 类属性和方法详解
前端·javascript