add product sku
This commit is contained in:
@@ -46,35 +46,24 @@
|
||||
<strong>购买产品</strong>
|
||||
<span v-text="`${items.length} 项`" />
|
||||
</div>
|
||||
<div class="tips">同一产品单位不能重复,数量最多三位小数</div>
|
||||
<div class="tips">同一 SKU 单位不能重复,数量最多三位小数</div>
|
||||
<div class="items">
|
||||
<div v-for="(item, index) in items" :key="item.clientKey" class="item">
|
||||
<div class="header">
|
||||
<span class="no" v-text="String(index + 1).padStart(2, '0')" />
|
||||
<strong v-text="item.productName || '未选择产品'" />
|
||||
<strong v-text="getItemTitle(item)" />
|
||||
<t-button theme="danger" variant="text" size="extra-small" @click="removeItem(index)">删除</t-button>
|
||||
</div>
|
||||
<product-selector
|
||||
:model-value="item.productId ? {productId: item.productId, productUnitId: item.productUnitId || ''} : undefined"
|
||||
:model-value="item.skuId ? { skuId: item.skuId, skuUnitId: item.skuUnitId || '' } : undefined"
|
||||
:selected-product="getProduct(item)"
|
||||
label="产品"
|
||||
placeholder="请选择产品"
|
||||
selection-mode="product"
|
||||
required-mark
|
||||
@update:model-value="applyProduct(index, $event)"
|
||||
/>
|
||||
<t-form-item v-if="item.productId" label="产品单位" required-mark>
|
||||
<div v-if="getSaleableUnits(item).length" class="unit-list">
|
||||
<t-button
|
||||
v-for="unit in getSaleableUnits(item)"
|
||||
:key="unit.id"
|
||||
:theme="item.productUnitId === unit.id ? 'primary' : 'default'"
|
||||
:variant="item.productUnitId === unit.id ? 'base' : 'outline'"
|
||||
size="extra-small"
|
||||
:content="unit.name"
|
||||
@click="selectProductUnit(index, unit)"
|
||||
/>
|
||||
</div>
|
||||
<span v-else class="unit-empty" v-text="'暂无可售单位'" />
|
||||
<t-form-item v-if="item.skuId" label="SKU 单位" content-align="right">
|
||||
<span v-text="getUnit(item)?.name || '未选择单位'" />
|
||||
</t-form-item>
|
||||
<t-form-item :label="canPriceOverride ? '成交单价' : '单价'">
|
||||
<t-input
|
||||
@@ -93,7 +82,7 @@
|
||||
:max="maxQuantity"
|
||||
:min="getQuantityStep(item)"
|
||||
:step="getQuantityStep(item)"
|
||||
:integer="!getProduct(item)?.allowDecimalQuantity"
|
||||
:integer="!getSku(item)?.allowDecimalQuantity"
|
||||
theme="outline"
|
||||
/>
|
||||
</t-form-item>
|
||||
@@ -181,7 +170,7 @@ import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
|
||||
import CustomerSelector from "@/components/CustomerSelector.vue";
|
||||
import PhotoScroller from "@/components/PhotoScroller.vue";
|
||||
import type { Customer } from "@/types/Customer";
|
||||
import type { Product, ProductUnit } from "@/types/Product";
|
||||
import type { Product, ProductSku, ProductSkuUnit } from "@/types/Product";
|
||||
import type { PhotoItem } from "@/types/Photo";
|
||||
import type { DirectProductOrderItemRequest } from "@/types/SalesOrder";
|
||||
import { createPackageRequestNo, normalizePackageQuantity, parsePackageAmount } from "@/utils/package";
|
||||
@@ -213,6 +202,7 @@ const photoItems = ref<PhotoItem[]>([]);
|
||||
const photoUrlList = computed(() => photoItems.value.map(item => item.url));
|
||||
const maxQuantity = Number.MAX_SAFE_INTEGER;
|
||||
const productCache = new Map<string, Product>();
|
||||
const skuCache = new Map<string, ProductSku>();
|
||||
const requestNo = ref("");
|
||||
const customerDisplayText = computed(() => customer.value?.name || customer.value?.code || "选择客户");
|
||||
const customerCode = ref("");
|
||||
@@ -221,12 +211,27 @@ const scannerVisible = ref(false);
|
||||
const scanStatus = ref("点击扫码开始识别。");
|
||||
let latestDecodedText = "";
|
||||
|
||||
function getProduct(item: FormItem): Product | undefined {
|
||||
return productCache.get(item.productId || "");
|
||||
function getSku(item: FormItem): ProductSku | undefined {
|
||||
return skuCache.get(item.skuId || "");
|
||||
}
|
||||
|
||||
function getQuantityStep(item: FormItem): number {
|
||||
return getProductQuantityStep(getProduct(item));
|
||||
return getProductQuantityStep(getSku(item));
|
||||
}
|
||||
|
||||
function getProduct(item: FormItem): Product | undefined {
|
||||
const sku = getSku(item);
|
||||
return productCache.get(sku?.productId || "");
|
||||
}
|
||||
|
||||
function getUnit(item: FormItem): ProductSkuUnit | undefined {
|
||||
return getSku(item)?.unitList?.find(unit => unit.id === item.skuUnitId);
|
||||
}
|
||||
|
||||
function getItemTitle(item: FormItem): string {
|
||||
const product = getProduct(item);
|
||||
const sku = getSku(item);
|
||||
return sku?.specSummary || sku?.name || product?.name || item.productName || "未选择产品";
|
||||
}
|
||||
|
||||
const totalAmount = computed(() => {
|
||||
@@ -264,45 +269,30 @@ function removeItem(index: number): void {
|
||||
}
|
||||
}
|
||||
|
||||
async function applyProduct(index: number, value?: { productId: string; productUnitId: string }): Promise<void> {
|
||||
async function applyProduct(index: number, value?: { skuId: string; skuUnitId: string }): Promise<void> {
|
||||
if (!value) return;
|
||||
const product = productCache.get(value.productId) || await ProductAPI.detail(value.productId);
|
||||
productCache.set(value.productId, product);
|
||||
const sku = skuCache.get(value.skuId) || await ProductAPI.skuDetail(value.skuId);
|
||||
skuCache.set(value.skuId, sku);
|
||||
const product = productCache.get(sku.productId || "") || await ProductAPI.detail(sku.productId || "");
|
||||
productCache.set(sku.productId || "", product);
|
||||
const item = items.value[index];
|
||||
const saleableUnits = (product.unitList || []).filter(unit => unit.allowSale !== false && unit.id);
|
||||
const productUnitId = saleableUnits.length < 2 ? saleableUnits[0]?.id || "" : value.productUnitId;
|
||||
const selectionChanged = item.productId !== value.productId || item.productUnitId !== productUnitId;
|
||||
item.productId = value.productId;
|
||||
item.productUnitId = productUnitId;
|
||||
const selectionChanged = item.skuId !== value.skuId || item.skuUnitId !== value.skuUnitId;
|
||||
item.skuId = value.skuId;
|
||||
item.skuUnitId = value.skuUnitId;
|
||||
item.productName = product.name;
|
||||
if (selectionChanged) {
|
||||
item.unitPriceText = "";
|
||||
}
|
||||
}
|
||||
function getSaleableUnits(item: FormItem): ProductUnit[] {
|
||||
return (productCache.get(item.productId || "")?.unitList || []).filter(unit => unit.allowSale !== false && unit.id);
|
||||
}
|
||||
function selectProductUnit(index: number, unit: ProductUnit): void {
|
||||
const item = items.value[index];
|
||||
const product = productCache.get(item.productId || "");
|
||||
if (!product?.id || !unit.id) {
|
||||
return;
|
||||
}
|
||||
if (item.productUnitId !== unit.id) {
|
||||
item.unitPriceText = "";
|
||||
}
|
||||
item.productUnitId = unit.id;
|
||||
item.productName = product.name;
|
||||
}
|
||||
function getItemUnitPrice(item: FormItem): number | undefined {
|
||||
if (canPriceOverride.value && item.unitPriceText.trim()) {
|
||||
return parsePackageAmount(item.unitPriceText);
|
||||
}
|
||||
const salePrice = productCache.get(item.productId || "")?.unitList?.find(unit => unit.id === item.productUnitId)?.salePrice;
|
||||
const salePrice = getUnit(item)?.salePrice;
|
||||
return salePrice === null ? undefined : salePrice;
|
||||
}
|
||||
function getItemUnitPricePlaceholder(item: FormItem): string {
|
||||
const salePrice = productCache.get(item.productId || "")?.unitList?.find(unit => unit.id === item.productUnitId)?.salePrice;
|
||||
const salePrice = getUnit(item)?.salePrice;
|
||||
return salePrice === undefined || salePrice === null ? "产品价" : `产品价:${formatProductAmount(salePrice)}`;
|
||||
}
|
||||
function calculateItemAmount(item: FormItem): number | undefined {
|
||||
@@ -388,9 +378,9 @@ function validate(): boolean {
|
||||
const keys = new Set<string>();
|
||||
for (const item of items.value) {
|
||||
const quantity = normalizePackageQuantity(item.quantity ?? "");
|
||||
const key = `${item.productId}\u0000${item.productUnitId}`;
|
||||
if (!item.productId || !item.productUnitId || !quantity || quantity === "0") { Message.warning("产品单位和正数量不能为空"); return false; }
|
||||
if (getProduct(item) && !getProduct(item)!.allowDecimalQuantity && quantity.includes(".")) { Message.warning("该产品不允许小数数量"); return false; }
|
||||
const key = `${item.skuId}\u0000${item.skuUnitId}`;
|
||||
if (!item.skuId || !item.skuUnitId || !quantity || quantity === "0") { Message.warning("SKU 单位和正数量不能为空"); return false; }
|
||||
if (getSku(item) && !getSku(item)!.allowDecimalQuantity && quantity.includes(".")) { Message.warning("该 SKU 不允许小数数量"); return false; }
|
||||
if (keys.has(key)) { Message.warning("产品单位不能重复"); return false; }
|
||||
keys.add(key);
|
||||
if (canPriceOverride.value && item.unitPriceText && parsePackageAmount(item.unitPriceText) === undefined) { Message.warning("成交单价必须是合法金额"); return false; }
|
||||
@@ -403,8 +393,8 @@ async function save(): Promise<void> {
|
||||
isSaving.value = true;
|
||||
try {
|
||||
const itemList = items.value.map((item, index) => ({
|
||||
productId: item.productId!,
|
||||
productUnitId: item.productUnitId!,
|
||||
skuId: item.skuId!,
|
||||
skuUnitId: item.skuUnitId!,
|
||||
quantity: normalizePackageQuantity(item.quantity ?? "")!,
|
||||
unitPrice: canPriceOverride.value && item.unitPriceText ? parsePackageAmount(item.unitPriceText) : undefined,
|
||||
sort: index,
|
||||
|
||||
Reference in New Issue
Block a user