更正前端内容

This commit is contained in:
2026-01-09 16:31:59 +08:00
parent bcfe3ff273
commit 965ff72f7b
2 changed files with 182 additions and 1 deletions

View File

@@ -61,6 +61,7 @@
"konva": "^10.0.2",
"mqtt": "^5.13.3",
"nprogress": "0.2.0",
"pdf-lib": "^1.17.1",
"pdfjs-dist": "^3.6.172",
"print-js": "^1.6.0",
"qrcode": "^1.5.4",

View File

@@ -63,6 +63,9 @@
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExportAll">导出</el-button>
</el-col>
<el-col :span="2">
<el-button type="info" plain icon="el-icon-printer" size="mini" :disabled="multiple" @click="handleBatchPrintLabel">批量打印标签</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
@@ -229,6 +232,24 @@
<el-dialog title="标签预览" :visible.sync="labelRender.visible" append-to-body>
<label-render :content="labelRender.data" :labelType="labelType" />
</el-dialog>
<!-- 批量导出标签PDF弹窗 -->
<el-dialog title="批量导出标签PDF" :visible.sync="batchPrint.visible" width="520px" append-to-body>
<div style="line-height: 22px; font-size: 12px; color: #909399; margin-bottom: 10px;">
已选择 <b>{{ batchPrint.list.length }}</b> 个钢卷点击生成PDF并打开将每个标签作为一页180mm × 100mm
</div>
<div style="text-align:right;">
<el-button size="mini" @click="batchPrint.visible = false">取消</el-button>
<el-button type="primary" size="mini" :loading="batchPrint.loading" @click="handleBatchExportLabelPdf">生成PDF并打开</el-button>
</div>
<!-- 渲染容器屏幕隐藏仅用于截图生成PDF -->
<div ref="batchPdfContainer" class="batch-pdf-root" aria-hidden="true">
<div v-for="(item, idx) in batchPrint.list" :key="item.coilId || idx" class="batch-pdf-page">
<OuterTagPreview :content="item" :paperWidthMm="180" :paperHeightMm="100" />
</div>
</div>
</el-dialog>
</div>
</template>
@@ -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 {
}
}
};
</script>
</script>
<style scoped>
/* 批量导出PDF渲染容器屏幕隐藏但保留真实布局尺寸给 html2canvas 截图 */
.batch-pdf-root {
position: fixed;
left: -100000px;
top: 0;
width: 180mm;
background: #fff;
}
.batch-pdf-page {
width: 180mm;
height: 100mm;
box-sizing: border-box;
overflow: hidden;
}
</style>