add product sku
This commit is contained in:
@@ -30,8 +30,8 @@
|
||||
<t-cell
|
||||
class="item"
|
||||
v-for="item in config.itemList || []"
|
||||
:title="item.productNameSnapshot"
|
||||
:key="item.id || `${item.productId}-${item.productUnitId}`"
|
||||
:title="getItemTitle(item)"
|
||||
:key="item.id || `${item.skuId}-${item.skuUnitId}`"
|
||||
:note="`${item.defaultQuantity} ${item.unitSnapshot || ''}${item.remark ? `|${item.remark}` : ''}`"
|
||||
>
|
||||
<template #leftIcon>
|
||||
@@ -50,7 +50,7 @@
|
||||
</template>
|
||||
<template #description>
|
||||
<t-tag
|
||||
:content="getProductTypeText(item.product?.productType)"
|
||||
:content="item.skuCodeSnapshot || item.sku?.code || 'SKU'"
|
||||
theme="primary"
|
||||
variant="light"
|
||||
/>
|
||||
@@ -89,7 +89,7 @@ import { getErrorMessage } from "@/utils/error";
|
||||
import { formatPackageAmount, getPackageConfigStatusText } from "@/utils/package";
|
||||
import { Message } from "@/utils/message";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
import { getProductPhotoURL as getProductPhotoUrl, getProductTypeText } from "@/utils/product";
|
||||
import { getProductPhotoURL as getProductPhotoUrl } from "@/utils/product";
|
||||
|
||||
defineOptions({ name: "PackageConfigDetail" });
|
||||
|
||||
@@ -113,7 +113,12 @@ function getStatusTheme(status?: PackageConfigStatus): "success" | "warning" | "
|
||||
}
|
||||
|
||||
function getItemPhotoURL(item: PackageConfigItem): string {
|
||||
return getProductPhotoUrl(item.product || {});
|
||||
return getProductPhotoUrl(item.sku || {});
|
||||
}
|
||||
|
||||
function getItemTitle(item: PackageConfigItem): string {
|
||||
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
|
||||
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""}`;
|
||||
}
|
||||
|
||||
async function loadData(): Promise<void> {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
>
|
||||
<div class="header">
|
||||
<span class="no" v-text="String(index + 1).padStart(2, '0')" />
|
||||
<strong v-text="item.productNameSnapshot || '未选择产品'" />
|
||||
<strong v-text="getItemTitle(item)" />
|
||||
<t-button
|
||||
v-if="1 < form.itemList.length"
|
||||
theme="danger"
|
||||
@@ -44,26 +44,15 @@
|
||||
>删除</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="请选择产品"
|
||||
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="默认配额" content-align="right" required-mark>
|
||||
<t-stepper
|
||||
@@ -72,7 +61,7 @@
|
||||
:max="Number.MAX_SAFE_INTEGER"
|
||||
:min="getQuantityStep(item)"
|
||||
:step="getQuantityStep(item)"
|
||||
:integer="!getProduct(item)?.allowDecimalQuantity"
|
||||
:integer="!getSku(item)?.allowDecimalQuantity"
|
||||
theme="outline"
|
||||
/>
|
||||
</t-form-item>
|
||||
@@ -122,7 +111,7 @@ import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { GaoPackageConfig, PackageConfigItem, PackageConfigStatus } from "@/types/Package";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import type { Product, ProductUnit } from "@/types/Product";
|
||||
import type { Product, ProductSku, ProductSkuUnit } from "@/types/Product";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { formatPackageAmountInput, getPackageConfigStatusText, normalizePackageQuantity, parsePackageAmount } from "@/utils/package";
|
||||
import { Message } from "@/utils/message";
|
||||
@@ -178,13 +167,28 @@ function createItem(item?: Partial<PackageConfigItem>): PackageConfigFormItem {
|
||||
}
|
||||
|
||||
const productCache = new Map<string, Product>();
|
||||
const skuCache = new Map<string, ProductSku>();
|
||||
|
||||
function getProduct(item: PackageConfigFormItem): Product | undefined {
|
||||
return productCache.get(item.productId || "");
|
||||
const sku = getSku(item);
|
||||
return productCache.get(sku?.productId || "");
|
||||
}
|
||||
|
||||
function getSku(item: PackageConfigFormItem): ProductSku | undefined {
|
||||
return skuCache.get(item.skuId || "") || item.sku;
|
||||
}
|
||||
|
||||
function getUnit(item: PackageConfigFormItem): ProductSkuUnit | undefined {
|
||||
return getSku(item)?.unitList?.find(unit => unit.id === item.skuUnitId);
|
||||
}
|
||||
|
||||
function getItemTitle(item: PackageConfigFormItem): string {
|
||||
const sku = getSku(item);
|
||||
return sku?.specSummary || sku?.name || item.productNameSnapshot || "未选择产品";
|
||||
}
|
||||
|
||||
function getQuantityStep(item: PackageConfigFormItem): number {
|
||||
return getProductQuantityStep(getProduct(item));
|
||||
return getProductQuantityStep(getSku(item));
|
||||
}
|
||||
|
||||
const estimatedOriginalAmount = computed<number | undefined>(() => {
|
||||
@@ -200,45 +204,29 @@ const estimatedOriginalAmount = computed<number | undefined>(() => {
|
||||
});
|
||||
const estimatedOriginalAmountText = computed(() => estimatedOriginalAmount.value === undefined ? "待完善" : `¥${formatProductAmount(estimatedOriginalAmount.value)}`);
|
||||
|
||||
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 saleableUnits = (product.unitList || []).filter(unit => unit.allowSale !== false && unit.id);
|
||||
const productUnitId = saleableUnits.length < 2 ? saleableUnits[0]?.id || "" : value.productUnitId;
|
||||
const unit = saleableUnits.find(item => item.id === productUnitId);
|
||||
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 unit = (sku.unitList || []).find(item => item.id === value.skuUnitId);
|
||||
Object.assign(form.itemList[index], {
|
||||
productId: value.productId,
|
||||
productUnitId,
|
||||
skuId: value.skuId,
|
||||
skuUnitId: value.skuUnitId,
|
||||
productNameSnapshot: product.name,
|
||||
skuNameSnapshot: sku.name,
|
||||
specSummarySnapshot: sku.specSummary,
|
||||
unitSnapshot: unit?.name || "",
|
||||
baseQuantityPerUnitSnapshot: unit?.baseQuantityPerUnit
|
||||
});
|
||||
}
|
||||
|
||||
function getSaleableUnits(item: PackageConfigFormItem): ProductUnit[] {
|
||||
return (productCache.get(item.productId || "")?.unitList || []).filter(unit => unit.allowSale !== false && unit.id);
|
||||
}
|
||||
|
||||
function selectProductUnit(index: number, unit: ProductUnit): void {
|
||||
const item = form.itemList[index];
|
||||
const product = productCache.get(item.productId || "");
|
||||
if (!product?.id || !unit.id) {
|
||||
return;
|
||||
}
|
||||
Object.assign(item, {
|
||||
productUnitId: unit.id,
|
||||
productNameSnapshot: product.name,
|
||||
unitSnapshot: unit.name,
|
||||
baseQuantityPerUnitSnapshot: unit.baseQuantityPerUnit
|
||||
});
|
||||
}
|
||||
|
||||
function calculateOriginalAmount(item: PackageConfigFormItem): number | undefined {
|
||||
const quantity = normalizePackageQuantity(item.defaultQuantity ?? "");
|
||||
const salePrice = productCache.get(item.productId || "")?.unitList?.find(unit => unit.id === item.productUnitId)?.salePrice;
|
||||
const salePrice = getUnit(item)?.salePrice;
|
||||
if (!quantity || quantity === "0" || salePrice === undefined || salePrice === null) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -331,13 +319,13 @@ function validateForm(): boolean {
|
||||
const keySet = new Set<string>();
|
||||
for (const item of form.itemList) {
|
||||
const quantity = normalizePackageQuantity(item.defaultQuantity ?? "");
|
||||
const key = `${item.productId}\u0000${item.productUnitId}`;
|
||||
if (!item.productId || !item.productUnitId || !quantity || quantity === "0") {
|
||||
Message.warning("产品单位和正数量不能为空");
|
||||
const key = `${item.skuId}\u0000${item.skuUnitId}`;
|
||||
if (!item.skuId || !item.skuUnitId || !quantity || quantity === "0") {
|
||||
Message.warning("SKU 单位和正数量不能为空");
|
||||
return false;
|
||||
}
|
||||
if (getProduct(item) && !getProduct(item)!.allowDecimalQuantity && quantity.includes(".")) {
|
||||
Message.warning("该产品不允许小数数量");
|
||||
if (getSku(item) && !getSku(item)!.allowDecimalQuantity && quantity.includes(".")) {
|
||||
Message.warning("该 SKU 不允许小数数量");
|
||||
return false;
|
||||
}
|
||||
if (keySet.has(key)) {
|
||||
@@ -371,8 +359,8 @@ async function save(): Promise<void> {
|
||||
beginAt: form.beginAt ?? null,
|
||||
endAt: form.endAt ?? null,
|
||||
itemList: form.itemList.map((item, index) => ({
|
||||
productId: item.productId!,
|
||||
productUnitId: item.productUnitId!,
|
||||
skuId: item.skuId!,
|
||||
skuUnitId: item.skuUnitId!,
|
||||
defaultQuantity: normalizePackageQuantity(item.defaultQuantity ?? "")!,
|
||||
sort: index,
|
||||
remark: item.remark?.trim() || null
|
||||
@@ -408,9 +396,13 @@ async function loadData(): Promise<void> {
|
||||
}
|
||||
const result = await PackageAPI.configDetail(routeId.value);
|
||||
const itemList = (result.itemList || []).map(createItem);
|
||||
const productIdList = [...new Set(itemList.map(item => item.productId).filter((id): id is string => !!id))];
|
||||
await Promise.all(productIdList.map(async productId => {
|
||||
productCache.set(productId, await ProductAPI.detail(productId));
|
||||
const skuIdList = [...new Set(itemList.map(item => item.skuId).filter((id): id is string => !!id))];
|
||||
await Promise.all(skuIdList.map(async skuId => {
|
||||
const sku = itemList.find(item => item.skuId === skuId)?.sku || await ProductAPI.skuDetail(skuId);
|
||||
skuCache.set(skuId, sku);
|
||||
if (sku.productId) {
|
||||
productCache.set(sku.productId, await ProductAPI.detail(sku.productId));
|
||||
}
|
||||
}));
|
||||
Object.assign(form, {
|
||||
id: result.id || routeId.value,
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<div class="products">
|
||||
<t-tag
|
||||
v-for="productItem in item.itemList || []"
|
||||
:key="productItem.id || `${productItem.productId}-${productItem.productUnitId}`"
|
||||
:key="productItem.id || `${productItem.skuId}-${productItem.skuUnitId}`"
|
||||
:content="getProductRightText(productItem)"
|
||||
theme="primary"
|
||||
variant="light"
|
||||
@@ -193,7 +193,8 @@ function getStatusTheme(statusValue?: PackageConfigStatus): "success" | "warning
|
||||
|
||||
function getProductRightText(item: PackageConfigItem): string {
|
||||
const quantity = getPackageQuantityText(item.defaultQuantity);
|
||||
return `${item.productNameSnapshot || "未命名产品"} × ${quantity}${item.unitSnapshot ? ` ${item.unitSnapshot}` : ""}`;
|
||||
const skuText = item.skuNameSnapshot || item.specSummarySnapshot;
|
||||
return `${item.productNameSnapshot || "未命名产品"}${skuText ? ` · ${skuText}` : ""} × ${quantity}${item.unitSnapshot ? ` ${item.unitSnapshot}` : ""}`;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
</div>
|
||||
<t-form-item
|
||||
v-for="(item, index) in packagePurchaseItemList"
|
||||
:key="`${index}-${item.productId}-${item.productUnitId}`"
|
||||
:key="`${index}-${item.skuId}-${item.skuUnitId}`"
|
||||
class="item"
|
||||
>
|
||||
<template #label>
|
||||
@@ -374,21 +374,21 @@ const {
|
||||
});
|
||||
|
||||
function getItemLabel(item: PackagePurchaseItem): string {
|
||||
const configItem = config.value?.itemList?.find(configItem => configItem.productId === item.productId && configItem.productUnitId === item.productUnitId);
|
||||
return configItem ? `${configItem.productNameSnapshot || "未命名产品"}` : "产品权益";
|
||||
const configItem = config.value?.itemList?.find(configItem => configItem.skuId === item.skuId && configItem.skuUnitId === item.skuUnitId);
|
||||
return configItem ? `${configItem.productNameSnapshot || "未命名产品"} · ${configItem.skuNameSnapshot || configItem.specSummarySnapshot || "默认 SKU"}` : "产品权益";
|
||||
}
|
||||
|
||||
function getItemName(item: PackagePurchaseItem): string {
|
||||
return config.value?.itemList?.find(configItem => configItem.productId === item.productId && configItem.productUnitId === item.productUnitId)?.productNameSnapshot || "未命名产品";
|
||||
return config.value?.itemList?.find(configItem => configItem.skuId === item.skuId && configItem.skuUnitId === item.skuUnitId)?.productNameSnapshot || "未命名产品";
|
||||
}
|
||||
|
||||
function getItemPhotoURL(item: PackagePurchaseItem): string {
|
||||
const configItem = config.value?.itemList?.find(configItem => configItem.productId === item.productId && configItem.productUnitId === item.productUnitId);
|
||||
return getProductPhotoURL(configItem?.product || {});
|
||||
const configItem = config.value?.itemList?.find(configItem => configItem.skuId === item.skuId && configItem.skuUnitId === item.skuUnitId);
|
||||
return getProductPhotoURL(configItem?.sku || {});
|
||||
}
|
||||
|
||||
function getItemUnit(item: PackagePurchaseItem): string {
|
||||
return config.value?.itemList?.find(configItem => configItem.productId === item.productId && configItem.productUnitId === item.productUnitId)?.unitSnapshot || "";
|
||||
return config.value?.itemList?.find(configItem => configItem.skuId === item.skuId && configItem.skuUnitId === item.skuUnitId)?.unitSnapshot || "";
|
||||
}
|
||||
|
||||
function updateItemQuantity(index: number, value: string | number): void {
|
||||
|
||||
@@ -42,8 +42,8 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
|
||||
}
|
||||
saleAmountText.value = formatPackageAmountInput(config.standardAmount);
|
||||
packagePurchaseItemList.value = (config.itemList || []).map((item, index) => ({
|
||||
productId: item.productId,
|
||||
productUnitId: item.productUnitId,
|
||||
skuId: item.skuId,
|
||||
skuUnitId: item.skuUnitId,
|
||||
totalQuantity: normalizePackageQuantity(item.defaultQuantity) || "",
|
||||
sort: item.sort ?? index,
|
||||
remark: item.remark ?? null
|
||||
@@ -79,8 +79,8 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
|
||||
purchasedAt: options.buildRegisteredAt(),
|
||||
attachmentList: options.buildAttachments(),
|
||||
itemList: packagePurchaseItemList.value.length ? packagePurchaseItemList.value.map((item, index) => ({
|
||||
productId: item.productId,
|
||||
productUnitId: item.productUnitId,
|
||||
skuId: item.skuId,
|
||||
skuUnitId: item.skuUnitId,
|
||||
totalQuantity: normalizePackageQuantity(item.totalQuantity)!,
|
||||
sort: item.sort ?? index,
|
||||
remark: item.remark ?? null
|
||||
@@ -112,8 +112,13 @@ export function usePackagePurchase(options: UsePackagePurchaseOptions) {
|
||||
}
|
||||
for (const item of packagePurchaseItemList.value) {
|
||||
const quantity = normalizePackageQuantity(item.totalQuantity);
|
||||
if (!item.productId || !item.productUnitId || !quantity || quantity === "0") {
|
||||
Message.warning("产品单位和正数量不能为空");
|
||||
if (!item.skuId || !item.skuUnitId || !quantity || quantity === "0") {
|
||||
Message.warning("SKU 单位和正数量不能为空");
|
||||
return false;
|
||||
}
|
||||
const configItem = options.config.value.itemList?.find(source => source.skuId === item.skuId && source.skuUnitId === item.skuUnitId);
|
||||
if (configItem?.sku && !configItem.sku.allowDecimalQuantity && quantity.includes(".")) {
|
||||
Message.warning("该 SKU 不允许小数数量");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user