+
+ 已选择 {{ batchPrint.list.length }} 个钢卷。点击“生成PDF并打开”将每个标签作为一页(180mm × 100mm)。
+
+
+ 取消
+ 生成PDF并打开
+
+
+
+
+
@@ -247,12 +268,16 @@ import RawMaterialInfo from "@/components/KLPService/Renderer/RawMaterialInfo";
// 引入封装的追溯结果组件
import CoilTraceResult from "./CoilTraceResult.vue";
import LabelRender from './LabelRender/index.vue'
+import OuterTagPreview from './LabelRender/OuterTagPreview.vue'
import MaterialSelect from "@/components/KLPService/MaterialSelect";
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
import { findItemWithBom } from "@/store/modules/category";
import CoilNo from "@/components/KLPService/Renderer/CoilNo.vue";
import MemoInput from "@/components/MemoInput";
import MutiSelect from "@/components/MutiSelect";
+import html2canvas from 'html2canvas';
+import { PDFDocument } from 'pdf-lib';
+
export default {
@@ -272,6 +297,7 @@ export default {
CoilNo,
MemoInput,
MutiSelect,
+ OuterTagPreview,
},
dicts: ['product_coil_status', 'coil_material', 'coil_itemname', 'coil_manufacturer'],
props: {
@@ -407,6 +433,12 @@ export default {
data: {},
type: '2'
},
+ batchPrint: {
+ visible: false,
+ loading: false,
+ list: [],
+ },
+ __printOldTitle: document.title,
floatLayerConfig: {
columns: [
{ label: '入场钢卷号', prop: 'enterCoilNo' },
@@ -736,6 +768,136 @@ export default {
...this.queryParams
}, `materialCoil_${new Date().getTime()}.xlsx`)
},
+
+ /** 批量打印标签按钮 */
+ handleBatchPrintLabel() {
+ if (!this.ids || this.ids.length === 0) {
+ this.$message.warning('请先勾选要打印标签的钢卷');
+ return;
+ }
+
+ // 取出选中行数据,并补齐标签渲染需要的字段
+ const selectedData = this.materialCoilList
+ .filter(item => this.ids.includes(item.coilId))
+ .map(row => {
+ const item = row.itemType === 'product' ? row.product : row.rawMaterial;
+ const itemName = row.itemType === 'product' ? item?.productName || '' : item?.rawMaterialName || '';
+
+ return {
+ ...row,
+ itemName,
+ // OuterTagPreview.vue 里字段名使用了 specification/material(而列表里是 itemSpecification/itemMaterial)
+ specification: row.itemSpecification || row.specification || '',
+ material: row.itemMaterial || row.material || '',
+ updateTime: row.updateTime?.split(' ')[0] || row.updateTime || '',
+ }
+ });
+
+ this.batchPrint.list = selectedData;
+ this.batchPrint.visible = true;
+ },
+
+ /** 批量导出标签PDF:每个标签一页(180mm × 100mm) */
+ async handleBatchExportLabelPdf() {
+ if (!this.batchPrint.list || this.batchPrint.list.length === 0) {
+ this.$message.warning('没有可导出的数据');
+ return;
+ }
+
+ const container = this.$refs.batchPdfContainer;
+ if (!container) {
+ this.$message.error('PDF渲染容器未初始化');
+ return;
+ }
+
+ try {
+ this.batchPrint.loading = true;
+
+ await this.$nextTick();
+ await new Promise(resolve => setTimeout(resolve, 100));
+
+ const mmToPt = 72 / 25.4;
+ // 纸张尺寸
+ const paperWidthMm = 180;
+ const paperHeightMm = 100;
+ // 留白:左右约 4mm;上下更小一些,避免底部空得太多
+ const marginXmm = 4;
+ const marginTopMm = 2;
+ const marginBottomMm = 1;
+
+ const pageWidthPt = paperWidthMm * mmToPt;
+ const pageHeightPt = paperHeightMm * mmToPt;
+ const marginXPt = marginXmm * mmToPt;
+ const marginTopPt = marginTopMm * mmToPt;
+ const marginBottomPt = marginBottomMm * mmToPt;
+
+ const contentWidthPt = pageWidthPt - marginXPt * 2;
+ const contentHeightPt = pageHeightPt - marginTopPt - marginBottomPt;
+
+ const pdfDoc = await PDFDocument.create();
+
+ // 关键:只截取标签自身(OuterTagPreview),不要把页面菜单/弹窗带进去
+ const pageEls = container.querySelectorAll('.batch-pdf-page');
+
+ for (let i = 0; i < pageEls.length; i++) {
+ const el = pageEls[i];
+
+ // 强制用标签的mm尺寸作为截图基准,避免被外层布局影响
+ const canvas = await html2canvas(el, {
+ backgroundColor: '#ffffff',
+ scale: 2,
+ useCORS: true,
+ // 让 html2canvas 为频繁读回优化 Canvas(浏览器会提示 willReadFrequently)
+ willReadFrequently: true,
+ // 确保按元素尺寸截图
+ width: el.offsetWidth,
+ height: el.offsetHeight,
+ windowWidth: el.scrollWidth,
+ windowHeight: el.scrollHeight,
+ });
+
+ const imgDataUrl = canvas.toDataURL('image/png');
+ const pngImage = await pdfDoc.embedPng(imgDataUrl);
+
+ const page = pdfDoc.addPage([pageWidthPt, pageHeightPt]);
+
+ // 图片铺满页面(保持比例,居中)
+ const imgW = pngImage.width;
+ const imgH = pngImage.height;
+ // 按“内容区域(去掉边距)”计算缩放,让四周留下约 4mm 空白
+ const scale = Math.min(contentWidthPt / imgW, contentHeightPt / imgH);
+ const drawW = imgW * scale;
+ const drawH = imgH * scale;
+
+ // 内容区域内居中 + 外层边距
+ const x = marginXPt + (contentWidthPt - drawW) / 2;
+ const y = marginBottomPt + (contentHeightPt - drawH) / 2;
+
+ page.drawImage(pngImage, { x, y, width: drawW, height: drawH });
+ }
+
+ const pdfBytes = await pdfDoc.save();
+ const blob = new Blob([pdfBytes], { type: 'application/pdf' });
+ const url = URL.createObjectURL(blob);
+
+ // 尽量避免被浏览器拦截弹窗:优先在当前tab打开;如仍被策略限制,可改为下载
+ const win = window.open(url, '_blank');
+ if (!win) {
+ // fallback:触发下载
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `钢卷标签_${new Date().getTime()}.pdf`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ }
+ } catch (e) {
+ console.error(e);
+ this.$message.error('生成PDF失败,请重试');
+ } finally {
+ this.batchPrint.loading = false;
+ }
+ },
/** 导出选中数据操作 */
handleExport() {
// 1. 判断是否有选中数据
@@ -792,4 +954,22 @@ export default {
}
}
};
-
\ No newline at end of file
+
+
+