| | |
| | | } |
| | | } |
| | | |
| | | /** 当前追溯数据的关键信息(下载/打印图片叠加文字用) */ |
| | | function buildLabelText(): { label: string; value: string }[] { |
| | | if (traceType.value === "bag" && bagTrace.value) { |
| | | const d = bagTrace.value; |
| | | return [ |
| | | { label: "袋码", value: d.code || "" }, |
| | | { label: "袋序号", value: d.bagNo ? String(d.bagNo) : "" }, |
| | | { label: "批次号", value: d.batchCode || "" }, |
| | | { label: "品种", value: d.itemName || "" }, |
| | | { label: "规格", value: d.itemSpecification || "" }, |
| | | { |
| | | label: "产线", |
| | | value: [d.lineCode, d.lineName].filter(Boolean).join(" / "), |
| | | }, |
| | | { label: "生产日期", value: d.produceDate || "" }, |
| | | { label: "责任人", value: d.ownerUserName || "" }, |
| | | ]; |
| | | } |
| | | if (traceType.value === "pallet" && palletTrace.value) { |
| | | const d = palletTrace.value; |
| | | return [ |
| | | { label: "托盘码", value: d.code || d.tempCode || "" }, |
| | | { label: "托盘序号", value: d.palletNo ? String(d.palletNo) : "" }, |
| | | { label: "批次号", value: d.batchCode || "" }, |
| | | { label: "品种", value: d.itemName || "" }, |
| | | { label: "规格", value: d.itemSpecification || "" }, |
| | | { |
| | | label: "产线", |
| | | value: [d.lineCode, d.lineName].filter(Boolean).join(" / "), |
| | | }, |
| | | { label: "生产日期", value: d.produceDate || "" }, |
| | | { label: "责任人", value: d.ownerUserName || "" }, |
| | | ]; |
| | | } |
| | | if (batchTrace.value) { |
| | | const d = batchTrace.value; |
| | | return [ |
| | | { label: "批次号", value: d.code || d.tempCode || "" }, |
| | | { label: "品种", value: d.itemName || "" }, |
| | | { label: "规格", value: d.itemSpecification || "" }, |
| | | { |
| | | label: "产线", |
| | | value: [d.lineCode, d.lineName].filter(Boolean).join(" / "), |
| | | }, |
| | | { label: "生产日期", value: d.produceDate || "" }, |
| | | { label: "计划数量", value: d.planQuantity ? String(d.planQuantity) : "" }, |
| | | { label: "责任人", value: d.ownerUserName || "" }, |
| | | ]; |
| | | } |
| | | return []; |
| | | } |
| | | |
| | | function escapeHtml(value: string) { |
| | | return value |
| | | .replaceAll("&", "&") |
| | |
| | | .replaceAll(">", ">"); |
| | | } |
| | | |
| | | /** 下载二维码 PNG(二维码 + 关键信息文字合成) */ |
| | | /** 下载二维码 PNG(二维码在上,码值在下) */ |
| | | function handleDownload() { |
| | | const base64 = barcodeRef.value?.getImageBase64(); |
| | | if (!base64) { |
| | | message.warning("二维码生成失败,无法下载"); |
| | | return; |
| | | } |
| | | const items = buildLabelText(); |
| | | const code = currentCode.value || ""; |
| | | const canvas = document.createElement("canvas"); |
| | | const ctx = canvas.getContext("2d"); |
| | | if (!ctx) { |
| | |
| | | } |
| | | const img = new Image(); |
| | | img.onload = () => { |
| | | const padding = 20; |
| | | const infoX = img.width + padding * 2; |
| | | const infoWidth = 320; |
| | | canvas.width = infoX + infoWidth + padding; |
| | | canvas.height = Math.max(img.height + padding * 2, 300); |
| | | const padding = 16; |
| | | const gap = 4; |
| | | const textHeight = 28; |
| | | canvas.width = img.width + padding * 2; |
| | | canvas.height = img.height + padding * 2 + gap + textHeight; |
| | | ctx.fillStyle = "#ffffff"; |
| | | ctx.fillRect(0, 0, canvas.width, canvas.height); |
| | | ctx.drawImage(img, padding, padding); |
| | | ctx.fillStyle = "#000000"; |
| | | let y = padding + 10; |
| | | const lineHeight = 26; |
| | | items.forEach((item) => { |
| | | ctx.font = "bold 14px 'Microsoft YaHei', Arial, sans-serif"; |
| | | ctx.fillText(`${item.label}:`, infoX, y); |
| | | y += 18; |
| | | ctx.font = "14px 'Microsoft YaHei', Arial, sans-serif"; |
| | | const text = item.value || "-"; |
| | | const maxWidth = infoWidth - 10; |
| | | if (ctx.measureText(text).width > maxWidth) { |
| | | let currentText = text; |
| | | while (currentText.length > 0) { |
| | | let line = ""; |
| | | for (let i = 0; i < currentText.length; i++) { |
| | | const testLine = line + currentText[i]; |
| | | if (ctx.measureText(testLine).width > maxWidth) { |
| | | break; |
| | | } |
| | | line = testLine; |
| | | } |
| | | ctx.fillText(line, infoX + 5, y); |
| | | y += 16; |
| | | currentText = currentText.slice(line.length); |
| | | } |
| | | y += 6; |
| | | } else { |
| | | ctx.fillText(text, infoX + 5, y); |
| | | y += lineHeight; |
| | | } |
| | | }); |
| | | let fontSize = 20; |
| | | ctx.font = `bold ${fontSize}px Arial, sans-serif`; |
| | | while (ctx.measureText(code).width > img.width && fontSize > 1) { |
| | | ctx.font = `bold ${--fontSize}px Arial, sans-serif`; |
| | | } |
| | | ctx.textAlign = "center"; |
| | | ctx.textBaseline = "middle"; |
| | | ctx.fillText(code, canvas.width / 2, padding + img.height + gap + textHeight / 2); |
| | | const link = document.createElement("a"); |
| | | link.href = canvas.toDataURL("image/png"); |
| | | link.download = `${currentCode.value || "qr"}_${Date.now()}.png`; |
| | |
| | | message.warning("二维码生成失败,无法打印"); |
| | | return; |
| | | } |
| | | const items = buildLabelText(); |
| | | const code = currentCode.value || ""; |
| | | const printWindow = window.open("", "_blank"); |
| | | if (!printWindow) { |
| | | message.error("无法打开打印窗口,请检查浏览器设置"); |
| | |
| | | } |
| | | printWindow.document.write(`<!DOCTYPE html> |
| | | <html><head><meta charset="UTF-8"><title>打印二维码</title> |
| | | <style>*{margin:0;padding:0}body{font-family:'Microsoft YaHei',Arial,sans-serif;padding:20px}.print-container{text-align:center}.qr-img{max-width:100%;margin:20px 0}.info{margin-top:20px;text-align:left;font-size:13px}.info p{margin:4px 0}@media print{body{padding:0}.print-container{padding:20px}}</style> |
| | | <style> |
| | | *{margin:0;padding:0;box-sizing:border-box} |
| | | body{font-family:Arial,sans-serif;background:#fff;color:#000} |
| | | .print-container{width:40mm;padding:2mm;break-inside:avoid;text-align:center} |
| | | .qr-img{display:block;width:36mm;height:36mm;object-fit:contain} |
| | | .code{display:block;width:36mm;height:5mm;margin-top:0.5mm} |
| | | @page{margin:0} |
| | | </style> |
| | | </head><body><div class="print-container"> |
| | | <img src="${base64}" class="qr-img" alt="二维码" /> |
| | | <div class="info"> |
| | | ${items.map((item) => `<p><strong>${escapeHtml(item.label)}:</strong> ${escapeHtml(item.value || "-")}</p>`).join("")} |
| | | </div></div></body></html>`); |
| | | <svg class="code" viewBox="0 0 200 28" xmlns="http://www.w3.org/2000/svg" aria-label="码值"> |
| | | <text x="100" y="20" text-anchor="middle" font-family="Arial,sans-serif" font-size="20" font-weight="700"${code.length > 16 ? ' textLength="196" lengthAdjust="spacingAndGlyphs"' : ''}>${escapeHtml(code)}</text> |
| | | </svg></div></body></html>`); |
| | | printWindow.document.close(); |
| | | printWindow.addEventListener("load", () => { |
| | | setTimeout(() => printWindow.print(), 500); |