Files
xgy-oa/klp-ui/src/views/wms/coil/panels/LabelRender/index.vue
2026-01-09 14:25:20 +08:00

337 lines
12 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 class="label-render-container">
<!-- 标签预览容器 -->
<div class="preview-container" id="label-preview-container" ref="labelRef">
<ProductionTagPreview v-if="labelType === '2'" :content="content" />
<OuterTagPreview v-if="labelType === '3'" :content="content" />
<SampleTagPreview v-if="labelType === '4'" :content="content" />
<ForgeTagPreview v-if="labelType === '5'" :content="content" />
<SaltSprayTagPreview v-if="labelType === '6'" :content="content" />
</div>
<div class="action-buttons">
<el-button type="primary" @click="downloadLabelAsImage">下载标签图片</el-button>
<el-button type="primary" @click="printLabel" style="margin-left: 10px;">打印标签</el-button>
</div>
</div>
</template>
<script>
import domToImage from 'dom-to-image';
import printJS from 'print-js';
import html2canvas from 'html2canvas'; // 新增:引入高清渲染库
import { Message } from 'element-ui';
import ProductionTagPreview from './ProductionTagPreview.vue';
import OuterTagPreview from './OuterTagPreview.vue';
import SampleTagPreview from './SampleTagPreview.vue';
import ForgeTagPreview from './ForgeTagPreview.vue';
import SaltSprayTagPreview from './SaltSprayTagPreview.vue';
export default {
name: 'LabelRender',
components: {
ProductionTagPreview,
OuterTagPreview,
SampleTagPreview,
ForgeTagPreview,
SaltSprayTagPreview,
},
props: {
labelType: {
type: String,
required: true,
},
content: {
type: Object,
required: true,
},
},
methods: {
// -------- 图片下载方法保留可按需替换为html2canvas --------
async downloadLabelAsImage() {
const labelContainer = document.getElementById('label-preview-container');
if (!labelContainer) {
Message.error('未找到标签容器,无法下载');
return;
}
try {
// 可选也替换为html2canvas提升下载清晰度
// const canvas = await html2canvas(labelContainer, { scale: 3, backgroundColor: '#ffffff', allowTaint: true, taintTest: false });
// const dataUrl = canvas.toDataURL('image/png', 1.0);
const dataUrl = await domToImage.toPng(labelContainer);
const downloadLink = document.createElement('a');
downloadLink.href = dataUrl;
downloadLink.download = `标签_${new Date().getTime()}.png`;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
} catch (error) {
console.error('标签图片下载失败:', error);
Message.error('标签图片下载失败,请重试');
}
},
// -------- 重构后的打印方法(核心优化) --------
async printLabel() {
// 1. 获取标签容器DOM
const labelContainer = document.querySelector('.label-container') ||
document.querySelector('.material-label-container');
if (!labelContainer) {
Message.error('未找到标签容器,无法打印');
return;
}
try {
Message.info('正在准备打印内容,请稍等...');
// 2. 等待二维码/字体等资源加载完成(复用之前的等待逻辑)
await this.waitForAllResources(labelContainer);
// 3. 计算纸张尺寸和缩放比例
// 纸张尺寸180mm x 100mm
// 1mm ≈ 3.779527559px (96 DPI)
const paperWidthMm = 180;
const paperHeightMm = 100;
const dpi = 96;
const mmToPx = dpi / 25.4;
const paperWidthPx = paperWidthMm * mmToPx; // 约 680.3px
const paperHeightPx = paperHeightMm * mmToPx; // 约 377.95px
// 获取标签容器的实际尺寸
const containerRect = labelContainer.getBoundingClientRect();
const containerWidth = containerRect.width;
const containerHeight = containerRect.height;
// 计算缩放比例确保内容适配到纸张留出2mm边距
const marginMm = 2;
const marginPx = marginMm * mmToPx;
const availableWidth = paperWidthPx - marginPx * 2;
const availableHeight = paperHeightPx - marginPx * 2;
const scaleX = containerWidth > 0 ? availableWidth / containerWidth : 1;
const scaleY = containerHeight > 0 ? availableHeight / containerHeight : 1;
const printScale = Math.min(scaleX, scaleY, 1); // 不超过1不放大
// 计算最终Canvas尺寸适配纸张
const finalCanvasWidth = containerWidth * printScale;
const finalCanvasHeight = containerHeight * printScale;
// 使用合适的scale值生成高清Canvas但不超过纸张尺寸
const canvasScale = Math.min(1.5, printScale * 1.5); // 确保不超过纸张
// 4. 用html2canvas生成高清Canvas解决文字模糊+二维码丢失)
const canvas = await html2canvas(labelContainer, {
scale: canvasScale,
backgroundColor: '#ffffff', // 强制白色背景,避免打印时背景透明
useCORS: true, // 支持跨域图片
allowTaint: true, // 允许渲染canvas二维码
taintTest: false, // 关闭canvas污染检测
width: containerWidth,
height: containerHeight,
logging: false,
});
// 5. 如果Canvas尺寸超出纸张需要缩放Canvas
let finalCanvas = canvas;
if (canvas.width > paperWidthPx || canvas.height > paperHeightPx) {
// 创建新的Canvas尺寸适配纸张
const scaledCanvas = document.createElement('canvas');
scaledCanvas.width = Math.min(canvas.width, paperWidthPx);
scaledCanvas.height = Math.min(canvas.height, paperHeightPx);
const ctx = scaledCanvas.getContext('2d');
ctx.drawImage(canvas, 0, 0, scaledCanvas.width, scaledCanvas.height);
finalCanvas = scaledCanvas;
}
// 6. 创建临时打印容器避免影响原DOM
const printContainerId = 'temp-print-container-' + new Date().getTime();
const tempPrintContainer = document.createElement('div');
tempPrintContainer.id = printContainerId;
tempPrintContainer.style.cssText = 'position: absolute; left: -9999px; top: -9999px;';
// 将Canvas插入临时容器
finalCanvas.style.cssText = `width: ${paperWidthMm}mm; height: ${paperHeightMm}mm; max-width: 100%; max-height: 100%; object-fit: contain;`;
tempPrintContainer.appendChild(finalCanvas);
document.body.appendChild(tempPrintContainer);
// 7. 调用printJS打印高清Canvas而非原HTML
printJS({
printable: tempPrintContainer, // 打印临时容器
type: 'html',
header: null,
footer: null,
scanStyles: false, // 禁用自动扫描样式,手动控制
// 自定义打印样式确保Canvas适配纸张、无多余边距、强制单页
style: `
@page {
size: ${paperWidthMm}mm ${paperHeightMm}mm;
margin: 2mm !important; /* 留出页边距,避免紧贴边框 */
padding: 0 !important;
}
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
html, body {
margin: 0 !important;
padding: 0 !important;
width: ${paperWidthMm}mm !important;
height: ${paperHeightMm}mm !important;
overflow: hidden !important;
}
#${printContainerId} {
width: calc(${paperWidthMm}mm - 4mm) !important; /* 两侧各2mm的页边距后可用宽度 */
height: calc(${paperHeightMm}mm - 4mm) !important; /* 上下各2mm的页边距后可用高度 */
margin: 0 !important;
padding: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
#${printContainerId} canvas {
width: 100% !important;
height: 100% !important;
max-width: calc(${paperWidthMm}mm - 4mm) !important;
max-height: calc(${paperHeightMm}mm - 4mm) !important;
object-fit: contain !important;
border: none !important;
}
@media print {
body * { visibility: hidden !important; }
#${printContainerId}, #${printContainerId} * { visibility: visible !important; }
#${printContainerId} {
position: absolute !important;
top: 0 !important;
left: 0 !important;
page-break-inside: avoid !important;
break-inside: avoid !important;
page-break-after: avoid !important;
break-after: avoid !important;
page-break-before: avoid !important;
break-before: avoid !important;
orphans: 999 !important;
widows: 999 !important;
}
}
`,
printContainer: true,
onAfterPrint: () => {
// 8. 打印完成后清理临时容器
if (document.body.contains(tempPrintContainer)) {
document.body.removeChild(tempPrintContainer);
}
Message.success('打印完成');
},
onError: (error) => {
// 异常时也清理临时容器
if (document.body.contains(tempPrintContainer)) {
document.body.removeChild(tempPrintContainer);
}
console.error('打印失败:', error);
Message.error('打印失败,请重试');
}
});
} catch (error) {
console.error('打印准备失败:', error);
Message.error('打印内容准备失败,请重试');
}
},
// -------- 辅助方法:等待所有资源加载(图片+字体+二维码) --------
async waitForAllResources(element) {
// 等待图片加载
const images = element.querySelectorAll('img');
const imgPromises = Array.from(images).map(img =>
img.complete ? Promise.resolve() : new Promise(resolve => {
img.onload = resolve;
img.onerror = resolve;
})
);
await Promise.all(imgPromises);
// 等待字体加载
await document.fonts.ready;
// 等待二维码Canvas渲染
await this.waitForQRCodeRender(element);
// 最终等待样式稳定
await new Promise(resolve => setTimeout(resolve, 300));
},
// -------- 辅助方法等待二维码Canvas渲染完成 --------
async waitForQRCodeRender(element) {
const qrCanvasList = element.querySelectorAll('canvas');
if (qrCanvasList.length === 0) return;
const qrLoadPromises = Array.from(qrCanvasList).map(canvas => {
return new Promise(resolve => {
const checkInterval = setInterval(() => {
const ctx = canvas.getContext('2d');
if (!ctx) {
clearInterval(checkInterval);
resolve();
return;
}
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const hasContent = imageData.data.some(value => value !== 0);
if (hasContent || Date.now() - (ctx.startTime || 0) > 2000) {
clearInterval(checkInterval);
resolve();
}
}, 100);
});
});
await Promise.all(qrLoadPromises);
}
},
};
</script>
<style scoped>
.label-render-container {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
/* padding: 1rem; */
box-sizing: border-box;
}
.action-buttons {
margin-top: 1rem;
}
.preview-container {
background-color: #fff;
color: #000;
/* padding: 1.5rem; */
width: fit-content;
/* min-width: 200px; */
box-shadow: 0 0 60px rgba(255, 255, 255, 0.2);
/* border: 1px solid rgba(255, 255, 255, 0.1); */
overflow: hidden;
transition: all 0.3s ease;
}
.preview-container:hover {
box-shadow: 0 0 80px rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
/* 强制二维码Canvas可见避免渲染丢失 */
:deep(canvas) {
display: block;
visibility: visible;
opacity: 1;
}
/* 字体平滑,提升文字清晰度 */
:deep(.preview-container) {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-smooth: always;
}
</style>