Files
estate/src/utils/print.ts
2026-09-20 08:57:53 +08:00

387 lines
9.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 🔥 Vue后台专用 - 打印指定DIV(样式完全还原 + 无侧边栏/导航)
* 适配 Element Plus / scoped样式 / 后台管理系统
*/
export function printVueDiv(elementId: string, title: string = '打印') {
const el = document.getElementById(elementId);
if (!el) {
console.error('未找到打印元素');
return;
}
const win = window.open('', '_blank', 'width=1000,height=800');
if (!win) return;
// ==============================================
// 【核心1】复制 全局样式 + Element Plus 样式
// ==============================================
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style:not([scoped])'))
.map((item) => item.outerHTML)
.join('');
// ==============================================
// 【核心2】手动注入打印区域的 scoped 样式(关键!)
// ==============================================
const printScopeStyle = `
<style>
* {
box-sizing: border-box;
margin:0;
padding:0;
}
body {
margin: 10px;
padding: 0;
background: #fff;
font-size: 12px; /* 全局缩小字体(核心) */
font-family: "Microsoft YaHei", sans-serif;
}
/* 强制不显示后台侧边栏/导航 */
.el-container, .el-aside, .el-header, .el-footer {
display: none !important;
}
/* 打印区域基础样式 */
#print-content {
width: 100% !important;
}
/* 标题样式(缩小) */
#print-content h3 {
font-size: 16px;
text-align: center;
margin: 10px 0;
font-weight: bold;
}
#print-content p {
font-size: 12px;
margin: 5px 0;
}
/* ====================== 表格核心优化 ====================== */
table {
border-collapse: collapse;
width: 100% !important;
/*table-layout: fixed; !* 固定表格布局,防止列变形 *!*/
}
table td, table th {
border: 1px solid #333;
font-size: 9px; /* 表格字体最小化 */
text-align: center;
/*white-space: nowrap; !* ✅ 禁止文字自动换行(核心) *!*/
/*overflow: hidden;*/
}
/* 适配合并列 */
td[colspan], th[colspan] {
white-space: normal !important;
}
/* 打印纸张设置 */
@page {
size: A4;
margin: 8mm; /* 缩小页边距 */
}
.navtitle {
text-align: center;
height: 41px;
line-height: 14px;
color: rgba(48, 49, 51, 1);
font-size: 20px;
margin-bottom: 20px;
}
p {
line-height: 14px;
color: rgba(48, 49, 51, 1);
font-size: 9px;
margin-bottom: 10px;
}
table {
margin-top: 15px;
width: 100%;
border-collapse: collapse;
color: rgba(48, 49, 51, 1);
}
tr {
border: 1px solid #dddddd;
}
td {
border: 1px solid #dddddd;
height: 50px;
line-height: normal;
background-color: rgba(255, 255, 255, 1);
text-align: center;
font-size: 16px;
font-weight: 450;
}
.footertr td {
height: 60px;
}
.foorter {
display: grid;
grid-template-columns: repeat(3, 1fr);
width: 100%;
gap: 30px;
margin: 20px auto 0;
}
.formark {
font-size: smaller;
margin-top: 10px;
}
</style>
`;
// ==============================================
// 【核心3】写入新窗口
// ==============================================
win.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<meta charset="UTF-8">
${styles}
${printScopeStyle}
</head>
<body>
${el.outerHTML} <!-- 直接用innerHTML复制完整结构 -->
</body>
</html>
`);
win.document.close();
win.onload = () => {
setTimeout(() => {
win.print();
win.close();
}, 300);
};
}
/**
* 打印指定 DOM 元素(绝对不会打印其他内容)
* @param elementId 目标 div 的 id
* @param title 打印标题(可选)
*/
export function printDivPure(elementId: string, title: string = '打印') {
// 1. 获取目标元素
const targetElement = document.getElementById(elementId);
if (!targetElement) {
console.error(`未找到 id 为 "${elementId}" 的元素`);
return;
}
// 2. 保存原页面的所有内容
const originalBodyContent = document.body.innerHTML;
// 3. 克隆目标元素
const cloneElement = targetElement.cloneNode(true) as HTMLElement;
// 4. 【核心】把 body 替换成只有目标元素的内容
document.body.innerHTML = '';
// 添加打印专用样式
const printStyle = document.createElement('style');
printStyle.innerHTML = `
@page {
size: A4;
margin: 10mm;
}
body {
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
`;
document.head.appendChild(printStyle);
// 插入目标元素
document.body.appendChild(cloneElement);
// 5. 打印
window.print();
// 6. 【核心】打印完恢复原页面内容
setTimeout(() => {
document.body.innerHTML = originalBodyContent;
document.head.removeChild(printStyle);
// 强制刷新一下 Vue 组件(避免恢复后事件丢失)
window.location.reload();
}, 100);
}
/**
* 打印指定 DOM 元素(新窗口法加强版,不刷新页面)
* @param elementId 目标 div 的 id
* @param title 打印标题(可选)
*/
export function printDivNewWindow(elementId: string, title: string = '打印') {
const targetElement = document.getElementById(elementId);
if (!targetElement) {
console.error(`未找到 id 为 "${elementId}" 的元素`);
return;
}
const printWindow = window.open('', '_blank', 'width=800,height=1000');
if (!printWindow) {
console.error('无法打开新窗口,请检查浏览器弹窗拦截设置');
return;
}
// 【关键】深度克隆目标元素,包括所有子节点
const clone = targetElement.cloneNode(true) as HTMLElement;
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<meta charset="UTF-8">
<!-- 复制所有外部 CSS -->
${Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
.map((l) => l.outerHTML)
.join('')}
<!-- 复制所有 style 标签 -->
${Array.from(document.querySelectorAll('style'))
.map((s) => s.outerHTML)
.join('')}
<style>
body { margin: 20px; padding: 0; }
@page { size: A4; margin: 10mm; }
</style>
</head>
<body></body>
</html>
`);
// 把克隆的元素插入到新窗口
printWindow.document.body.appendChild(clone);
printWindow.document.close();
// 等待加载完成后打印
printWindow.onload = () => {
printWindow.focus();
setTimeout(() => {
printWindow.print();
printWindow.close();
}, 300);
};
}
/**
* 🔥 Vue后台专用 - 票据专用打印(固定尺寸 宽10cm 高25cm)
* 新窗口打印指定DIV、还原原样式、屏蔽后台侧边栏/导航、适配Element Plus
* 纸张规格:10cm(宽) × 25cm(高) 票据纸
*/
export function printTicketDiv(elementId: string, title: string = '票据打印', callback?: () => void) {
const el = document.getElementById(elementId);
if (!el) {
console.error('未找到打印元素');
return;
}
const win = window.open('', '_blank', 'width=1200,height=1000');
if (!win) return;
const styles = '';
const printScopeStyle = `
<style>
* {
box-sizing: border-box;
margin: 0 !important;
padding: 0 !important;
}
html, body {
width: 100%;
height: 100%;
background: #fff;
font-size: 12px;
font-family: "Microsoft YaHei", sans-serif;
page-break-inside: avoid !important;
}
/* 隐藏后台布局 */
.el-container, .el-aside, .el-header, .el-footer {
display: none !important;
}
/* ✅ @page 提升到外层,不要嵌套在 @media print 内部!修复不生效BUG */
@page {
size: 25cm 10cm !important;
margin: 0 !important;
}
/* 自定义纸张 25cm × 10cm 无边距 */
@media print {
html {
height: 10cm !important;
overflow: hidden !important;
}
body {
/* 强制内容左上角对齐,禁止浏览器自动居中 */
transform-origin: top left !important;
position: absolute !important;
left: 0 !important;
top: 0 !important;
height: 10cm !important;
overflow: hidden !important;
}
/* =========票据容器:贴左上角,不再居中========= */
#${elementId} {
width: 25cm !important;
height: 10cm !important;
box-sizing: border-box;
page-break-inside: avoid !important;
page-break-before: avoid !important;
page-break-after: avoid !important;
break-inside: avoid;
break-before: avoid;
break-after: avoid;
padding: 8mm; /* 内部留白,模拟页边距 */
}
.title{
font-weight: 800;
}
table {
border-collapse: collapse;
width: 100% !important;
page-break-inside: avoid !important;
break-inside: avoid;
table-layout: auto;
}
table th, table td {
border: 1px solid #333;
font-size: 10px;
height:40px;
text-align: center;
padding: 1px 2px;
line-height: 1.1;
white-space: normal;
}
}
</style>
`;
const html = `
<!DOCTYPE html>
<html>
<head>
<title>${title}</title>
<meta charset="UTF-8">
${styles}
${printScopeStyle}
</head>
<body>
${el.outerHTML}
</body>
</html>
`;
win.document.write(html);
win.document.close();
win.onload = () => {
setTimeout(() => {
win.print();
win.close();
callback && callback();
}, 300);
};
}