feat(标签打印): 优化标签打印功能并新增批量导出
- 新增html2canvas依赖以支持高清打印 - 重构标签打印逻辑,解决二维码丢失和文字模糊问题 - 优化外标签样式布局和公司名称显示 - 新增标签预览功能,可在物料列表中直接查看 - 实现批量导出功能,支持任务规划和进度展示 - 添加导出配置选项,可调整清晰度和单次导出数量
This commit is contained in:
@@ -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