From 443d0a619056d39594ced989622a45753d7d4edc Mon Sep 17 00:00:00 2001 From: Timi Date: Wed, 16 Sep 2026 14:53:15 +0800 Subject: [PATCH] hidden sku code --- src/components/ProductSkuSnapshotTags.vue | 1 - .../fulfillment/OrderFulfillmentCreate.vue | 131 +++++++++++++----- 2 files changed, 93 insertions(+), 39 deletions(-) diff --git a/src/components/ProductSkuSnapshotTags.vue b/src/components/ProductSkuSnapshotTags.vue index 27f8f31..6c7aea5 100644 --- a/src/components/ProductSkuSnapshotTags.vue +++ b/src/components/ProductSkuSnapshotTags.vue @@ -8,7 +8,6 @@ variant="light-outline" :content="tag.content" /> - diff --git a/src/pages/fulfillment/OrderFulfillmentCreate.vue b/src/pages/fulfillment/OrderFulfillmentCreate.vue index cd8fde7..d4cff57 100644 --- a/src/pages/fulfillment/OrderFulfillmentCreate.vue +++ b/src/pages/fulfillment/OrderFulfillmentCreate.vue @@ -26,10 +26,7 @@
- +
+
+
-
-
-
全部履约 + :disabled="getAvailableBaseQuantity(item) === '0' || (directVerify && isStockChecking)" + @click.stop="fillAllQuantity(item)" + >全部履约
@@ -100,10 +90,13 @@ /> - - + + + +
+ 直接核销会立即扣减实物库存 + +
@@ -135,18 +128,31 @@ import FulfillmentAPI from "@/api/FulfillmentAPI"; import ProductAPI from "@/api/ProductAPI"; import PhotoScroller from "@/components/PhotoScroller.vue"; import ProductSkuSnapshotTags from "@/components/ProductSkuSnapshotTags.vue"; +import ProductStockPreview from "@/components/ProductStockPreview.vue"; import { useNavBarStore } from "@/store/navBarStore"; +import type { Attachment } from "timi-web-api"; import { useUserStore } from "timi-web-api"; import type { GaoOrderFulfillmentItemRequest } from "@/types/Fulfillment"; import type { GaoSalesOrderItem, SalesOrder } from "@/types/SalesOrder"; import type { ProductSku, ProductSkuUnit } from "@/types/Product"; import type { PhotoItem } from "@/types/Photo"; -import type { Attachment } from "timi-web-api"; -import { comparePackageQuantity, convertPackageQuantity, createPackageRequestNo, getPackageQuantityText, normalizePackageQuantity, toPackageBaseQuantity } from "@/utils/package"; +import { + comparePackageQuantity, + convertPackageQuantity, + createPackageRequestNo, + getPackageQuantityText, + normalizePackageQuantity, + toPackageBaseQuantity +} from "@/utils/package"; import { getErrorMessage } from "@/utils/error"; import { Message } from "@/utils/message"; import { gaoRoleCodes, permissionCodes } from "@/utils/permission"; -import { getProductStockShortageMessage, mergeProductStockRequirements } from "@/utils/productStock"; +import type { ProductStockPreviewItem } from "@/utils/productStock"; +import { + buildProductStockPreview, + getProductStockShortageMessage, + mergeProductStockRequirements +} from "@/utils/productStock"; import { uploadPhotoList } from "@/utils/photo"; import { getProductPhotoURL } from "@/utils/product"; @@ -170,6 +176,9 @@ const unitPickerVisible = ref(false); const unitPickerSourceItemId = ref(""); const unitPickerDraft = ref>([]); const directVerify = ref(false); +const isStockChecking = ref(false); +const stockPreviewList = ref([]); +let stockCheckToken = 0; const photoUrlList = computed(() => photoItems.value.map(item => item.url)); const isPackage = computed(() => order.value?.orderType === "PACKAGE"); const expectedItemType = computed(() => isPackage.value ? "PACKAGE_ENTITLEMENT" : "DIRECT_PRODUCT"); @@ -185,6 +194,25 @@ const canDirectVerify = computed(() => userStore.hasRoles(gaoRoleCodes.storeMana && userStore.hasPermissions(permissionCodes.orderFulfillment.verify) && userStore.hasPermissions(permissionCodes.productStock.read)); +watch(directVerify, async value => { + stockCheckToken += 1; + const token = stockCheckToken; + if (!value) { + isStockChecking.value = false; + return; + } + isStockChecking.value = true; + try { + if (!await validateDirectStock() && token === stockCheckToken && directVerify.value) { + directVerify.value = false; + } + } finally { + if (token === stockCheckToken) { + isStockChecking.value = false; + } + } +}); + async function loadData(): Promise { const salesOrderId = String(route.query.salesOrderId || ""); if (!salesOrderId) { @@ -262,7 +290,7 @@ function getFulfillmentUnitForAvailableQuantity(item: GaoSalesOrderItem, unit?: return getBaseUnit(item) || unit; } -function fillAllQuantity(item: GaoSalesOrderItem): void { +async function fillAllQuantity(item: GaoSalesOrderItem): Promise { const unit = getFulfillmentUnit(item); if (!item.id || !unit) { Message.warning("请先选择实际履约单位"); @@ -274,6 +302,9 @@ function fillAllQuantity(item: GaoSalesOrderItem): void { return; } quantityMap[item.id] = quantity; + if (directVerify.value) { + await previewDirectStock(); + } } function getFulfillmentUnitList(item: GaoSalesOrderItem): ProductSkuUnit[] { @@ -356,6 +387,7 @@ function buildAttachments(): Partial[] { } async function validateDirectStock(): Promise { + stockPreviewList.value = []; const selected = sourceItems.value.filter(item => !!item.id && quantityMap[item.id]); const skuMap = new Map(); for (const item of selected) { @@ -385,13 +417,14 @@ async function validateDirectStock(): Promise { if (!item.skuId || !sku?.stockEnabled || !baseQuantity) { return []; } - return [{ skuId: item.skuId, baseQuantity, productName: item.productNameSnapshot || "未命名产品", unitName: "基础单位" }]; + return [{ skuId: item.skuId, baseQuantity, productName: item.productNameSnapshot || "未命名产品", unitName: "基础单位", stockEnabled: sku.stockEnabled }]; })); if (!requirementList.length) { return true; } try { const stockList = await ProductAPI.stockBatchDetail({ skuIdList: requirementList.map(item => item.skuId) }); + stockPreviewList.value = buildProductStockPreview(stockList, requirementList); const shortageMessage = getProductStockShortageMessage(stockList, requirementList); if (shortageMessage) { Message.warning(`${shortageMessage},不能直接核销履约`); @@ -399,11 +432,30 @@ async function validateDirectStock(): Promise { } return true; } catch (error) { + stockPreviewList.value = []; Message.error(getErrorMessage(error, "库存查询失败,暂不能直接核销履约")); return false; } } +async function previewDirectStock(): Promise { + if (!directVerify.value || isStockChecking.value) { + return; + } + stockCheckToken += 1; + const token = stockCheckToken; + isStockChecking.value = true; + try { + if (!await validateDirectStock() && token === stockCheckToken && directVerify.value) { + directVerify.value = false; + } + } finally { + if (token === stockCheckToken) { + isStockChecking.value = false; + } + } +} + function validate(): boolean { if (!order.value?.id) { Message.warning("请从订单详情进入履约创建"); @@ -447,7 +499,12 @@ function validate(): boolean { } async function save(): Promise { - if (isSaving.value || !validate()) return; + if (isSaving.value) return; + if (isStockChecking.value) { + Message.warning("库存正在预检,请稍候再提交"); + return; + } + if (!validate()) return; if (!requestNo.value) requestNo.value = createPackageRequestNo(); isSaving.value = true; try { @@ -552,10 +609,10 @@ onUnmounted(() => navBarStore.clearRight()); min-width: 0; display: flex; overflow: hidden; + align-items: center; + justify-content: start; strong { - flex: 1 1 0; - min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -568,6 +625,12 @@ onUnmounted(() => navBarStore.clearRight()); white-space: nowrap; } } + } + + .input { + gap: .5rem; + display: flex; + margin-top: .5rem; .sku-tags { gap: .375rem; @@ -575,12 +638,6 @@ onUnmounted(() => navBarStore.clearRight()); flex-wrap: wrap; align-items: center; } - } - - .input { - gap: .5rem; - display: flex; - margin-top: .5rem; .unit { font-size: .875rem; @@ -591,10 +648,8 @@ onUnmounted(() => navBarStore.clearRight()); } } - .empty { - color: var(--td-text-color-secondary); - padding: 2rem 1rem; - text-align: center; + .tips { + font-size: .875rem; } }