fix(outbound): 终极护航版confirmPrint,iframe克隆样式+强制分页CSS

This commit is contained in:
DXC
2026-04-21 14:39:48 +08:00
parent dd54e047dd
commit 2006b7275f

View File

@ -23,7 +23,7 @@
批量操作 批量操作
</el-button> </el-button>
<el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="danger" :disabled="selectedItems.length === 0" @click="clearAll"> <el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="danger" :disabled="selectedItems.length === 0" @click="clearAll">
清空货车 清空列表
</el-button> </el-button>
<el-divider direction="vertical" /> <el-divider direction="vertical" />
<el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="primary" :icon="Plus" @click="openManualSelect"> <el-button v-if="userStore.hasPermission('outbound_selection:operation')" type="primary" :icon="Plus" @click="openManualSelect">
@ -794,18 +794,9 @@ const handlePreview = () => {
previewVisible.value = true previewVisible.value = true
} }
// 创建隐藏 iframe 用于打印
const printFrame = document.createElement('iframe');
printFrame.style.position = 'absolute';
printFrame.style.width = '0';
printFrame.style.height = '0';
printFrame.style.border = '0';
document.body.appendChild(printFrame);
const iframeDoc = printFrame.contentWindow.document;
const confirmPrint = async () => { const confirmPrint = async () => {
previewVisible.value = false; previewVisible.value = false;
// 记录日志
try { try {
const payload = validSelectedItems.value.map(item => ({ const payload = validSelectedItems.value.map(item => ({
name: item.name, standard: item.standard, quantity: item.export_quantity name: item.name, standard: item.standard, quantity: item.export_quantity
@ -813,85 +804,89 @@ const confirmPrint = async () => {
printSelectionList(JSON.parse(JSON.stringify(payload))).catch(() => {}); printSelectionList(JSON.parse(JSON.stringify(payload))).catch(() => {});
} catch (e) {} } catch (e) {}
// 获取 #print-area 元素和当前 computed 样式 setTimeout(() => {
// 1. 获取要打印的区域 DOM
const printElement = document.getElementById('print-area'); const printElement = document.getElementById('print-area');
if (!printElement) return; if (!printElement) return;
const styles = Array.from(document.styleSheets)
.filter(sheet => {
try { return !sheet.href || sheet.href.indexOf(window.location.host) !== -1; } catch { return false; }
})
.map(sheet => {
try {
return Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
} catch { return ''; }
}).join('\n');
// 4. 将提取的样式和要打印的 HTML 写入 iframe // 2. 创建并挂载隐藏的 iframe
const iframe = document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.right = '0';
iframe.style.bottom = '0';
iframe.style.width = '0';
iframe.style.height = '0';
iframe.style.border = '0';
document.body.appendChild(iframe);
const iframeDoc = iframe.contentWindow?.document || iframe.contentDocument;
if (!iframeDoc) return;
// 3. 安全初始化 iframe 骨架(只写基本结构,不拼接任何业务代码)
iframeDoc.open(); iframeDoc.open();
iframeDoc.write(` iframeDoc.write('<!DOCTYPE html><html><head><title>出库单打印</title></head><body></body></html>');
<!DOCTYPE html> iframeDoc.close();
<html>
<head> // 4. 【核心修复】安全克隆所有样式节点,彻底告别乱码
<title>IRIS出库拣货确认单</title> const styles = document.querySelectorAll('style, link[rel="stylesheet"]');
${styles} styles.forEach(styleNode => {
<style> iframeDoc.head.appendChild(styleNode.cloneNode(true));
/* ================= 终极分页破解指令 ================= */ });
/* 1. 彻底粉碎所有从全局继承来的高度锁死和隐藏属性 */
// 5. 动态追加针对打印的强制分页 CSS
const customStyle = iframeDoc.createElement('style');
customStyle.innerHTML = `
/* 重置基础布局,解除所有高度死锁 */
html, body { html, body {
height: auto !important; height: auto !important;
min-height: 100% !important; min-height: 100% !important;
max-height: none !important;
overflow: visible !important; overflow: visible !important;
position: static !important;
margin: 0 !important;
padding: 0 !important;
background: white !important; background: white !important;
margin: 0;
padding: 0;
} }
/* 规范 A4 纸张 */
/* 2. 规范 A4 纸张,利用浏览器原生分页 */
@page { @page {
size: A4 portrait; size: A4 portrait;
margin: 10mm; margin: 10mm;
} }
/* 确保打印区正常流式显示 */
/* 3. 强制打印区化为最普通的流式元素,解除一切束缚 */
#print-area { #print-area {
display: block !important; display: block !important;
position: static !important; position: static !important;
height: auto !important;
overflow: visible !important;
width: 100% !important; width: 100% !important;
margin: 0 !important; height: auto !important;
} }
/* 核心:保护表格不被跨页截断 */
/* 4. 让表格宽度自适应 A4 纸,并严禁在行中间断裂 */
.print-table { .print-table {
width: 100% !important; width: 100% !important;
table-layout: auto !important; table-layout: auto !important;
border-collapse: collapse;
} }
.print-table tr, .print-table td, .print-table th { .print-table tr, .print-table td, .print-table th {
page-break-inside: avoid !important; page-break-inside: avoid !important;
break-inside: avoid !important; break-inside: avoid !important;
} }
/* 隐藏不需要的全局 UI */
/* 5. 隐藏 Vue 拷进来的多余弹窗 UI */
.el-overlay, .el-dialog__wrapper, .no-print-content { .el-overlay, .el-dialog__wrapper, .no-print-content {
display: none !important; display: none !important;
} }
</style> `;
</head> iframeDoc.head.appendChild(customStyle);
<body>
${printElement.outerHTML}
</body>
</html>
`);
iframeDoc.close();
// 等 iframe 加载完毕后触发打印 // 6. 【核心修复】安全克隆打印区域到 body 中
printFrame.onload = () => { iframeDoc.body.appendChild(printElement.cloneNode(true));
printFrame.contentWindow.focus();
printFrame.contentWindow.print(); // 7. 延迟触发打印,等待样式完全渲染
}; setTimeout(() => {
iframe.contentWindow?.focus();
iframe.contentWindow?.print();
// 打印结束后清理 iframe
setTimeout(() => {
document.body.removeChild(iframe);
}, 1000);
}, 500); // 预留 500ms 渲染时间
}, 300);
} }
const confirmExport = () => { const confirmExport = () => {