vue3 + vue3-print-nb 插件实现打印功能

前言

在现代 Web 应用开发中,打印功能是一个常见但又容易被忽视的需求。无论是电商后台的订单打印、企业管理系统的报表输出,还是教育平台的证书生成,都离不开页面打印功能的支持。对于 Vue 3 项目而言,vue3-print-nb 插件无疑是一个值得关注的选择。

本文将从实际应用角度出发,系统性地介绍 vue3-print-nb 插件的完整使用方法,涵盖从环境搭建到高级技巧的全流程内容。无论你是 Vue 3 新手还是老手,都能从中获得有价值的信息。

一、插件简介与优势

vue3-print-nb 是一款专为 Vue 3 设计的打印功能插件,它以自定义指令的形式提供简洁、高效的打印解决方案。与其他打印方案相比,该插件具有以下显著优势:

轻量级设计:插件体积小巧,对项目性能影响极低,无需引入庞大的第三方依赖。

操作简便 :通过简单的 v-print 指令即可实现复杂打印功能,学习成本极低。

功能完备:支持局部打印、全局打印、URL 打印、打印预览等多种场景,几乎涵盖所有常见打印需求。

高度可定制:提供丰富的配置选项和回调函数,开发者可以根据业务需求灵活调整打印行为。

Vue 3 原生支持:专为 Vue 3 设计,充分利用 Composition API 的优势,与现代 Vue 项目完美融合。

在实际项目中,该插件已被广泛应用于订单管理、数据报表、证书打印、合同输出等多个业务场景,获得了良好的社区反馈。

二、环境准备与安装

2.1 前置条件

在使用 vue3-print-nb 之前,请确保你的开发环境满足以下要求:

Node.js 版本建议 14.x 或更高版本,以确保最佳的兼容性。项目应基于 Vue 3 构建,推荐使用 Vite 作为构建工具以获得更好的开发体验。

2.2 安装命令

通过 npm 或 yarn 即可轻松完成插件安装。打开终端,进入项目根目录,执行以下命令:

使用 npm 安装:

bash 复制代码
npm install vue3-print-nb --save

或者使用 yarn 安装:

bash 复制代码
yarn add vue3-print-nb

如果你的项目仍在使用 Vue 2,则需要安装对应的 Vue 2 版本插件:

bash 复制代码
npm install vue-print-nb --save

本文将重点介绍 Vue 3 版本的使用方法,Vue 2 版本的基本用法类似,将在后续章节简要说明差异。

2.3 TypeScript 类型支持

对于使用 TypeScript 的项目,可能需要在类型定义文件中添加模块声明,以避免 IDE 报类型错误。在 src 目录下找到或创建 env.d.ts 文件,添加以下内容:

typescript 复制代码
declare module "vue3-print-nb";

这样 TypeScript 编译器就能正确识别插件的导入类型。

三、项目集成配置

插件安装完成后,需要在项目入口文件中进行注册配置。vue3-print-nb 支持全局注册和局部注册两种方式,开发者可以根据实际需求选择合适的方案。

3.1 全局注册(推荐)

全局注册是最常用的方式,一次配置即可在项目任意组件中使用打印功能。在项目入口文件(通常是 main.jsmain.ts)中添加以下代码:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import print from 'vue3-print-nb'

const app = createApp(App)
app.use(print)
app.mount('#app')

这种配置方式的优势在于简单快捷,所有组件无需额外导入即可使用 v-print 指令。对于大多数项目而言,全局注册是首选方案。

3.2 局部注册

在某些特定场景下,你可能希望打印功能只在特定组件中使用,此时可以选择局部注册方式。在目标组件中按需引入:

javascript 复制代码
import print from 'vue3-print-nb'

export default {
  directives: {
    print
  }
}

或者在 <script setup> 语法糖中使用:

javascript 复制代码
<script setup>
import print from 'vue3-print-nb'

defineOptions({
  directives: {
    print
  }
})
</script>

局部注册虽然增加了每个组件的配置工作量,但可以有效控制插件功能的作用范围,提升代码的可维护性。在大型项目中,建议对打印功能的使用场景进行统一规划。

四、基础使用详解

完成插件注册后,让我们通过实际示例来了解其核心用法。vue3-print-nb 提供了三种基本的打印模式:整页打印、局部打印和 URL 打印。

4.1 整页打印

整页打印是最简单的使用方式,只需在按钮上添加 v-print 指令,无需任何参数,即可打印整个页面:

html 复制代码
<template>
  <div>
    <h1>订单详情页面</h1>
    <p>订单编号:DD20240315001</p>
    <p>下单时间:2024-03-15 10:30:00</p>
    <p>订单金额:¥299.00</p>

    <!-- 打印按钮 -->
    <button v-print>打印订单</button>
  </div>
</template>

这种模式适用于简单的内容预览或不需要精细控制打印范围的场景。点击按钮后,浏览器将打开系统打印对话框,显示整个页面的打印预览。

4.2 局部范围打印

局部打印是实际项目中最常用的功能,它允许我们精确控制只打印页面中的特定区域。通过为打印区域设置唯一 ID,然后在 v-print 指令中指定该 ID 即可实现:

html 复制代码
<template>
  <div>
    <!-- 页面上的其他内容不会被打印 -->
    <div class="page-header">
      <h1>系统管理后台</h1>
      <p>欢迎使用后台管理系统</p>
    </div>

    <!-- 打印区域 -->
    <div id="printArea">
      <h2>用户信息表</h2>
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>部门</th>
            <th>职位</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>1</td>
            <td>张三</td>
            <td>技术部</td>
            <td>高级工程师</td>
          </tr>
          <tr>
            <td>2</td>
            <td>李四</td>
            <td>产品部</td>
            <td>产品经理</td>
          </tr>
        </tbody>
      </table>
    </div>

    <!-- 打印按钮 -->
    <button v-print="'#printArea'">打印表格</button>
  </div>
</template>

注意 v-print 指令的值需要使用引号包裹,并且以井号(#)开头表示 ID 选择器。也可以传入对象形式的配置参数,实现更精细的控制:

javascript 复制代码
export default {
  data() {
    return {
      printOptions: {
        id: 'printArea',
        popTitle: '用户信息表'
      }
    }
  }
}
html 复制代码
<button v-print="printOptions">打印表格</button>

4.3 URL 打印

除了打印页面内容,插件还支持打印指定 URL 的页面内容。这在需要打印独立页面或第三方页面时非常有用:

html 复制代码
<button v-print="printUrlOptions">打印指定页面</button>
javascript 复制代码
export default {
  data() {
    return {
      printUrlOptions: {
        url: 'http://localhost:8080/print-template',
        beforeOpenCallback() {
          console.log('即将打开打印工具')
        },
        openCallback() {
          console.log('打印工具已打开')
        },
        closeCallback() {
          console.log('打印工具已关闭')
        }
      }
    }
  }
}

需要特别注意的是,出于浏览器安全策略的限制,被打印的 URL 必须与当前页面遵循相同的同源策略,否则可能会被浏览器阻止。

五、完整 API 参数文档

vue3-print-nb 提供了丰富的配置选项,下面是完整的 API 参数说明文档。这些参数可以通过 v-print 指令以对象形式传入,实现各种定制化的打印效果。

参数名 说明 类型 可选值 默认值
id 局部打印的区域 ID,必填项 String --- ---
standard HTML 文档类型 String html5 / loose / strict html5
extraHead 插入到 <head> 标签内的额外元素,多个用逗号分隔 String --- ---
extraCss 额外的 CSS 样式表路径,多个用逗号分隔 String --- ---
popTitle 打印页面的标题内容 String --- ---
openCallback 打印工具成功打开时的回调函数 Function 返回 Vue 实例 ---
closeCallback 关闭打印工具成功时的回调函数 Function 返回 Vue 实例 ---
beforeOpenCallback 调用打印工具之前的回调函数 Function 返回 Vue 实例 ---
url 要打印的指定 URL(不能与 id 同时设置) String --- ---
asyncUrl 通过 resolve() 返回 URL 的异步函数 Function --- ---
preview 是否开启打印预览模式 Boolean true/false false
previewTitle 预览窗口的标题 String --- '打印预览'
previewPrintBtnLabel 预览窗口中打印按钮的文本 String --- '打印'
zIndex 预览窗口的 CSS z-index 值 Number --- 20002
previewBeforeOpenCallback 预览工具启动前的回调函数 Function 返回 Vue 实例 ---
previewOpenCallback 预览工具完全打开后的回调函数 Function 返回 Vue 实例 ---

5.1 标准文档类型参数

standard 参数用于指定打印输出的 HTML 文档类型,不同类型会影响浏览器的解析和渲染方式:

  • html5:HTML5 标准类型,兼容性最好,推荐作为默认选项
  • loose:宽松类型,允许存在一些非标准的 HTML 结构
  • strict:严格类型,要求严格的 HTML 文档结构
javascript 复制代码
printOptions: {
  id: 'printArea',
  standard: 'html5'
}

5.2 样式扩展参数

extraCssextraHead 参数允许在打印时注入额外的样式和元素,这在需要特殊打印效果时非常有用:

javascript 复制代码
printOptions: {
  id: 'printArea',
  // 引入外部 CSS 样式表
  extraCss: 'https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.min.css',
  // 添加额外的 head 元素
  extraHead: '<meta http-equiv="Content-Language" content="zh-cn"/>'
}

5.3 回调函数详解

插件提供了完整的生命周期回调函数,帮助开发者在打印过程的各个阶段执行自定义逻辑:

javascript 复制代码
printOptions: {
  id: 'printArea',
  popTitle: '打印预览',

  // 预览窗口加载前触发
  previewBeforeOpenCallback() {
    console.log('预览窗口正在加载中...')
  },

  // 预览窗口完全打开后触发
  previewOpenCallback() {
    console.log('预览窗口已显示')
  },

  // 调用系统打印对话框前触发
  beforeOpenCallback(vue) {
    // 可以访问 Vue 实例的响应式数据
    vue.printLoading = true
    console.log('准备打开打印对话框')
  },

  // 打印对话框成功打开时触发
  openCallback(vue) {
    vue.printLoading = false
    console.log('打印对话框已打开')
  },

  // 打印对话框关闭时触发
  closeCallback(vue) {
    console.log('打印操作已结束')
  }
}

六、实战应用示例

理论知识的掌握需要通过实践来巩固。下面我们通过几个实际项目中的典型场景,展示 vue3-print-nb 的完整使用流程。

6.1 订单打印功能

订单打印是企业级应用中最常见的需求之一,下面演示一个完整的订单打印组件实现:

html 复制代码
<template>
  <div class="order-detail">
    <!-- 页面其他内容 -->
    <div class="order-nav">
      <el-button @click="goBack">返回列表</el-button>
      <el-button type="primary" v-print="printOptions" :loading="printLoading">
        打印订单
      </el-button>
    </div>

    <!-- 订单基本信息 -->
    <el-card class="order-info">
      <template #header>
        <span>订单信息</span>
      </template>
      <el-descriptions :column="2" border>
        <el-descriptions-item label="订单编号">
          {{ orderData.orderNo }}
        </el-descriptions-item>
        <el-descriptions-item label="下单时间">
          {{ orderData.createTime }}
        </el-descriptions-item>
        <el-descriptions-item label="收货人">
          {{ orderData.receiverName }}
        </el-descriptions-item>
        <el-descriptions-item label="联系电话">
          {{ orderData.receiverPhone }}
        </el-descriptions-item>
        <el-descriptions-item label="收货地址" :span="2">
          {{ orderData.receiverAddress }}
        </el-descriptions-item>
      </el-descriptions>
    </el-card>

    <!-- 订单商品列表 -->
    <el-card class="order-products">
      <template #header>
        <span>商品明细</span>
      </template>
      <el-table :data="orderData.products" border stripe>
        <el-table-column prop="name" label="商品名称" />
        <el-table-column prop="spec" label="规格" />
        <el-table-column prop="price" label="单价" width="100">
          <template #default="{ row }">
            ¥{{ row.price.toFixed(2) }}
          </template>
        </el-table-column>
        <el-table-column prop="quantity" label="数量" width="80" />
        <el-table-column prop="subtotal" label="小计" width="100">
          <template #default="{ row }">
            ¥{{ row.subtotal.toFixed(2) }}
          </template>
        </el-table-column>
      </el-table>
    </el-card>

    <!-- 打印区域(隐藏的 DOM 结构) -->
    <div v-show="false">
      <div id="orderPrintArea">
        <div class="print-header">
          <h1>销售订单</h1>
          <p>单据编号:{{ orderData.orderNo }}</p>
          <p>打印时间:{{ currentDate }}</p>
        </div>

        <div class="print-section">
          <h3>客户信息</h3>
          <p>收货人:{{ orderData.receiverName }}</p>
          <p>电话:{{ orderData.receiverPhone }}</p>
          <p>地址:{{ orderData.receiverAddress }}</p>
        </div>

        <div class="print-section">
          <h3>商品清单</h3>
          <table class="print-table">
            <thead>
              <tr>
                <th>序号</th>
                <th>商品名称</th>
                <th>规格</th>
                <th>单价</th>
                <th>数量</th>
                <th>小计</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(item, index) in orderData.products" :key="index">
                <td>{{ index + 1 }}</td>
                <td>{{ item.name }}</td>
                <td>{{ item.spec }}</td>
                <td>¥{{ item.price.toFixed(2) }}</td>
                <td>{{ item.quantity }}</td>
                <td>¥{{ item.subtotal.toFixed(2) }}</td>
              </tr>
            </tbody>
            <tfoot>
              <tr>
                <td colspan="5" class="text-right">合计金额:</td>
                <td class="text-bold">¥{{ orderData.totalAmount.toFixed(2) }}</td>
              </tr>
            </tfoot>
          </table>
        </div>

        <div class="print-footer">
          <p>备注:{{ orderData.remark || '无' }}</p>
          <p class="signature">客户签名:________________</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const printLoading = ref(false)
const currentDate = computed(() => {
  return new Date().toLocaleString('zh-CN')
})

const orderData = ref({
  orderNo: 'DD2024031500001',
  createTime: '2024-03-15 10:30:00',
  receiverName: '王小明',
  receiverPhone: '138****8888',
  receiverAddress: '北京市朝阳区建国路88号SOHO现代城A座1201室',
  remark: '请勿暴力轻放',
  totalAmount: 599.00,
  products: [
    { name: 'iPhone 15 Pro', spec: '256GB 深空黑', price: 7999, quantity: 1, subtotal: 7999 },
    { name: 'MagSafe 充电器', spec: '官方标配', price: 299, quantity: 1, subtotal: 299 },
    { name: 'AirPods Pro 2', spec: 'USB-C充电盒', price: 1899, quantity: 1, subtotal: 1899 }
  ]
})

const printOptions = ref({
  id: 'orderPrintArea',
  popTitle: '订单打印',
  beforeOpenCallback() {
    printLoading.value = true
  },
  openCallback() {
    printLoading.value = false
  },
  closeCallback() {
    console.log('打印任务已完成')
  }
})

const goBack = () => {
  // 返回上一页逻辑
}
</script>

<style scoped>
/* 页面显示样式 */
.order-detail {
  padding: 20px;
}

.order-nav {
  margin-bottom: 20px;
  display: flex;
  gap: 10px;
}

/* 打印样式 - 关键部分 */
@media print {
  /* 隐藏非打印元素 */
  .order-nav,
  .order-info,
  .order-products {
    display: none !important;
  }

  /* 打印区域样式 */
  #orderPrintArea {
    padding: 20px;
    font-size: 14px;
    line-height: 1.6;
  }

  .print-header {
    text-align: center;
    margin-bottom: 30px;
    border-bottom: 2px solid #333;
    padding-bottom: 20px;
  }

  .print-header h1 {
    font-size: 24px;
    margin-bottom: 10px;
  }

  .print-section {
    margin-bottom: 25px;
  }

  .print-section h3 {
    background: #f5f5f5;
    padding: 8px 10px;
    margin-bottom: 10px;
  }

  .print-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 10px;
  }

  .print-table th,
  .print-table td {
    border: 1px solid #333;
    padding: 8px;
    text-align: center;
  }

  .print-table th {
    background: #eee;
  }

  .text-right {
    text-align: right;
  }

  .text-bold {
    font-weight: bold;
  }

  .print-footer {
    margin-top: 50px;
    border-top: 1px solid #ddd;
    padding-top: 20px;
  }

  .signature {
    margin-top: 60px;
    text-align: right;
  }
}
</style>

这个示例展示了订单打印的完整实现,包括数据展示、打印配置和打印专用样式的定义。注意在 <style> 中使用 @media print 规则来控制打印时的样式显示。

6.2 异步数据打印处理

在实际业务中,打印内容往往需要从后端接口获取。vue3-print-nb 本身不直接支持异步数据,但我们可以结合 Vue 的响应式系统和 nextTick 来实现:

html 复制代码
<template>
  <div>
    <el-button type="primary" @click="handlePrint">打印报表</el-button>

    <!-- 模拟点击触发器 -->
    <div id="printTrigger" v-print="printOptions" style="display: none"></div>

    <!-- 打印区域 -->
    <div id="reportPrintArea" style="display: none">
      <h1>数据报表</h1>
      <p>生成时间:{{ reportData.generateTime }}</p>
      <table>
        <thead>
          <tr>
            <th>指标</th>
            <th>数值</th>
            <th>同比</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item in reportData.items" :key="item.id">
            <td>{{ item.name }}</td>
            <td>{{ item.value }}</td>
            <td>{{ item.change }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</template>

<script setup>
import { ref, nextTick } from 'vue'

const reportData = ref({
  generateTime: '',
  items: []
})

const printOptions = ref({
  id: 'reportPrintArea',
  popTitle: '数据报表'
})

async function fetchReportData() {
  // 模拟接口请求
  const response = await fetch('/api/report')
  const data = await response.json()

  reportData.value = {
    generateTime: new Date().toLocaleString('zh-CN'),
    items: data.items
  }
}

async function handlePrint() {
  // 显示打印区域
  document.getElementById('reportPrintArea').style.display = 'block'

  // 等待 DOM 更新
  await nextTick()

  // 触发打印
  document.getElementById('printTrigger').click()
}
</script>

6.3 打印预览功能

打印预览是用户体验的重要环节,让用户在正式打印前确认内容无误。vue3-print-nb 内置了预览功能,只需简单配置即可启用:

html 复制代码
<template>
  <div>
    <el-button type="primary" v-print="previewOptions">
      预览并打印
    </el-button>

    <div id="previewPrintArea">
      <h2>合同文件预览</h2>
      <div class="contract-content">
        <p>甲方:________________</p>
        <p>乙方:________________</p>
        <p>签订日期:{{ currentDate }}</p>
        <div class="contract-body">
          <p>第一条 合同金额</p>
          <p>本合同总金额为人民币(大写):__________元整</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const currentDate = computed(() => {
  return new Date().toLocaleDateString('zh-CN')
})

const previewOptions = {
  id: 'previewPrintArea',
  popTitle: '合同文档',
  // 开启预览模式
  preview: true,
  // 预览窗口标题
  previewTitle: '打印预览 - 合同文档',
  // 预览窗口中打印按钮的文本
  previewPrintBtnLabel: '确认打印',
  // 预览窗口的层级
  zIndex: 20002,
  // 预览加载前回调
  previewBeforeOpenCallback() {
    console.log('正在加载预览窗口...')
  },
  // 预览加载完成回调
  previewOpenCallback() {
    console.log('预览窗口已打开')
  },
  // 打印对话框打开前回调
  beforeOpenCallback() {
    console.log('即将打开系统打印对话框')
  },
  // 打印对话框关闭回调
  closeCallback() {
    console.log('打印任务已结束')
  }
}
</script>

七、打印样式深度优化

打印效果的好坏很大程度上取决于 CSS 样式的控制。以下是一些实用的打印样式优化技巧。

7.1 分页控制

通过 CSS 可以精确控制打印内容的分页行为:

css 复制代码
@media print {
  /* 避免在表格中间分页 */
  table {
    page-break-inside: avoid;
  }

  /* 标题总是在新页开始 */
  h2, h3 {
    page-break-after: avoid;
    page-break-inside: avoid;
  }

  /* 强制在元素前分页 */
  .new-page {
    page-break-before: always;
  }

  /* 避免在链接和图片后分页 */
  a, img {
    page-break-after: avoid;
  }
}

7.2 页边距与纸张设置

使用 @page 规则可以控制打印的页面布局:

css 复制代码
@media print {
  @page {
    size: A4;  /* 纸张大小:A4、Letter、Legal 等 */
    margin: 15mm 20mm;  /* 页边距 */
  }

  /* 首页可以有不同的边距 */
  @page :first {
    margin-top: 10mm;
  }
}

7.3 页眉页脚处理

默认情况下,浏览器会打印 URL 和页码。如果想去除这些默认元素,可以使用 extraHead 参数:

javascript 复制代码
const printOptions = {
  id: 'printArea',
  extraHead: `
    <style>
      @page { margin: 10mm; }
      body { -webkit-print-color-adjust: exact; }
    </style>
  `
}

7.4 图片与颜色处理

确保打印时保持原始颜色和布局:

css 复制代码
@media print {
  /* 强制保留背景颜色和图片 */
  * {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
    -webkit-box-shadow: none !important;
    box-shadow: none !important;
  }

  /* 确保图片完整显示 */
  img {
    max-width: 100% !important;
    page-break-inside: avoid;
  }
}

八、常见问题与解决方案

8.1 打印区域为空或不完整

问题表现:点击打印后,预览区域显示空白或内容不完整。

原因分析:这通常是由于打印区域的 DOM 结构在打印时还未渲染完成,或者 CSS 样式导致元素被隐藏。

解决方案

javascript 复制代码
// 确保在 DOM 渲染完成后执行打印
import { nextTick } from 'vue'

async function handlePrint() {
  // 显示打印区域
  printAreaVisible.value = true

  // 等待 Vue 完成 DOM 更新
  await nextTick()

  // 额外等待确保样式渲染
  await new Promise(resolve => setTimeout(resolve, 100))

  // 执行打印
  document.getElementById('printTrigger').click()
}

8.2 样式在打印时丢失

问题表现:屏幕显示正常,但打印出来的效果与预览差异很大。

原因分析:打印样式可能没有正确应用,或者被其他样式覆盖。

解决方案

css 复制代码
/* 在打印区域组件内部添加 !important 确保优先级 */
@media print {
  .print-area * {
    font-family: 'SimSun', serif !important;
    color: #000 !important;
    background: #fff !important;
  }
}

8.3 跨域 URL 无法打印

问题表现:打印外部 URL 时内容为空或报错。

原因分析:浏览器的同源策略限制不允许跨域 iframe 打印。

解决方案:需要后端支持 CORS,或者将外部内容先获取到本地再打印:

javascript 复制代码
async function printRemoteContent() {
  try {
    // 通过代理获取远程内容
    const response = await fetch('/api/proxy?url=' + encodeURIComponent(targetUrl))
    const html = await response.text()

    // 将内容注入到本地 DOM
    document.getElementById('printArea').innerHTML = html

    // 执行打印
    document.getElementById('printTrigger').click()
  } catch (error) {
    console.error('获取打印内容失败', error)
  }
}

8.4 打印时出现空白页

问题表现:打印内容正常,但最后多出一页空白页。

原因分析:通常是由于页面底部存在空白区域或隐藏元素导致的。

解决方案

css 复制代码
@media print {
  html, body {
    height: auto;
    overflow: visible;
  }

  /* 确保没有多余的空白元素 */
  .no-print {
    display: none !important;
  }
}

8.5 Vue 2 与 Vue 3 版本差异

功能 Vue 2 版本 Vue 3 版本
安装命令 npm install vue-print-nb npm install vue3-print-nb
引入方式 import Print from 'vue-print-nb' import print from 'vue3-print-nb'
全局注册 Vue.use(Print) app.use(print)
局部注册 directives: { print } directives: { print }
API 参数 相同 相同

九、进阶使用技巧

9.1 批量打印实现

在实际业务中,可能需要一次性打印多个文档。可以通过循环调用或动态设置打印 ID 来实现:

html 复制代码
<template>
  <div>
    <el-button @click="printMultiple">批量打印选中订单</el-button>

    <el-checkbox-group v-model="selectedOrders">
      <el-checkbox
        v-for="order in orderList"
        :key="order.id"
        :label="order.id"
      >
        {{ order.orderNo }}
      </el-checkbox>
    </el-checkbox-group>

    <template v-for="order in orderList" :key="order.id">
      <div :id="'print-' + order.id" class="print-template">
        <h1>订单:{{ order.orderNo }}</h1>
        <!-- 订单内容 -->
      </div>
    </template>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const selectedOrders = ref([])
const orderList = ref([
  { id: 1, orderNo: 'DD001', content: '...' },
  { id: 2, orderNo: 'DD002', content: '...' },
  { id: 3, orderNo: 'DD003', content: '...' }
])

async function printMultiple() {
  if (selectedOrders.value.length === 0) {
    alert('请先选择要打印的订单')
    return
  }

  for (const orderId of selectedOrders.value) {
    await new Promise(resolve => {
      const options = {
        id: `print-${orderId}`,
        beforeOpenCallback: resolve,
        closeCallback: () => console.log(`订单 ${orderId} 打印完成`)
      }

      // 动态设置 v-print
      const btn = document.createElement('button')
      btn.setAttribute('v-print', JSON.stringify(options))
      document.body.appendChild(btn)
      btn.click()
      document.body.removeChild(btn)
    })
  }
}
</script>

9.2 打印参数动态配置

根据不同业务场景动态调整打印配置:

javascript 复制代码
function getPrintOptions(type) {
  const baseOptions = {
    id: 'printArea',
    beforeOpenCallback() {
      console.log('开始打印')
    }
  }

  // 根据类型扩展配置
  switch (type) {
    case 'invoice':
      return {
        ...baseOptions,
        popTitle: '发票打印',
        extraCss: '/styles/invoice-print.css'
      }
    case 'report':
      return {
        ...baseOptions,
        popTitle: '报表打印',
        preview: true,
        previewTitle: '报表预览'
      }
    case 'label':
      return {
        ...baseOptions,
        popTitle: '标签打印',
        extraHead: '<style>@page { size: 100mm 50mm; margin: 0; }</style>'
      }
    default:
      return baseOptions
  }
}

9.3 打印前的数据预处理

在实际项目中,打印前往往需要对数据进行格式化处理:

javascript 复制代码
function preprocessPrintData(data) {
  return {
    ...data,
    // 格式化日期
    createTime: formatDate(data.createTime),
    // 格式化金额
    amount: formatCurrency(data.amount),
    // 添加水印标识
    watermark: `打印时间:${new Date().toLocaleString()}`
  }
}

function formatDate(date) {
  return new Date(date).toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: '2-digit',
    day: '2-digit'
  })
}

function formatCurrency(amount) {
  return `¥${Number(amount).toFixed(2)}`
}

十、总结与最佳实践

通过本文的详细介绍,相信你已经对 vue3-print-nb 插件有了全面深入的理解。在实际项目中应用这个插件时,以下几点建议值得关注:

合理规划打印区域:将需要打印的内容和页面交互元素分开,使用独立的 DOM 区域来组织打印内容,便于样式控制和功能维护。

注重打印样式设计:打印效果与屏幕显示同样重要,应该针对打印场景设计专门的样式,确保输出的文档专业美观。

完善错误处理:打印功能可能因为各种原因失败,应该添加适当的错误处理和用户提示,提升用户体验。

测试多浏览器兼容性:虽然现代浏览器对打印的支持已经很好,但仍建议在不同浏览器上进行测试,确保打印效果一致。

保持性能优化:避免在打印区域中放置大量图片或复杂动画,这会严重影响打印性能。

vue3-print-nb 作为 Vue 3 生态中优秀的打印插件,以其简洁的 API、丰富的功能和良好的扩展性,值得在实际项目中推广应用。如果你有任何问题或建议,欢迎在评论区交流讨论。


参考链接

相关技术栈:Vue 3、Element Plus、Vite、TypeScript

相关推荐
paul_chen212 小时前
Vite + Vue SPA 在子路径部署(内外网访问+Nginx 反向代理)
前端·vue.js·nginx
星如雨グッ!(๑•̀ㅂ•́)و✧2 小时前
Reactor背压
java·服务器·前端
笑笑先生2 小时前
从接口搬运工到研发控制平面,BFF 到底在解决什么?
前端·架构·node.js
霪霖笙箫2 小时前
「JS全栈AI Agent学习」二、反思、工具使用、规划——让 Agent 从"执行者"变成"自主完成者"
前端·agent·ai编程
前端缘梦2 小时前
Next.js全栈项目部署全流程|从0到1解决数据库、WebSocket、图片上传所有坑
前端·全栈·next.js
www_stdio2 小时前
🚀 从 Event Loop 到 AI Agent:我的 Node.js 全栈进阶之路
前端·node.js·nestjs
www_stdio2 小时前
拒绝做Git“蜘蛛网”制造者!从分支管理到Rebase,带你走一遍标准开发流
前端·github
Moment2 小时前
面试爱问底层时,我是怎么读大型前端源码的❓❓❓
前端·javascript·面试
long_songs2 小时前
纯前端 PNG/JPG 转 PDF 工具(无需服务器,源码分享)
服务器·前端·pdf