yyb
2 天以前 1bd1061c60c286e3b2216a67090c4976c9c7b35f
出入库扫码
已修改5个文件
268 ■■■■ 文件已修改
src/pages/inventoryManagement/scanIn/index.vue 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/scanOut/index.vue 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/scanOut/scanOut.fields.ts 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/inventoryManagement/scanOut/scanOut.logic.ts 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/plugins/modal.ts 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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;
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"),
        }));
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]);
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 };
  });
}
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,
    })
  },
  /**