feat(标签打印): 优化标签打印功能并新增批量导出
- 新增html2canvas依赖以支持高清打印 - 重构标签打印逻辑,解决二维码丢失和文字模糊问题 - 优化外标签样式布局和公司名称显示 - 新增标签预览功能,可在物料列表中直接查看 - 实现批量导出功能,支持任务规划和进度展示 - 添加导出配置选项,可调整清晰度和单次导出数量
This commit is contained in:
@@ -4,9 +4,10 @@
|
||||
<div class="company-header">
|
||||
<img :src="logo" alt="Company Logo" class="company-logo" />
|
||||
<div class="company-name">
|
||||
嘉祥科伦普重工有限公司<br />
|
||||
<span class="english-name">Jiaxiang KLP Heavy Industry Co., Ltd.</span>
|
||||
科伦普<br />
|
||||
<span class="english-name">KE LUN PU</span>
|
||||
</div>
|
||||
<div class="title">嘉祥科伦普重工有限公司</div>
|
||||
</div>
|
||||
|
||||
<!-- 核心信息网格布局:替代原表格 -->
|
||||
@@ -102,7 +103,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="contact-timestamp">
|
||||
<QRCode :content="content.qrcodeRecordId || ''" :size="10" />
|
||||
<QRCode :content="content.qrcodeRecordId || ''" :size="60" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -161,11 +162,11 @@ export default {
|
||||
<style scoped>
|
||||
.label-container {
|
||||
width: 45em;
|
||||
height: 25em;
|
||||
height: 26em;
|
||||
padding: 0.5em;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Arial', sans-serif;
|
||||
border: 1px solid #000;
|
||||
/* border: 1px solid #000; */
|
||||
}
|
||||
|
||||
.company-header {
|
||||
@@ -175,17 +176,24 @@ export default {
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
width: 2.8em;
|
||||
height: 2.8em;
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.company-name {
|
||||
font-size: 0.95em;
|
||||
width: 100%;
|
||||
font-size: 1em;
|
||||
font-weight: bold;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.title {
|
||||
flex: 1;
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.english-name {
|
||||
width: 100%;
|
||||
font-size: 0.6em;
|
||||
@@ -211,8 +219,8 @@ export default {
|
||||
|
||||
.info-grid-item {
|
||||
border: 1px solid #000;
|
||||
padding: 0.25em;
|
||||
font-size: 0.78em;
|
||||
padding: 0.1em;
|
||||
font-size: 1em;
|
||||
height: 2.1em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
@@ -220,10 +228,11 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.label-cell {
|
||||
font-weight: bold;
|
||||
/* font-weight: bold; */
|
||||
}
|
||||
|
||||
/* 生产日期跨3列 */
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<tr>
|
||||
<td class="label-cell" colspan="1">料卷号</td>
|
||||
<td class="value-cell" colspan="2">
|
||||
<input type="text" class="nob" :value="content.enterCoilNo || ''" />
|
||||
<input type="text" class="nob" style="line-height: 95px;" :value="content.enterCoilNo || ''" />
|
||||
</td>
|
||||
<td class="value-cell" colspan="1">
|
||||
<QRCode :content="content.qrcodeRecordId || ' '" :size="5" />
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<div class="label-render-container">
|
||||
<!-- 操作按钮 -->
|
||||
|
||||
<!-- 标签预览容器 -->
|
||||
<div class="preview-container" id="label-preview-container" ref="labelRef">
|
||||
<ProductionTagPreview v-if="labelType === '2'" :content="content" />
|
||||
@@ -20,7 +18,8 @@
|
||||
<script>
|
||||
import domToImage from 'dom-to-image';
|
||||
import printJS from 'print-js';
|
||||
import { Message } from 'element-ui'; // 若使用Element UI,引入消息提示
|
||||
import html2canvas from 'html2canvas'; // 新增:引入高清渲染库
|
||||
import { Message } from 'element-ui';
|
||||
|
||||
import ProductionTagPreview from './ProductionTagPreview.vue';
|
||||
import OuterTagPreview from './OuterTagPreview.vue';
|
||||
@@ -48,9 +47,8 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// -------- 图片下载方法 --------
|
||||
// -------- 图片下载方法(保留,可按需替换为html2canvas) --------
|
||||
async downloadLabelAsImage() {
|
||||
// 1. 获取要转换的DOM容器
|
||||
const labelContainer = document.getElementById('label-preview-container');
|
||||
if (!labelContainer) {
|
||||
Message.error('未找到标签容器,无法下载');
|
||||
@@ -58,24 +56,25 @@ export default {
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. 用dom-to-image生成PNG图片的DataURL
|
||||
// 可选:也替换为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);
|
||||
|
||||
// 3. 创建临时a标签,触发下载
|
||||
|
||||
const downloadLink = document.createElement('a');
|
||||
downloadLink.href = dataUrl;
|
||||
downloadLink.download = `标签_${new Date().getTime()}.png`; // 自定义文件名(带时间戳防重复)
|
||||
downloadLink.download = `标签_${new Date().getTime()}.png`;
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink); // 下载后移除临时标签
|
||||
document.body.removeChild(downloadLink);
|
||||
} catch (error) {
|
||||
console.error('标签图片下载失败:', error);
|
||||
Message.error('标签图片下载失败,请重试');
|
||||
}
|
||||
},
|
||||
|
||||
// 打印方法
|
||||
printLabel() {
|
||||
// -------- 重构后的打印方法(核心优化) --------
|
||||
async printLabel() {
|
||||
// 1. 获取标签容器DOM
|
||||
const labelContainer = document.getElementById('label-preview-container');
|
||||
if (!labelContainer) {
|
||||
@@ -83,21 +82,137 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 调用print-js打印
|
||||
printJS({
|
||||
printable: 'label-preview-container', // 要打印的元素ID
|
||||
type: 'html', // 打印类型为HTML
|
||||
header: null, // 不显示页眉
|
||||
footer: null, // 不显示页脚
|
||||
// style: printStyles, // 注入打印样式
|
||||
scanStyles: true, // 禁用自动扫描页面样式(避免冲突)
|
||||
targetStyles: ['*'], // 允许所有样式生效
|
||||
printContainer: true, // 打印指定容器
|
||||
onError: (error) => { // 错误处理
|
||||
console.error('打印失败:', error);
|
||||
Message.error('打印失败,请重试');
|
||||
}
|
||||
try {
|
||||
Message.info('正在准备打印内容,请稍等...');
|
||||
|
||||
// 2. 等待二维码/字体等资源加载完成(复用之前的等待逻辑)
|
||||
await this.waitForAllResources(labelContainer);
|
||||
|
||||
// 3. 用html2canvas生成高清Canvas(解决文字模糊+二维码丢失)
|
||||
const canvas = await html2canvas(labelContainer, {
|
||||
scale: 3, // 3倍高清渲染(核心)
|
||||
backgroundColor: '#ffffff', // 强制白色背景,避免打印时背景透明
|
||||
useCORS: true, // 支持跨域图片
|
||||
allowTaint: true, // 允许渲染canvas(二维码)
|
||||
taintTest: false, // 关闭canvas污染检测
|
||||
windowWidth: labelContainer.offsetWidth * 3, // 适配缩放后的宽度
|
||||
windowHeight: labelContainer.offsetHeight * 3, // 适配缩放后的高度
|
||||
logging: false,
|
||||
});
|
||||
|
||||
// 4. 创建临时打印容器(避免影响原DOM)
|
||||
const printContainerId = 'temp-print-container-' + new Date().getTime();
|
||||
const tempPrintContainer = document.createElement('div');
|
||||
tempPrintContainer.id = printContainerId;
|
||||
// 样式:让打印容器居中、适配纸张
|
||||
tempPrintContainer.style.cssText = `
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
`;
|
||||
// 将高清Canvas插入临时容器
|
||||
tempPrintContainer.appendChild(canvas);
|
||||
// document.body.appendChild(tempPrintContainer);
|
||||
|
||||
// 5. 调用printJS打印高清Canvas(而非原HTML)
|
||||
printJS({
|
||||
printable: tempPrintContainer, // 打印临时容器
|
||||
type: 'html',
|
||||
header: null,
|
||||
footer: null,
|
||||
scanStyles: false, // 禁用自动扫描样式,手动控制
|
||||
// 自定义打印样式:确保Canvas适配纸张、无多余边距
|
||||
style: `
|
||||
#${printContainerId} {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#${printContainerId} canvas {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border: none;
|
||||
}
|
||||
@media print {
|
||||
body * { visibility: hidden; }
|
||||
#${printContainerId}, #${printContainerId} * { visibility: visible; }
|
||||
#${printContainerId} { position: absolute; top: 0; left: 0; }
|
||||
@page { margin: 10mm; } /* 打印纸张边距 */
|
||||
}
|
||||
`,
|
||||
printContainer: true,
|
||||
onAfterPrint: () => {
|
||||
// 6. 打印完成后清理临时容器
|
||||
document.body.removeChild(tempPrintContainer);
|
||||
Message.success('打印准备完成,请在打印预览中确认打印');
|
||||
},
|
||||
onError: (error) => {
|
||||
// 异常时也清理临时容器
|
||||
document.body.removeChild(tempPrintContainer);
|
||||
console.error('打印失败:', error);
|
||||
Message.error('打印失败,请重试');
|
||||
}
|
||||
});
|
||||
|
||||
// document.removeChild(tempPrintContainer);
|
||||
} 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);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -108,7 +223,6 @@ export default {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* 按钮与预览垂直排列 */
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
@@ -117,7 +231,6 @@ export default {
|
||||
|
||||
.action-buttons {
|
||||
margin-top: 1rem;
|
||||
/* 按钮与预览区的间距 */
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
@@ -137,4 +250,18 @@ export default {
|
||||
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>
|
||||
Reference in New Issue
Block a user