使用@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)

相关推荐
来碗盐焗星球16 分钟前
实时通信技术开发经历
前端·javascript
阳火锅18 分钟前
为了实现AI对话的打字效果,我封装一个vue3自定义指令
前端·vue.js·面试
就是我27 分钟前
【React Hook】深入浅出:10分钟理解useContext
前端·javascript·react.js
银色的白28 分钟前
重构前端代码,定义开发规划
前端·重构
HarryHY41 分钟前
检查项目中的依赖是否有更新——npm outdated
前端·npm·node.js
lens941 小时前
Oxlint:干掉ESLint卡顿!前端火箭级代码检查器来了!
前端·javascript
RePeaT1 小时前
JavaScript切换元素显示隐藏的方法
前端·javascript
bitbitDown1 小时前
同事用了个@vue:mounted,我去官网找了半天没找到
前端·javascript·vue.js
孜然卷k1 小时前
前端样式CSS设置 display: ‘grid‘, gridTemplateColumns: ‘repeat(4, 1fr)‘ 部分电脑展示内容溢出
前端·css