管理后台订单页,财务要打发票:别弹浏览器框,样式用现有 HTML。后端能不动就不动。
浏览器自己搞不定静默,得本机挂打印客户端,前端用 npm 发包。我用的是 web-print-pdf:
https://www.npmjs.com/package/web-print-pdf
bash
npm install web-print-pdf
# 或 pnpm add web-print-pdf
抽了个小函数
ts
// useSilentPrint.ts
import webPrintPdf from 'web-print-pdf'
export async function printInvoiceHtml(
html: string,
opts: { printerName?: string; copies?: number; preview?: boolean } = {}
) {
return webPrintPdf.printHtml(
html,
{
paperFormat: 'A4',
margin: { top: '15mm', bottom: '15mm', left: '12mm', right: '12mm' },
printBackground: true,
},
{
printerName: opts.printerName,
paperFormat: 'A4',
copies: opts.copies ?? 1,
},
{ action: opts.preview ? 'preview' : 'print' }
)
}
页面里
打印区和屏幕展示区不必同一个 DOM,拿数据拼字符串我更稳,少被页面上的按钮、侧栏污染。
vue
<script setup lang="ts">
import { ref } from 'vue'
import { printInvoiceHtml } from '@/composables/useSilentPrint'
const props = defineProps<{ order: any }>()
const loading = ref(false)
const error = ref('')
function buildHtml() {
const rows = props.order.items
.map((i: any) => `<tr><td>${i.name}</td><td>${i.qty}</td><td>¥${i.price}</td></tr>`)
.join('')
return `
<div style="font-family:sans-serif">
<h1 style="font-size:18px">销售发票</h1>
<p>单号:${props.order.id}</p>
<p>客户:${props.order.customer}</p>
<table width="100%" cellpadding="8" cellspacing="0">${rows}</table>
<p style="text-align:right;font-weight:600">合计:¥${props.order.amount}</p>
</div>`
}
async function onPrint(preview: boolean) {
loading.value = true
error.value = ''
try {
await printInvoiceHtml(buildHtml(), { preview })
} catch (e: any) {
error.value = e?.message || '打印失败,先看本机客户端开了没'
} finally {
loading.value = false
}
}
</script>
按钮两个:一个 preview,一个真正 print。版式问题别和「连不上」搅在一起查。
联调时我真正在看的
客户端托盘在不在。
CSS 背景要 printBackground。
多机写死 printerName。
批量别自己 for 裸奔,先看文档 batchPrint。
React 也一样,换个按钮 onClick 而已。框架不是瓶颈,客户端在不在才是。
能接受弹窗的低频打印,Print.js 就够。必须静默再上这套。Print.js 的 issue 里也有人要 silent,维护者说过纯前端不行------别再找开关了。