hidden sku code
This commit is contained in:
@@ -26,10 +26,7 @@
|
||||
<div class="title">
|
||||
<div class="heading">
|
||||
<div class="name-gift">
|
||||
<strong
|
||||
class="clip-text"
|
||||
v-text="item.productNameSnapshot || '未命名产品'"
|
||||
/>
|
||||
<strong v-text="item.productNameSnapshot || '未命名产品'"/>
|
||||
<t-tag
|
||||
v-if="item.gifted"
|
||||
size="small"
|
||||
@@ -39,17 +36,12 @@
|
||||
</div>
|
||||
<span class="can-fulfill" v-text="getItemDescription(item)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<div
|
||||
v-if="item.skuCodeSnapshot || item.skuSpecSnapshot?.length || item.skuNameSnapshot || item.specSummarySnapshot || item.unitSnapshot"
|
||||
class="sku-tags"
|
||||
>
|
||||
<t-tag
|
||||
v-if="item.skuCodeSnapshot"
|
||||
size="small"
|
||||
theme="primary"
|
||||
variant="light"
|
||||
:content="item.skuCodeSnapshot"
|
||||
/>
|
||||
<product-sku-snapshot-tags
|
||||
:product-name="item.productNameSnapshot"
|
||||
:snapshot="item.skuSpecSnapshot"
|
||||
@@ -57,8 +49,6 @@
|
||||
:unit="item.unitSnapshot"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input">
|
||||
<t-input
|
||||
v-model.trim="quantityMap[item.id || '']"
|
||||
class="quantity"
|
||||
@@ -80,9 +70,9 @@
|
||||
<t-button
|
||||
size="extra-small"
|
||||
variant="outline"
|
||||
:disabled="getAvailableBaseQuantity(item) === '0'"
|
||||
@click.stop="fillAllQuantity(item)"
|
||||
>全部履约</t-button>
|
||||
:disabled="getAvailableBaseQuantity(item) === '0' || (directVerify && isStockChecking)"
|
||||
@click.stop="fillAllQuantity(item)"
|
||||
>全部履约</t-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,10 +90,13 @@
|
||||
/>
|
||||
</t-form-item>
|
||||
<t-form-item v-if="canDirectVerify" label="直接核销履约" content-align="right">
|
||||
<t-switch v-model="directVerify" size="small" :disabled="isSaving" />
|
||||
<template #help v-if="directVerify">
|
||||
<span class="red">直接核销会立即扣减实物库存;提交前会校验库存,库存不足时不能提交。</span>
|
||||
</template>
|
||||
<t-switch v-model="directVerify" size="small" :disabled="isSaving || isStockChecking" />
|
||||
</t-form-item>
|
||||
<t-form-item v-if="directVerify" label-width="0">
|
||||
<div class="stock-tips">
|
||||
<span class="tips red">直接核销会立即扣减实物库存</span>
|
||||
<product-stock-preview v-if="stockPreviewList.length" :items="stockPreviewList" />
|
||||
</div>
|
||||
</t-form-item>
|
||||
<t-form-item label="备注">
|
||||
<t-textarea v-model="remark" borderless autosize maxlength="500" />
|
||||
@@ -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<Array<string | number>>([]);
|
||||
const directVerify = ref(false);
|
||||
const isStockChecking = ref(false);
|
||||
const stockPreviewList = ref<ProductStockPreviewItem[]>([]);
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<Attachment>[] {
|
||||
}
|
||||
|
||||
async function validateDirectStock(): Promise<boolean> {
|
||||
stockPreviewList.value = [];
|
||||
const selected = sourceItems.value.filter(item => !!item.id && quantityMap[item.id]);
|
||||
const skuMap = new Map<string, ProductSku>();
|
||||
for (const item of selected) {
|
||||
@@ -385,13 +417,14 @@ async function validateDirectStock(): Promise<boolean> {
|
||||
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<boolean> {
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
stockPreviewList.value = [];
|
||||
Message.error(getErrorMessage(error, "库存查询失败,暂不能直接核销履约"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function previewDirectStock(): Promise<void> {
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user