From 10dd6590cea8f20eff21025ee71c8a614a48cdb7 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 01 九月 2026 13:58:23 +0800
Subject: [PATCH] feat(mes): 追溯功能新增消费者扫码分析和窜货预警模块

---
 src/views/mes/pro/components/qr-trace-modal.vue |   89 ++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 80 insertions(+), 9 deletions(-)

diff --git a/src/views/mes/pro/components/qr-trace-modal.vue b/src/views/mes/pro/components/qr-trace-modal.vue
index 710a2e7..7834a3f 100644
--- a/src/views/mes/pro/components/qr-trace-modal.vue
+++ b/src/views/mes/pro/components/qr-trace-modal.vue
@@ -3,7 +3,7 @@
   import type { MesProBatchApi } from "#/api/mes/pro/batch";
   import type { MesProPalletApi } from "#/api/mes/pro/pallet";
 
-  import { ref } from "vue";
+  import { computed, ref } from "vue";
 
   import { Barcode, useVbenModal } from "@vben/common-ui";
   import { DICT_TYPE } from "@vben/constants";
@@ -14,6 +14,7 @@
   import { getBagCodeByCode } from "#/api/mes/pro/bagCode";
   import { getBatchByCode } from "#/api/mes/pro/batch";
   import { getPalletByCode } from "#/api/mes/pro/pallet";
+  import { getQrConfig } from "#/api/mes/trace/config";
   import { useDescription } from "#/components/description";
 
   defineOptions({ name: "MesProQrTraceModal" });
@@ -35,13 +36,55 @@
 
   const traceTypeLabel = ref("");
 
-  /** 璇嗗埆浜岀淮鐮佸唴瀹圭被鍨嬶細鎵规鍙� / 鎵樼洏鐮� / 琚嬬爜 */
+  /** 婧簮 H5 椤甸潰鍩虹鍦板潃锛堜細璇濆唴鍙媺鍙栦竴娆★級 */
+  const traceBaseUrl = ref<string>("");
+
+  async function ensureTraceBaseUrl() {
+    if (traceBaseUrl.value) return;
+    try {
+      const res = await getQrConfig();
+      traceBaseUrl.value = res.traceBaseUrl || "";
+    } catch {
+      traceBaseUrl.value = "";
+    }
+  }
+
+  /** 浜岀淮鐮佸睍绀哄唴瀹癸細閰嶇疆浜嗘函婧愬湴鍧�鍒欐嫾 H5 URL锛屽惁鍒欏洖閫�绾爜鍊硷紙涓庡悗绔� buildQrCodeContent 涓�鑷达級 */
+  const qrContent = computed(() => {
+    if (!currentCode.value) return "";
+    return traceBaseUrl.value
+      ? `${traceBaseUrl.value}?code=${encodeURIComponent(currentCode.value)}`
+      : currentCode.value;
+  });
+
+  /** 浠庡畬鏁� H5 閾炬帴涓彁鍙栬拷婧爜锛堝吋瀹规壂鐮佹灙鎵弿鎵撳嵃鏍囩閾炬帴锛� */
+  function extractCodeFromUrl(raw: string): string {
+    const trimmed = raw.trim();
+    if (/^https?:\/\//i.test(trimmed)) {
+      try {
+        const url = new URL(trimmed);
+        const code = url.searchParams.get("code");
+        if (code) return code;
+      } catch {
+        // 瑙f瀽澶辫触鎸夊師濮嬭緭鍏ュ鐞�
+      }
+    }
+    return trimmed;
+  }
+
+  /** 璇嗗埆浜岀淮鐮佸唴瀹圭被鍨嬶細鎵规鍙凤紙鍚复鏃舵壒娆$爜锛�/ 鎵樼洏鐮侊紙鍚复鏃舵墭鐩樼爜锛�/ 琚嬬爜 */
   function resolveCodeType(raw: string): TraceType | undefined {
     const value = raw.trim().toUpperCase();
+    if (/^PB-\d{8}$/.test(value)) {
+      return "batch";
+    }
+    if (/^PL-\d{8}$/.test(value)) {
+      return "pallet";
+    }
     if (/^.*-P\d{2}$/.test(value)) {
       return "pallet";
     }
-    if (/^.*-\d{4}$/.test(value)) {
+    if (/^BAG-\d{8}$/.test(value) || /^.*-\d{4}$/.test(value)) {
       return "bag";
     }
     return "batch";
@@ -60,7 +103,8 @@
 
   /** 鎵爜/杈撳叆浜岀淮鐮佸唴瀹规煡璇㈣拷婧俊鎭� */
   async function handleSearch(raw?: string) {
-    const value = (raw ?? codeInput.value)?.trim().toUpperCase();
+    const input = (raw ?? codeInput.value)?.trim() ?? "";
+    const value = extractCodeFromUrl(input).toUpperCase();
     if (!value) {
       message.warning("璇锋壂鎻忔垨杈撳叆浜岀淮鐮佸唴瀹�");
       return;
@@ -125,7 +169,7 @@
     if (traceType.value === "pallet" && palletTrace.value) {
       const d = palletTrace.value;
       return [
-        { label: "鎵樼洏鐮�", value: d.code || "" },
+        { label: "鎵樼洏鐮�", value: d.code || d.tempCode || "" },
         { label: "鎵樼洏搴忓彿", value: d.palletNo ? String(d.palletNo) : "" },
         { label: "鎵规鍙�", value: d.batchCode || "" },
         { label: "鍝佺", value: d.itemName || "" },
@@ -141,7 +185,7 @@
     if (batchTrace.value) {
       const d = batchTrace.value;
       return [
-        { label: "鎵规鍙�", value: d.code || "" },
+        { label: "鎵规鍙�", value: d.code || d.tempCode || "" },
         { label: "鍝佺", value: d.itemName || "" },
         { label: "瑙勬牸", value: d.itemSpecification || "" },
         {
@@ -259,6 +303,7 @@
       if (!isOpen) {
         return;
       }
+      ensureTraceBaseUrl();
       const data = modalApi.getData<{ code?: string }>() || {};
       codeInput.value = data.code ?? undefined;
       resetState();
@@ -357,7 +402,12 @@
     schema: [
       {
         field: "code",
-        label: "鎵樼洏鐮侊紙浜岀淮鐮佸唴瀹癸級",
+        label: "鎵樼洏鐮侊紙姝e紡鎵樼洏鐮侊級",
+        span: 2,
+      },
+      {
+        field: "tempCode",
+        label: "涓存椂鎵樼洏鐮侊紙鏈粦瀹氭壒娆★級",
         span: 2,
       },
       {
@@ -419,7 +469,12 @@
     schema: [
       {
         field: "code",
-        label: "鎵规鍙凤紙浜岀淮鐮佸唴瀹癸級",
+        label: "鎵规鍙凤紙姝e紡鎵规鍙凤級",
+        span: 2,
+      },
+      {
+        field: "tempCode",
+        label: "涓存椂鎵规鐮侊紙寰呰ˉ褰曪級",
         span: 2,
       },
       {
@@ -496,12 +551,28 @@
             class="mb-4 flex items-center gap-6 rounded bg-gray-100 p-5"
           >
             <div class="flex-shrink-0">
-              <Barcode ref="barcodeRef" :content="currentCode" :width="200" />
+              <Barcode ref="barcodeRef" :content="qrContent" :width="200" />
             </div>
             <div class="min-w-0 flex-1">
               <p class="text-lg font-bold break-all">{{ currentCode }}</p>
               <p class="mt-1 text-xs text-gray-500">
                 鐮佺被鍨嬶細{{ traceTypeLabel }}
+                <Tag
+                  v-if="traceType === 'batch' && batchTrace && !batchTrace.code"
+                  color="warning"
+                  class="ml-2"
+                >
+                  寰呰ˉ褰�
+                </Tag>
+                <Tag
+                  v-else-if="
+                    traceType === 'pallet' && palletTrace && !palletTrace.code
+                  "
+                  color="warning"
+                  class="ml-2"
+                >
+                  鏈粦瀹氭壒娆�
+                </Tag>
               </p>
             </div>
           </div>

--
Gitblit v1.9.3