feat(hrm): 统一审批状态显示并添加外出条打印功能
- 在请假、外出申请页面统一审批状态显示为标签样式 - 将审批状态字段从approveStatus改为approvalStatus - 添加外出条打印组件,支持PDF格式打印 - 优化审批状态标签颜色和文本显示 - 修复已撤销状态显示错误问题
This commit is contained in:
320
klp-ui/src/views/wms/hrm/components/outLabelPrinter.vue
Normal file
320
klp-ui/src/views/wms/hrm/components/outLabelPrinter.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<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. 定义外出条纸张尺寸(自定义,可根据实际需求调整)
|
||||
// 外出条建议尺寸:宽210mm(A5宽度),高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的点(pt):1pt = 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>
|
||||
Reference in New Issue
Block a user