<script lang="ts" setup>
|
import type { MesWmBarcodeApi } from '#/api/mes/wm/barcode';
|
|
import { ref } from 'vue';
|
|
import { BarcodeBizTypeEnum } from '@vben/constants';
|
|
import { Button, message } from 'ant-design-vue';
|
|
import {
|
getBarcodeByBusiness,
|
renderBarcodeImage,
|
} from '#/api/mes/wm/barcode';
|
|
defineOptions({ name: 'WlsPrinterLabel' });
|
|
const props = withDefaults(
|
defineProps<{
|
bizCode?: string;
|
bizId?: number;
|
bizName?: string;
|
bizType?: number | string;
|
labelText?: string;
|
}>(),
|
{
|
bizCode: undefined,
|
bizId: undefined,
|
bizName: undefined,
|
bizType: undefined,
|
labelText: '标签打印',
|
},
|
);
|
|
const printing = ref(false);
|
|
function resolveBizType(value?: number | string) {
|
if (typeof value === 'number') {
|
return value;
|
}
|
const normalized = value?.trim().toUpperCase();
|
if (!normalized) {
|
return undefined;
|
}
|
// ITEM_BATCH 是部分入库页面沿用的旧别名,实际仍对应“批次”。
|
if (normalized === 'ITEM_BATCH') {
|
return BarcodeBizTypeEnum.BATCH;
|
}
|
const numericValue = Number(normalized);
|
if (Number.isInteger(numericValue)) {
|
return numericValue;
|
}
|
const enumValue =
|
BarcodeBizTypeEnum[
|
normalized as keyof typeof BarcodeBizTypeEnum
|
];
|
return typeof enumValue === 'number' ? enumValue : undefined;
|
}
|
|
function getBizTypeLabel(bizType: number) {
|
const labels: Partial<Record<BarcodeBizTypeEnum, string>> = {
|
[BarcodeBizTypeEnum.AREA]: '库位',
|
[BarcodeBizTypeEnum.BATCH]: '批次',
|
[BarcodeBizTypeEnum.CLIENT]: '客户',
|
[BarcodeBizTypeEnum.ITEM]: '物料',
|
[BarcodeBizTypeEnum.LOCATION]: '库区',
|
[BarcodeBizTypeEnum.MACHINERY]: '设备',
|
[BarcodeBizTypeEnum.PACKAGE]: '装箱单',
|
[BarcodeBizTypeEnum.PROCARD]: '流转卡',
|
[BarcodeBizTypeEnum.SN]: 'SN 码',
|
[BarcodeBizTypeEnum.STOCK]: '库存',
|
[BarcodeBizTypeEnum.TOOL]: '工具',
|
[BarcodeBizTypeEnum.USER]: '人员',
|
[BarcodeBizTypeEnum.VENDOR]: '供应商',
|
[BarcodeBizTypeEnum.WAREHOUSE]: '仓库',
|
[BarcodeBizTypeEnum.WORKORDER]: '工单',
|
[BarcodeBizTypeEnum.WORKSHOP]: '车间',
|
[BarcodeBizTypeEnum.WORKSTATION]: '工作站',
|
};
|
return labels[bizType as BarcodeBizTypeEnum] || '业务';
|
}
|
|
function escapeHtml(value?: null | string) {
|
return (value || '')
|
.replaceAll('&', '&')
|
.replaceAll('<', '<')
|
.replaceAll('>', '>')
|
.replaceAll('"', '"')
|
.replaceAll("'", ''');
|
}
|
|
async function getBarcodeForPrint(bizType: number) {
|
const barcode = await getBarcodeByBusiness(bizType, props.bizId!);
|
if (barcode?.content) {
|
return barcode;
|
}
|
if (!props.bizCode) {
|
throw new Error('缺少业务编码,无法打印标签');
|
}
|
|
// 历史批次可能未配置条码规则。此时使用业务编码即时生成 CODE128,
|
// 只用于本次打印,不写入条码表,避免“条码配置不存在”阻断业务。
|
return {
|
bizCode: props.bizCode,
|
bizId: props.bizId,
|
bizName: props.bizName || props.bizCode,
|
bizType,
|
content: props.bizCode,
|
} satisfies MesWmBarcodeApi.Barcode;
|
}
|
|
function writePrintDocument(
|
printWindow: Window,
|
barcode: MesWmBarcodeApi.Barcode,
|
imageBase64: string,
|
bizType: number,
|
) {
|
const typeLabel = getBizTypeLabel(bizType);
|
const isQrCode = barcode.format === 1;
|
printWindow.document.open();
|
printWindow.document.write(`<!doctype html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8" />
|
<title>${escapeHtml(typeLabel)}标签</title>
|
<style>
|
@page { size: 80mm 55mm; margin: 0; }
|
* { box-sizing: border-box; }
|
html, body { width: 80mm; height: 55mm; margin: 0; color: #000; font-family: "Microsoft YaHei", Arial, sans-serif; }
|
.label { width: 80mm; height: 55mm; padding: 3mm; border: .35mm solid #000; display: grid; grid-template-columns: ${isQrCode ? '31mm 1fr' : '1fr'}; gap: 3mm; overflow: hidden; }
|
.barcode { display: flex; align-items: center; justify-content: center; min-width: 0; }
|
.barcode img { display: block; max-width: 100%; max-height: ${isQrCode ? '38mm' : '22mm'}; object-fit: contain; }
|
.info { min-width: 0; display: flex; flex-direction: column; justify-content: center; }
|
.title { margin-bottom: 2mm; padding-bottom: 1.5mm; border-bottom: .3mm solid #000; font-size: 15pt; font-weight: 700; text-align: center; }
|
.row { margin: .8mm 0; font-size: 9.5pt; line-height: 1.35; overflow-wrap: anywhere; }
|
.key { font-weight: 700; }
|
.content { margin-top: 1mm; font-family: Consolas, monospace; font-size: 7.5pt; text-align: center; overflow-wrap: anywhere; }
|
${isQrCode ? '' : '.label { grid-template-rows: auto 1fr; } .barcode { order: 2; } .info { order: 1; } .title { margin-bottom: 1mm; } .row { display: inline-block; margin-right: 4mm; }'}
|
@media screen { body { margin: 8px; } }
|
</style>
|
</head>
|
<body>
|
<main class="label">
|
<section class="barcode">
|
<img src="${imageBase64}" alt="${escapeHtml(typeLabel)}条码" />
|
</section>
|
<section class="info">
|
<div class="title">${escapeHtml(typeLabel)}标签</div>
|
<div class="row"><span class="key">业务编码:</span>${escapeHtml(barcode.bizCode || props.bizCode)}</div>
|
<div class="row"><span class="key">业务名称:</span>${escapeHtml(barcode.bizName || props.bizName || '-')}</div>
|
<div class="content">${escapeHtml(barcode.content)}</div>
|
</section>
|
</main>
|
<script>
|
window.addEventListener('load', function () {
|
window.focus();
|
window.onafterprint = function () { window.close(); };
|
setTimeout(function () { window.print(); }, 150);
|
});
|
<\/script>
|
</body>
|
</html>`);
|
printWindow.document.close();
|
}
|
|
async function handlePrint() {
|
if (printing.value) {
|
return;
|
}
|
const bizType = resolveBizType(props.bizType);
|
if (!props.bizId || !bizType) {
|
message.warning('缺少业务类型或业务编号,无法打印标签');
|
return;
|
}
|
|
// 在用户点击事件中同步打开窗口,避免异步请求结束后被浏览器拦截。
|
const printWindow = window.open('', '_blank', 'width=900,height=650');
|
if (!printWindow) {
|
message.error('无法打开打印窗口,请允许浏览器弹出窗口后重试');
|
return;
|
}
|
printWindow.opener = null;
|
printWindow.document.write(
|
'<!doctype html><html><head><meta charset="UTF-8"><title>生成标签</title></head><body style="font-family:Microsoft YaHei;padding:24px">正在生成标签,请稍候……</body></html>',
|
);
|
printWindow.document.close();
|
|
printing.value = true;
|
try {
|
const barcode = await getBarcodeForPrint(bizType);
|
const isQrCode = barcode.format === 1;
|
const imageBase64 = await renderBarcodeImage(
|
barcode.content!,
|
barcode.format,
|
isQrCode ? 360 : 720,
|
isQrCode ? 360 : 220,
|
);
|
if (!imageBase64?.startsWith('data:image/png;base64,')) {
|
throw new Error('服务端未返回有效的标签图片');
|
}
|
if (!printWindow.closed) {
|
writePrintDocument(printWindow, barcode, imageBase64, bizType);
|
}
|
} catch (error) {
|
if (!printWindow.closed) {
|
printWindow.close();
|
}
|
const errorMessage =
|
error instanceof Error ? error.message : '标签打印失败,请稍后重试';
|
message.error(errorMessage);
|
} finally {
|
printing.value = false;
|
}
|
}
|
</script>
|
|
<template>
|
<Button :loading="printing" type="link" @click="handlePrint">
|
{{ labelText }}
|
</Button>
|
</template>
|