From 077d36ebdf699961b9e66dd21e13b509240f8f79 Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期五, 23 十月 2026 15:43:31 +0800
Subject: [PATCH] feat: 完善设备数采与标签打印功能

---
 src/views/wls/barcode/components/printer-label.vue |  200 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 195 insertions(+), 5 deletions(-)

diff --git a/src/views/wls/barcode/components/printer-label.vue b/src/views/wls/barcode/components/printer-label.vue
index 7fbeee5..84b4149 100644
--- a/src/views/wls/barcode/components/printer-label.vue
+++ b/src/views/wls/barcode/components/printer-label.vue
@@ -1,30 +1,220 @@
 <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' });
 
-withDefaults(
+const props = withDefaults(
   defineProps<{
     bizCode?: string;
     bizId?: number;
-    bizType?: string;
+    bizName?: string;
+    bizType?: number | string;
     labelText?: string;
   }>(),
   {
     bizCode: undefined,
     bizId: undefined,
+    bizName: undefined,
     bizType: undefined,
     labelText: '鏍囩鎵撳嵃',
   },
 );
 
-function handlePrint() {
-  message.warning('鏍囩鎵撳嵃鍔熻兘鏆傛湭瀹炵幇锛屾暚璇锋湡寰�');
+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('&', '&amp;')
+    .replaceAll('<', '&lt;')
+    .replaceAll('>', '&gt;')
+    .replaceAll('"', '&quot;')
+    .replaceAll("'", '&#39;');
+}
+
+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('鏃犳硶鎵撳紑鎵撳嵃绐楀彛锛岃鍏佽娴忚鍣ㄥ脊鍑虹獥鍙e悗閲嶈瘯');
+    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">姝e湪鐢熸垚鏍囩锛岃绋嶅�欌�︹��</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 type="link" @click="handlePrint">
+  <Button :loading="printing" type="link" @click="handlePrint">
     {{ labelText }}
   </Button>
 </template>

--
Gitblit v1.9.3