Files
admin-web/src/modules/loading-lists/printLoadingList.ts
T

83 lines
3.8 KiB
TypeScript

import { nextTick } from 'vue'
const printStyles = `
@page { size: A4 landscape; margin: 10mm; }
body { margin: 0; color: #111; font: 10pt/1.4 Arial, "Microsoft YaHei", sans-serif; }
h1 { margin: 0 0 12px; font-size: 16pt; }
.detail-summary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 16px; border-top: 1px solid #aaa; border-left: 1px solid #aaa; }
.detail-summary > div { display: flex; gap: 8px; padding: 6px; border-right: 1px solid #aaa; border-bottom: 1px solid #aaa; }
.detail-summary > div > span:first-child { flex-shrink: 0; color: #555; }
.detail-summary strong { min-width: 0; overflow-wrap: anywhere; }
table { width: 100%; border-collapse: collapse; table-layout: fixed; font-size: 8pt; }
caption { margin-bottom: 6px; font-size: 11pt; font-weight: bold; text-align: left; }
th, td { padding: 4px; border: 1px solid #aaa; vertical-align: top; text-align: left; overflow-wrap: anywhere; }
th { background: #eee; }
.cell { white-space: normal; overflow-wrap: anywhere; }
small { display: block; }
thead { display: table-header-group; }
tr { break-inside: avoid; }
`
let printing = false
function printDocumentHtml(root: HTMLElement) {
const summary = root.querySelector('[data-loading-list-print-summary]')
const items = root.querySelector('[data-loading-list-print-items]')
const head = items?.querySelector('thead')
const body = items?.querySelector('tbody')
if (!summary || !head || !body) throw new Error('清单尚未加载完成,请稍后重试')
const title = root.getAttribute('aria-label') || '装柜清单'
const page = document.implementation.createHTMLDocument(title)
page.documentElement.lang = 'zh-CN'
const style = page.createElement('style')
style.textContent = printStyles
page.head.append(style)
const heading = page.createElement('h1')
heading.textContent = title
const table = page.createElement('table')
const caption = page.createElement('caption')
caption.textContent = '装柜货物明细'
table.append(caption, head.cloneNode(true), body.cloneNode(true))
page.body.append(heading, summary.cloneNode(true), table)
page.body.querySelectorAll('button, input, svg, [hidden], [aria-hidden="true"]').forEach(node => node.remove())
page.body.querySelectorAll<HTMLElement>('[style]').forEach(node => {
if (node.style.display === 'none') node.remove()
else node.removeAttribute('style')
})
return `<!doctype html>${page.documentElement.outerHTML}`
}
export async function printLoadingList(root: HTMLElement | null | undefined): Promise<boolean> {
if (printing) return false
if (!root) throw new Error('清单尚未加载完成,请稍后重试')
printing = true
let frame: HTMLIFrameElement | undefined
try {
await nextTick()
const html = printDocumentHtml(root)
frame = document.createElement('iframe')
frame.title = '装柜清单打印'
frame.setAttribute('aria-hidden', 'true')
frame.style.cssText = 'position:fixed;left:-10000px;top:0;width:297mm;height:210mm;border:0'
document.body.append(frame)
const printDocument = frame.contentDocument
if (!printDocument) throw new Error('无法创建打印页面,请重试')
const printFrame = frame
return await new Promise<boolean>((resolve, reject) => {
printFrame.addEventListener('load', () => {
const target = printFrame.contentWindow
if (!target) { reject(new Error('无法创建打印页面,请重试')); return }
target.addEventListener('afterprint', () => resolve(true), { once: true })
try { target.focus(); target.print() } catch (cause) { reject(cause) }
}, { once: true })
printDocument.open()
printDocument.write(html)
printDocument.close()
})
} finally {
frame?.remove()
printing = false
}
}