9 天以前 10dd6590cea8f20eff21025ee71c8a614a48cdb7
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 {
        // 解析失败按原始输入处理
      }
    }
    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: "托盘码(正式托盘码)",
        span: 2,
      },
      {
        field: "tempCode",
        label: "临时托盘码(未绑定批次)",
        span: 2,
      },
      {
@@ -419,7 +469,12 @@
    schema: [
      {
        field: "code",
        label: "批次号(二维码内容)",
        label: "批次号(正式批次号)",
        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>