Files
xgy-oa/klp-ui/src/views/wms/hrm/components/outLabelPrinter.vue
砂糖 9d35f39906 feat(hrm): 统一审批状态显示并添加外出条打印功能
- 在请假、外出申请页面统一审批状态显示为标签样式
- 将审批状态字段从approveStatus改为approvalStatus
- 添加外出条打印组件,支持PDF格式打印
- 优化审批状态标签颜色和文本显示
- 修复已撤销状态显示错误问题
2026-01-21 10:54:04 +08:00

320 lines
11 KiB
Vue
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.

<template>
<div>
<!-- 点击打印后为表单赋值打印内容为表单内容 -->
<div ref="printer" class="print-container" v-show="false">
<div class="print-content">
<div class="print-stub-title">存根</div>
<div class="print-stub-content">
<div class="print-stub-line">
<h4>外出条</h4>
<b class="print-stub-year" style="float: right; margin-top: 5px;">
<span>{{ printerInfo.startYear }}</span>
</b>
</div>
<div class="print-stub-apply-info">
<div class="print-stub-applicant">请假人<span>{{ printerInfo.applyName }}</span></div>
<div class="print-apply-date">
<div class="print-stub-date">
<span>{{ printerInfo.startMonth }}</span>
<span>{{ printerInfo.startDay }}</span>
</div>
<div class="print-stub-days">
<span>{{ printerInfo.startMonth }}</span>
<span>{{ printerInfo.startDay }}</span>
<span>{{ printerInfo.outHours }}</span>小时
</div>
</div>
<div class="print-stub-reason">事由<span>{{ printerInfo.reason }}</span></div>
<div class="print-stub-reason">外出地点<span>{{ printerInfo.outPlace }}</span></div>
<div class="print-stub-approver">批准人<span>{{ printerInfo.approverName }}</span></div>
</div>
</div>
<!-- 竖虚线分割 -->
<div class="print-vertical-line"></div>
<div class="print-document">
<div class="print-document-line">
<h4>外出条</h4>
</div>
<div class="print-document-apply-info">
<div class="print-document-applicant">请假人<span>{{ printerInfo.applyName }}</span></div>
<div class="print-apply-date">
<span>{{ printerInfo.startYear }}</span>
<span>{{ printerInfo.startMonth }}</span>
<span>{{ printerInfo.startDay }}</span>
<span>{{ printerInfo.endYear }}</span>
<span>{{ printerInfo.endMonth }}</span>
<span>{{ printerInfo.endDay }}</span>
<span>{{ printerInfo.outHours }}</span>小时
</div>
<div class="print-document-reason">事由<span>{{ printerInfo.reason }}</span></div>
<div class="print-document-reason">外出地点<span>{{ printerInfo.outPlace }}</span></div>
<div class="print-document-approver">批准人<span>{{ printerInfo.approverName }}</span></div>
</div>
</div>
<img class="print-stub-sign" src="http://140.143.206.120:10900/klp-oa/files/2025/12/30/1b8230d62d324fe498024b08ae4acc1d.png" alt=""></img>
</div>
</div>
</div>
</template>
<script>
import html2canvas from 'html2canvas';
import { PDFDocument } from 'pdf-lib';
// 假设你用的是Element UI的Message若不是可替换为其他提示组件
import { Message } from 'element-ui';
export default {
props: {
printerInfo: {
type: Object,
default: () => ({}),
}
},
methods: {
// 参考原方法:等待所有资源(图片、字体、二维码等)加载完成
async waitForAllResources(container) {
return new Promise((resolve) => {
// 等待图片加载
const images = container.querySelectorAll('img');
const imagePromises = Array.from(images).map(img => {
if (img.complete) return Promise.resolve();
return new Promise(resolve => {
img.onload = resolve;
img.onerror = resolve; // 加载失败也继续
});
});
// 等待字体加载(可选,根据实际需求)
const fontPromise = new Promise(resolve => setTimeout(resolve, 200));
// 等待所有资源加载完成
Promise.all([...imagePromises, fontPromise]).then(resolve);
});
},
// 重构后的打印方法先生成PDF再打印
async print() {
// 1. 获取打印容器DOM
const printContainer = this.$refs.printer?.querySelector('.print-content');
if (!printContainer) {
Message.error('未找到打印容器,无法打印');
return;
}
try {
Message.info('正在准备打印内容,请稍等...');
// 2. 定义外出条纸张尺寸(自定义,可根据实际需求调整)
// 外出条建议尺寸宽210mmA5宽度高140mm适配内容高度
const paperWidthMm = 240; // 纸张宽度(毫米)
const paperHeightMm = 100; // 纸张高度(毫米)
// 3. 创建临时隐藏容器,模拟打印布局(参考原方法)
const originalParent = printContainer.parentNode;
const originalNext = printContainer.nextSibling;
const wrapper = document.createElement('div');
wrapper.style.position = 'fixed';
wrapper.style.left = '-100000px'; // 隐藏在可视区域外
wrapper.style.top = '0';
wrapper.style.width = `${paperWidthMm}mm`; // 固定纸张宽度
wrapper.style.height = `${paperHeightMm}mm`; // 固定纸张高度
wrapper.style.boxSizing = 'border-box';
wrapper.style.backgroundColor = '#ffffff';
wrapper.style.overflow = 'hidden';
wrapper.style.padding = '10mm'; // 内边距,适配打印内容
// 设置打印内容的布局,保证居中适配
printContainer.style.width = '100%';
printContainer.style.height = '100%';
printContainer.style.display = 'flex';
printContainer.style.alignItems = 'center';
printContainer.style.justifyContent = 'center';
wrapper.appendChild(printContainer);
document.body.appendChild(wrapper);
// 4. 等待资源加载完成(字体、图片等)
await this.waitForAllResources(printContainer);
// 等待布局稳定
await new Promise(resolve => setTimeout(resolve, 100));
// 5. 用html2canvas生成高清Canvas
const containerWidth = printContainer.offsetWidth;
const containerHeight = printContainer.offsetHeight;
const canvas = await html2canvas(printContainer, {
backgroundColor: '#ffffff',
scale: 3, // 3倍缩放保证PDF清晰度
useCORS: true,
willReadFrequently: true, // 优化Canvas读取性能
width: containerWidth,
height: containerHeight,
windowWidth: containerWidth,
windowHeight: containerHeight,
});
// 6. 使用pdf-lib生成单页PDF占满整张纸精准适配
const mmToPt = 72 / 25.4; // 毫米转PDF的点pt1pt = 25.4/72 mm
const pageWidthPt = paperWidthMm * mmToPt;
const pageHeightPt = paperHeightMm * mmToPt;
// 创建PDF文档
const pdfDoc = await PDFDocument.create();
// 嵌入Canvas生成的PNG图片
const imgPng = await pdfDoc.embedPng(canvas.toDataURL('image/png'));
// 添加PDF页面设置页面尺寸与纸张一致
const page = pdfDoc.addPage([pageWidthPt, pageHeightPt]);
// 绘制图片占满整个PDF页面无边距
page.drawImage(imgPng, {
x: 0,
y: 0,
width: pageWidthPt,
height: pageHeightPt
});
// 保存PDF并生成Blob URL
const pdfBytes = await pdfDoc.save();
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const pdfUrl = URL.createObjectURL(blob);
// 7. 打开PDF窗口打印兼容弹窗拦截
const printWin = window.open(pdfUrl, '_blank');
if (!printWin) {
// 弹窗被拦截时改为下载PDF
Message.warning('浏览器弹窗被拦截已为你下载PDF文件请手动打开打印');
const a = document.createElement('a');
a.href = pdfUrl;
a.download = `外出条_${new Date().getTime()}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
// 8. 还原DOM结构和样式
// 恢复printContainer的原始样式
printContainer.style.width = '';
printContainer.style.height = '';
printContainer.style.display = '';
printContainer.style.alignItems = '';
printContainer.style.justifyContent = '';
// 把printContainer放回原位置
if (originalParent) {
if (originalNext) {
originalParent.insertBefore(printContainer, originalNext);
} else {
originalParent.appendChild(printContainer);
}
}
// 移除临时容器
document.body.removeChild(wrapper);
} catch (error) {
console.error('外出条打印准备失败:', error);
Message.error('打印内容准备失败,请重试');
// 异常时也要还原DOM避免布局错乱
const printContainer = this.$refs.printer?.querySelector('.print-content');
if (printContainer) {
printContainer.style.width = '';
printContainer.style.height = '';
printContainer.style.display = '';
printContainer.style.alignItems = '';
printContainer.style.justifyContent = '';
}
}
}
}
}
</script>
<style lang="scss" scoped>
.print-container {
width: 100%;
overflow: hidden;
padding: 20px 0;
}
// 图章
.print-stub-sign {
width: 100px;
height: 100px;
position: absolute;
bottom: 40px;
right: 60px;
}
.print-content {
display: flex;
width: fit-content;
height: fit-content;
margin: 0 auto;
padding: 20px;
position: relative;
align-items: center;
span {
display: inline-block;
min-width: 30px;
overflow: visible;
text-wrap: nowrap;
white-space: nowrap;
text-align: center;
font-size: 14px;
font-style: italic;
}
}
.print-stub-title {
font-size: 24px;
font-weight: 900;
text-align: center;
margin-bottom: 20px;
writing-mode: vertical-rl; // 文字纵向排列
}
.print-vertical-line {
width: 1px;
height: 280px;
border-left: 1px dashed #000;
margin: 0 5px;
}
.print-document,
.print-stub-content {
height: 100%;
margin: 0 auto;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.print-document-line,
.print-stub-line {
height: 50px;
min-height: 50px;
}
h4 {
text-align: center;
font-size: 24px;
font-weight: 900;
margin: 0; // 清除默认margin避免布局偏移
}
.print-document > div > div,
.print-stub-content > div > div {
border: 1px solid #000;
padding: 5px;
}
.print-apply-date {
height: 54px;
line-height: 44px;
.print-stub-date,
.print-stub-days {
line-height: 22px;
}
}
</style>