From 1bd1061c60c286e3b2216a67090c4976c9c7b35f Mon Sep 17 00:00:00 2001
From: yyb <995253665@qq.com>
Date: 星期六, 18 四月 2026 17:50:29 +0800
Subject: [PATCH] 出入库扫码

---
 src/pages/inventoryManagement/scanOut/scanOut.fields.ts |   31 ++++--
 src/pages/inventoryManagement/scanOut/index.vue         |  114 ++++++++++++++++------
 src/pages/inventoryManagement/scanOut/scanOut.logic.ts  |   25 +++-
 src/pages/inventoryManagement/scanIn/index.vue          |   81 ++++++++++++++--
 src/plugins/modal.ts                                    |   17 +++
 5 files changed, 205 insertions(+), 63 deletions(-)

diff --git a/src/pages/inventoryManagement/scanIn/index.vue b/src/pages/inventoryManagement/scanIn/index.vue
index 8479acd..147a7f0 100644
--- a/src/pages/inventoryManagement/scanIn/index.vue
+++ b/src/pages/inventoryManagement/scanIn/index.vue
@@ -99,7 +99,7 @@
             <text class="kv-label">鍏ュ簱鏁伴噺</text>
             <view class="kv-value stocked-qty-input-wrap">
               <up-input :key="'stocked-' + idx"
-                        v-model="item.stockedQuantity"
+                        v-model="item.operateQuantity"
                         type="number"
                         placeholder="璇疯緭鍏ュ叆搴撴暟閲�"
                         clearable
@@ -166,7 +166,22 @@
     };
   };
 
-  const { detailFieldRows, summaryFieldRows } = useScanOutFieldRows(contractKind);
+  const { detailFieldRows: rawDetailFieldRows, summaryFieldRows: rawSummaryFieldRows } = useScanOutFieldRows(
+    contractKind,
+    "inbound"
+  );
+  const shouldShowInboundQuantityField = key => {
+    if (type.value === QUALITY_TYPE.qualified) return key !== "unqualifiedStockedQuantity";
+    if (type.value === QUALITY_TYPE.unqualified)
+      return key !== "stockedQuantity" && key !== "remainingQuantity";
+    return true;
+  };
+  const detailFieldRows = computed(() =>
+    rawDetailFieldRows.value.filter(row => shouldShowInboundQuantityField(row.key))
+  );
+  const summaryFieldRows = computed(() =>
+    rawSummaryFieldRows.value.filter(row => shouldShowInboundQuantityField(row.key))
+  );
 
   const emptyDash = v => {
     if (v === null || v === undefined || v === "") return "-";
@@ -211,24 +226,33 @@
     return emptyDash(v);
   };
 
+  const shouldValidateStockStatus = computed(() => {
+    return (
+      contractKind.value === CONTRACT_KIND.sales &&
+      type.value === QUALITY_TYPE.qualified
+    );
+  });
+
   const isFullyStocked = item => {
+    if (!shouldValidateStockStatus.value) return false;
     const s = item?.productStockStatus;
     return s == 2 || s === "2";
   };
 
   const onStockedQtyBlur = item => {
     if (isFullyStocked(item)) return;
-    const raw = item.stockedQuantity;
+    const raw = item.operateQuantity;
     if (raw === null || raw === undefined || String(raw).trim() === "") {
-      item.stockedQuantity = "0";
+      item.operateQuantity = "0";
       return;
     }
     const n = Number(String(raw).trim());
     if (Number.isNaN(n)) {
-      item.stockedQuantity = defaultStockedQuantityFromRow(item);
+      item.operateQuantity =
+        type.value === QUALITY_TYPE.unqualified ? "0" : defaultStockedQuantityFromRow(item, "inbound");
       return;
     }
-    item.stockedQuantity = String(Math.max(0, n));
+    item.operateQuantity = String(Math.max(0, n));
   };
 
   const formatCell = (item, row, idx) => {
@@ -242,11 +266,35 @@
       return formatProductStockStatus(item.productStockStatus);
     if (row.key === "heavyBox") return formatHeavyBox(item.heavyBox);
     if (row.key === "remainingQuantity") {
+      const v = item.remainingQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "remainingShippedQuantity") {
+      const v = item.remainingShippedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "shippedQuantity") {
+      const v = item.shippedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "unqualifiedShippedQuantity") {
       const v =
-        item.remainingQuantity ??
-        item.remaining_quantity ??
-        item.remainQuantity ??
-        item.remain_quantity;
+        item.unqualifiedShippedQuantity ??
+        item.unQualifiedShippedQuantity ??
+        item.unqualifiedShippedQty ??
+        item.unqualifiedOutboundQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "stockedQuantity") {
+      const v = item.stockedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "unqualifiedStockedQuantity") {
+      const v =
+        item.unqualifiedStockedQuantity ??
+        item.unQualifiedStockedQuantity ??
+        item.unqualifiedStockedQty ??
+        item.unqualifiedInboundQuantity;
       return emptyDash(v);
     }
     if (row.key === "availableQuality") {
@@ -351,7 +399,18 @@
       if (res.code === 200 && res.data && res.data.length > 0) {
         recordList.value = res.data.map(row => ({
           ...row,
-          stockedQuantity: defaultStockedQuantityFromRow(row),
+          unqualifiedShippedQuantity:
+            row.unqualifiedShippedQuantity ??
+            row.unQualifiedShippedQuantity ??
+            row.unqualifiedShippedQty ??
+            row.unqualifiedOutboundQuantity,
+          unqualifiedStockedQuantity:
+            row.unqualifiedStockedQuantity ??
+            row.unQualifiedStockedQuantity ??
+            row.unqualifiedStockedQty ??
+            row.unqualifiedInboundQuantity,
+          operateQuantity:
+            type.value === QUALITY_TYPE.unqualified ? "0" : defaultStockedQuantityFromRow(row, "inbound"),
         }));
         expandedByIndex.value = {};
         showForm.value = true;
diff --git a/src/pages/inventoryManagement/scanOut/index.vue b/src/pages/inventoryManagement/scanOut/index.vue
index bcbe216..e5ef746 100644
--- a/src/pages/inventoryManagement/scanOut/index.vue
+++ b/src/pages/inventoryManagement/scanOut/index.vue
@@ -188,7 +188,7 @@
 
         </view>
 
-        <view v-if="!isFullyStocked(item)"
+        <view
 
               class="stocked-qty-block">
 
@@ -200,7 +200,7 @@
 
               <up-input :key="'stocked-' + idx"
 
-                        v-model="item.stockedQuantity"
+                        v-model="item.operateQuantity"
 
                         type="number"
 
@@ -322,7 +322,22 @@
 
 
 
-  const { detailFieldRows, summaryFieldRows } = useScanOutFieldRows(contractKind);
+  const { detailFieldRows: rawDetailFieldRows, summaryFieldRows: rawSummaryFieldRows } = useScanOutFieldRows(
+    contractKind,
+    "outbound"
+  );
+  const shouldShowOutboundQuantityField = key => {
+    if (type.value === QUALITY_TYPE.qualified)
+      return key !== "unqualifiedShippedQuantity" && key !== "unqualifiedStockedQuantity";
+    if (type.value === QUALITY_TYPE.unqualified) return key !== "shippedQuantity" && key !== "remainingShippedQuantity";
+    return true;
+  };
+  const detailFieldRows = computed(() =>
+    rawDetailFieldRows.value.filter(row => shouldShowOutboundQuantityField(row.key))
+  );
+  const summaryFieldRows = computed(() =>
+    rawSummaryFieldRows.value.filter(row => shouldShowOutboundQuantityField(row.key))
+  );
 
 
 
@@ -412,16 +427,6 @@
 
 
 
-  const isFullyStocked = item => {
-
-    const s = item?.productStockStatus;
-
-    return s == 2 || s === "2";
-
-  };
-
-
-
   const parseOptionalNumberLocal = raw => {
 
     if (raw === null || raw === undefined || raw === "") return null;
@@ -478,13 +483,11 @@
 
   const onStockedQtyBlur = item => {
 
-    if (isFullyStocked(item)) return;
-
-    const raw = item.stockedQuantity;
+    const raw = item.operateQuantity;
 
     if (raw === null || raw === undefined || String(raw).trim() === "") {
 
-      item.stockedQuantity = "0";
+      item.operateQuantity = "0";
 
       return;
 
@@ -493,14 +496,19 @@
     const n = Number(String(raw).trim());
 
     if (Number.isNaN(n)) {
-
-      item.stockedQuantity = defaultStockedQuantityFromRow(item);
+      if (type.value === QUALITY_TYPE.unqualified) {
+        const unqualifiedInbound = parseOptionalNumber(item.unqualifiedStockedQuantity) ?? 0;
+        const unqualifiedOutbound = parseOptionalNumber(item.unqualifiedShippedQuantity) ?? 0;
+        item.operateQuantity = String(Math.max(0, unqualifiedInbound - unqualifiedOutbound));
+      } else {
+        item.operateQuantity = defaultStockedQuantityFromRow(item, "outbound");
+      }
 
       return;
 
     }
 
-    item.stockedQuantity = String(Math.max(0, n));
+    item.operateQuantity = String(Math.max(0, n));
 
   };
 
@@ -528,18 +536,34 @@
 
     if (row.key === "remainingQuantity") {
 
-      const v =
-
-        item.remainingQuantity ??
-
-        item.remaining_quantity ??
-
-        item.remainQuantity ??
-
-        item.remain_quantity;
+      const v = item.remainingQuantity;
 
       return emptyDash(v);
 
+    }
+    if (row.key === "remainingShippedQuantity") {
+      const v = item.remainingShippedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "shippedQuantity") {
+      const v = item.shippedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "unqualifiedShippedQuantity") {
+      const v =
+        item.unqualifiedShippedQuantity ??
+        item.unQualifiedShippedQuantity ??
+        item.unqualifiedShippedQty ??
+        item.unqualifiedOutboundQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "stockedQuantity") {
+      const v = item.stockedQuantity;
+      return emptyDash(v);
+    }
+    if (row.key === "unqualifiedStockedQuantity") {
+      const v = item.unqualifiedStockedQuantity;
+      return emptyDash(v);
     }
 
     if (row.key === "availableQuality") {
@@ -799,8 +823,36 @@
         recordList.value = res.data.map(row => ({
 
           ...row,
-
-          stockedQuantity: defaultStockedQuantityFromRow(row),
+          unqualifiedShippedQuantity:
+            row.unqualifiedShippedQuantity ??
+            row.unQualifiedShippedQuantity ??
+            row.unqualifiedShippedQty ??
+            row.unqualifiedOutboundQuantity,
+          unqualifiedStockedQuantity:
+            row.unqualifiedStockedQuantity ??
+            row.unQualifiedStockedQuantity ??
+            row.unqualifiedStockedQty ??
+            row.unqualifiedInboundQuantity,
+          operateQuantity:
+            type.value === QUALITY_TYPE.unqualified
+              ? String(
+                  Math.max(
+                    0,
+                    (parseOptionalNumber(
+                      row.unqualifiedStockedQuantity ??
+                        row.unQualifiedStockedQuantity ??
+                        row.unqualifiedStockedQty ??
+                        row.unqualifiedInboundQuantity
+                    ) ?? 0) -
+                      (parseOptionalNumber(
+                        row.unqualifiedShippedQuantity ??
+                          row.unQualifiedShippedQuantity ??
+                          row.unqualifiedShippedQty ??
+                          row.unqualifiedOutboundQuantity
+                      ) ?? 0)
+                  )
+                )
+              : defaultStockedQuantityFromRow(row, "outbound"),
 
         }));
 
diff --git a/src/pages/inventoryManagement/scanOut/scanOut.fields.ts b/src/pages/inventoryManagement/scanOut/scanOut.fields.ts
index 3e1fd49..6aabc56 100644
--- a/src/pages/inventoryManagement/scanOut/scanOut.fields.ts
+++ b/src/pages/inventoryManagement/scanOut/scanOut.fields.ts
@@ -1,6 +1,19 @@
 import { computed, type Ref } from "vue";
 import { CONTRACT_KIND, type ContractKind } from "./scanOut.constants";
 
+const outboundQuantityRows = [
+  { label: "宸插嚭搴撴暟閲�", key: "shippedQuantity" },
+  { label: "涓嶅悎鏍煎嚭搴撴暟閲�", key: "unqualifiedShippedQuantity" },
+  { label: "涓嶅悎鏍煎叆搴撴暟閲�", key: "unqualifiedStockedQuantity" },
+  { label: "鍓╀綑鍑哄簱鏁伴噺", key: "remainingShippedQuantity" },
+] as const;
+
+const inboundQuantityRows = [
+  { label: "宸插叆搴撴暟閲�", key: "stockedQuantity" },
+  { label: "涓嶅悎鏍煎叆搴撴暟閲�", key: "unqualifiedStockedQuantity" },
+  { label: "鍓╀綑鍏ュ簱鏁伴噺", key: "remainingQuantity" },
+] as const;
+
 export const detailFieldRowsSales = [
   { label: "妤煎眰缂栧彿", key: "floorCode" },
   { label: "浜у搧澶х被", key: "productCategory" },
@@ -20,7 +33,6 @@
   { label: "鍙戣揣杞︾墝", key: "shippingCarNumber" },
   { label: "鍙戣揣鏃ユ湡", key: "shippingDate" },
   { label: "鏁伴噺", key: "quantity" },
-  { label: "鍓╀綑鏁伴噺", key: "remainingQuantity" },
   { label: "绋庣巼(%)", key: "taxRate" },
   { label: "鍚◣鍗曚环(鍏�)", key: "taxInclusiveUnitPrice" },
   { label: "鍚◣鎬讳环(鍏�)", key: "taxInclusiveTotalPrice" },
@@ -33,7 +45,6 @@
   { label: "鍗曚綅", key: "unit" },
   { label: "鏁伴噺", key: "quantity" },
   { label: "鍙敤鏁伴噺", key: "availableQuality" },
-  { label: "鍓╀綑鏁伴噺", key: "remainingQuantity" },
   { label: "閫�璐ф暟閲�", key: "returnQuality" },
   { label: "绋庣巼(%)", key: "taxRate" },
   { label: "鍚◣鍗曚环(鍏�)", key: "taxInclusiveUnitPrice" },
@@ -46,7 +57,6 @@
   { label: "浜у搧澶х被", key: "productCategory" },
   { label: "瑙勬牸鍨嬪彿", key: "specificationModel" },
   { label: "鏁伴噺", key: "quantity" },
-  { label: "鍓╀綑鏁伴噺", key: "remainingQuantity" },
   { label: "浜у搧鐘舵��", key: "approveStatus" },
   { label: "鍏ュ簱鐘舵��", key: "productStockStatus" },
 ] as const;
@@ -57,22 +67,23 @@
   { label: "鍗曚綅", key: "unit" },
   { label: "鏁伴噺", key: "quantity" },
   { label: "鍙敤鏁伴噺", key: "availableQuality" },
-  { label: "鍓╀綑鏁伴噺", key: "remainingQuantity" },
 ] as const;
 
 type FieldRow = { label: string; key: string };
+type ScanScene = "outbound" | "inbound";
 
-export function useScanOutFieldRows(contractKindRef: Ref<ContractKind>) {
+export function useScanOutFieldRows(contractKindRef: Ref<ContractKind>, scene: ScanScene = "outbound") {
+  const quantityRows = scene === "inbound" ? inboundQuantityRows : outboundQuantityRows;
   const fieldRowsByContractKind = {
     [CONTRACT_KIND.sales]: {
-      detail: detailFieldRowsSales,
-      summary: summaryFieldRowsSales,
+      detail: [...detailFieldRowsSales, ...quantityRows],
+      summary: [...summaryFieldRowsSales, ...quantityRows],
     },
     [CONTRACT_KIND.purchase]: {
-      detail: detailFieldRowsPurchase,
-      summary: summaryFieldRowsPurchase,
+      detail: [...detailFieldRowsPurchase, ...quantityRows],
+      summary: [...summaryFieldRowsPurchase, ...quantityRows],
     },
-  } as const satisfies Record<ContractKind, { detail: readonly FieldRow[]; summary: readonly FieldRow[] }>;
+  } satisfies Record<ContractKind, { detail: readonly FieldRow[]; summary: readonly FieldRow[] }>;
 
   const currentContractFieldRows = computed(() => fieldRowsByContractKind[contractKindRef.value]);
 
diff --git a/src/pages/inventoryManagement/scanOut/scanOut.logic.ts b/src/pages/inventoryManagement/scanOut/scanOut.logic.ts
index 05213fa..bd30c63 100644
--- a/src/pages/inventoryManagement/scanOut/scanOut.logic.ts
+++ b/src/pages/inventoryManagement/scanOut/scanOut.logic.ts
@@ -13,16 +13,23 @@
 }
 
 export function parseRemainingQuantity(row: AnyRow): number | null {
-  const remRaw =
-    row?.remainingQuantity ??
-    row?.remaining_quantity ??
-    row?.remainQuantity ??
-    row?.remain_quantity;
+  const remRaw = row?.remainingQuantity;
   return parseOptionalNumber(remRaw);
 }
 
-export function defaultStockedQuantityFromRow(row: AnyRow): string {
-  const rem = parseRemainingQuantity(row);
+export function parseRemainingShippedQuantity(row: AnyRow): number | null {
+  const remRaw = row?.remainingShippedQuantity;
+  return parseOptionalNumber(remRaw);
+}
+
+export function defaultStockedQuantityFromRow(
+  row: AnyRow,
+  scene: "inbound" | "outbound" = "inbound"
+): string {
+  const rem =
+    scene === "outbound"
+      ? parseRemainingShippedQuantity(row) ?? parseRemainingQuantity(row)
+      : parseRemainingQuantity(row) ?? parseRemainingShippedQuantity(row);
   if (rem !== null) return String(Math.max(0, rem));
 
   const avail = parseOptionalNumber(row?.availableQuality ?? row?.availableQuantity);
@@ -65,9 +72,9 @@
 
 export function buildSalesLedgerProductList(recordList: AnyRow[]): AnyRow[] {
   return recordList.map((item: AnyRow) => {
-    const n = parseOptionalNumber(item.stockedQuantity);
+    const n = parseOptionalNumber(item.operateQuantity);
     const qty = n !== null && !Number.isNaN(n) ? Math.max(0, n) : 0;
-    const { stockedQuantity: _sq, ...rest } = item;
+    const { operateQuantity: _oq, ...rest } = item;
     return { ...rest, stockedQuantity: qty };
   });
 }
diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts
index 4a97dd3..46d622c 100644
--- a/src/plugins/modal.ts
+++ b/src/plugins/modal.ts
@@ -14,9 +14,22 @@
    * @param content 娑堟伅鍐呭
    */
   msgError(content: string): void {
+    const text = (content ?? '').toString()
+    // showToast 鍦ㄥ绔粡甯镐細瀵� title 鍋氬崟琛�/闀垮害鎴柇锛涢暱鏂囨鐢� showModal 淇濊瘉瀹屾暣灞曠ず
+    const shouldUseModal = text.length > 16 || text.includes('\n')
+    if (shouldUseModal) {
+      uni.showModal({
+        title: '閿欒',
+        content: text,
+        showCancel: false,
+        confirmText: '鐭ラ亾浜�',
+      })
+      return
+    }
     uni.showToast({
-      title: content,
-      icon: 'error'
+      title: text,
+      icon: 'none',
+      duration: 2500,
     })
   },
   /**

--
Gitblit v1.9.3