From 563f14617f4b5ee57e8d550bf3c1e34103263106 Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 18 四月 2026 15:18:03 +0800
Subject: [PATCH] 优化扫码出库功能,增强前端页面交互体验,更新API接口以支持更灵活的出库状态处理

---
 src/pages/inventoryManagement/scanOut/scanOut.logic.ts  |   86 ++++++++++++++++++++++++++++
 src/pages/inventoryManagement/scanOut/scanOut.submit.ts |   56 ++++++++++++++++++
 2 files changed, 142 insertions(+), 0 deletions(-)

diff --git a/src/pages/inventoryManagement/scanOut/scanOut.logic.ts b/src/pages/inventoryManagement/scanOut/scanOut.logic.ts
new file mode 100644
index 0000000..05213fa
--- /dev/null
+++ b/src/pages/inventoryManagement/scanOut/scanOut.logic.ts
@@ -0,0 +1,86 @@
+import {
+  CONTRACT_KIND,
+  QUALITY_TYPE,
+} from "./scanOut.constants";
+import type { ContractKind, QualityType } from "./scanOut.constants";
+
+type AnyRow = Record<string, any>;
+
+export function parseOptionalNumber(raw: unknown): number | null {
+  if (raw === null || raw === undefined || raw === "") return null;
+  const n = Number(String(raw).trim());
+  return Number.isNaN(n) ? null : n;
+}
+
+export function parseRemainingQuantity(row: AnyRow): number | null {
+  const remRaw =
+    row?.remainingQuantity ??
+    row?.remaining_quantity ??
+    row?.remainQuantity ??
+    row?.remain_quantity;
+  return parseOptionalNumber(remRaw);
+}
+
+export function defaultStockedQuantityFromRow(row: AnyRow): string {
+  const rem = parseRemainingQuantity(row);
+  if (rem !== null) return String(Math.max(0, rem));
+
+  const avail = parseOptionalNumber(row?.availableQuality ?? row?.availableQuantity);
+  if (avail !== null) return String(Math.max(0, avail));
+
+  const qty = parseOptionalNumber(row?.quantity);
+  if (qty !== null) return String(Math.max(0, qty));
+
+  return "0";
+}
+
+/** 鏍规嵁浜岀淮鐮� JSON 鍒ゆ柇閿�鍞�(XS)/閲囪喘(CG) */
+export function resolveQrContractKind(scanData: AnyRow): ContractKind {
+  const t = scanData?.type;
+  const ts =
+    t !== null && t !== undefined && t !== "" ? String(t).trim().toUpperCase() : "";
+
+  if (ts === "CG" || t === 2 || t === "2") return CONTRACT_KIND.purchase;
+  if (ts === "XS" || t === 1 || t === "1") return CONTRACT_KIND.sales;
+
+  const pc = scanData?.purchaseContractNumber;
+  const sc = scanData?.salesContractNo;
+  if (pc != null && String(pc).trim() !== "" && (sc == null || String(sc).trim() === ""))
+    return CONTRACT_KIND.purchase;
+
+  return CONTRACT_KIND.sales;
+}
+
+export function resolveListTypeForDetail(contractKind: ContractKind): 1 | 2 {
+  return contractKind === CONTRACT_KIND.purchase ? 2 : 1;
+}
+
+export function resolveContractNo(scanData: AnyRow, contractKind: ContractKind): string {
+  const rawNo =
+    contractKind === CONTRACT_KIND.purchase
+      ? scanData.purchaseContractNumber
+      : scanData.salesContractNo;
+  return rawNo != null && String(rawNo).trim() !== "" ? String(rawNo).trim() : "";
+}
+
+export function buildSalesLedgerProductList(recordList: AnyRow[]): AnyRow[] {
+  return recordList.map((item: AnyRow) => {
+    const n = parseOptionalNumber(item.stockedQuantity);
+    const qty = n !== null && !Number.isNaN(n) ? Math.max(0, n) : 0;
+    const { stockedQuantity: _sq, ...rest } = item;
+    return { ...rest, stockedQuantity: qty };
+  });
+}
+
+export function hasAnyPositiveStockedQty(salesLedgerProductList: AnyRow[]): boolean {
+  return salesLedgerProductList.some((p: AnyRow) => Number(p.stockedQuantity) > 0);
+}
+
+export function resolveSubmitSceneKey(contractKind: ContractKind, qualityType: QualityType): string {
+  return `${contractKind}-${qualityType}`;
+}
+
+export function isQualified(qualityType: QualityType): boolean {
+  return qualityType === QUALITY_TYPE.qualified;
+}
+
diff --git a/src/pages/inventoryManagement/scanOut/scanOut.submit.ts b/src/pages/inventoryManagement/scanOut/scanOut.submit.ts
new file mode 100644
index 0000000..208fd58
--- /dev/null
+++ b/src/pages/inventoryManagement/scanOut/scanOut.submit.ts
@@ -0,0 +1,56 @@
+import {
+  CONTRACT_KIND,
+  QUALITY_TYPE,
+} from "./scanOut.constants";
+import type { ContractKind, QualityType } from "./scanOut.constants";
+import {
+  scanOutboundSales,
+  scanOutboundSalesUnqualified,
+} from "@/api/salesManagement/salesLedger";
+import {
+  scanOutboundPurchase,
+  scanOutboundPurchaseUnqualified,
+} from "@/api/procurementManagement/procurementLedger";
+
+type AnyRow = Record<string, any>;
+type AnyRef<T> = { value: T };
+
+type SubmitConfig = {
+  runApi: (data: any) => Promise<any>;
+  payloadBuilder: (list: AnyRow[]) => any;
+};
+
+export function createSubmitConfig(scanLedgerIdRef: AnyRef<string | number | null>) {
+  const cfg: Record<string, SubmitConfig> = {
+    [`${CONTRACT_KIND.sales}-${QUALITY_TYPE.qualified}`]: {
+      runApi: scanOutboundSales,
+      payloadBuilder: (list: AnyRow[]) => ({
+        salesLedgerId: scanLedgerIdRef.value,
+        salesLedgerProductList: list,
+      }),
+    },
+    [`${CONTRACT_KIND.sales}-${QUALITY_TYPE.unqualified}`]: {
+      runApi: scanOutboundSalesUnqualified,
+      payloadBuilder: (list: AnyRow[]) => ({
+        salesLedgerId: scanLedgerIdRef.value,
+        salesLedgerProductList: list,
+      }),
+    },
+    [`${CONTRACT_KIND.purchase}-${QUALITY_TYPE.qualified}`]: {
+      runApi: scanOutboundPurchase,
+      payloadBuilder: (list: AnyRow[]) => ({
+        purchaseLedgerId: scanLedgerIdRef.value,
+        salesLedgerProductList: list,
+      }),
+    },
+    [`${CONTRACT_KIND.purchase}-${QUALITY_TYPE.unqualified}`]: {
+      runApi: scanOutboundPurchaseUnqualified,
+      payloadBuilder: (list: AnyRow[]) => ({
+        purchaseLedgerId: scanLedgerIdRef.value,
+        salesLedgerProductList: list,
+      }),
+    },
+  };
+  return cfg;
+}
+

--
Gitblit v1.9.3