add product sku

This commit is contained in:
Timi
2026-09-07 12:59:27 +08:00
parent 9841723177
commit 1d70aebd83
49 changed files with 3700 additions and 756 deletions
+22 -17
View File
@@ -1,9 +1,10 @@
<template>
<div v-can="permissionCodes.productStock.read" class="product-stock-detail">
<t-loading v-if="isLoading" class="loading" size="32px" />
<template v-else-if="product && stock">
<template v-else-if="product && sku && stock">
<product-profile :product="product" v-entrance-transition>
<template #tags>
<t-tag :content="sku?.specSummary || sku?.name || '默认 SKU'" variant="light-outline" theme="primary" />
<t-tag :content="getStockStatusText(stock)" variant="light-outline" :theme="getStockTheme(stock)" />
</template>
</product-profile>
@@ -18,29 +19,29 @@
<span class="label">库存</span>
<div class="value">
<strong class="text" v-text="getProductQuantityText(stock.onHandQuantity)" />
<span class="unit" v-text="getProductBaseUnitName(product) || '基础单位'" />
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
</div>
</div>
<div class="footer">
<div class="available">
<span class="label">可用</span>
<strong class="value" v-text="getProductQuantityText(stock.availableQuantity)" />
<span class="unit" v-text="getProductBaseUnitName(product) || '基础单位'" />
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
</div>
<div class="reserved">
<span class="label">预占</span>
<strong class="value" v-text="getProductQuantityText(stock.reservedQuantity)" />
<span class="unit" v-text="getProductBaseUnitName(product) || '基础单位'" />
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
</div>
</div>
</div>
</template>
</t-cell>
<t-cell v-if="0 < Number(stock.reservedQuantity || 0)" title="查看预占流水" arrow @click="openReservedRecordList" />
<t-cell title="查看该产品流水" arrow @click="openRecordList" />
<t-cell title="查看该产品 SKU 流水" arrow @click="openRecordList" />
</t-cell-group>
<card-container
v-if="canOperate && product.stockEnabled"
v-if="canOperate && sku?.status === 'ACTIVE' && sku.stockEnabled"
class="actions-wrap"
title="库存操作"
description="所有操作必须选择业务单位、上传照片并填写备注"
@@ -65,9 +66,10 @@
</template>
<t-empty v-else description="库存或产品不存在" />
<product-stock-operation-popup
v-if="product && stock"
v-if="product && sku && stock"
v-model:visible="operationVisible"
:product="product"
:sku="sku"
:stock="stock"
:operation="operationType"
@success="loadAll"
@@ -81,7 +83,7 @@ import ProductAPI from "@/api/ProductAPI";
import ProductProfile from "@/pages/product/component/ProductProfile.vue";
import ProductStockOperationPopup from "@/pages/product/ProductStockOperationPopup.vue";
import { useNavBarStore } from "@/store/navBarStore";
import type { Product, ProductStock, StockOperationType } from "@/types/Product";
import type { Product, ProductSku, ProductStock, StockOperationType } from "@/types/Product";
import { getErrorMessage } from "@/utils/error";
import { getProductBaseUnitName, getProductQuantityText } from "@/utils/product";
import { Message } from "@/utils/message";
@@ -96,6 +98,7 @@ const router = useRouter();
const navBarStore = useNavBarStore();
const userStore = useUserStore();
const product = ref<Product>();
const sku = ref<ProductSku>();
const stock = ref<ProductStock>();
const isLoading = ref(false);
const operationVisible = ref(false);
@@ -109,36 +112,38 @@ const operationList: Array<{ text: string; icon: string; type: ManualStockOperat
];
function getStockStatusText(item: ProductStock): string {
if (!item.product?.stockEnabled) {
if (!item.sku?.stockEnabled) {
return "不管理库存";
}
const available = Number(item.availableQuantity || 0);
const min = Number(item.product.minStockQuantity || 0);
const min = Number(item.sku.minStockQuantity || 0);
return 0 < min && available < min ? "库存不足" : "库存正常";
}
function getStockTheme(item: ProductStock): "danger" | "success" | "default" {
if (!item.product?.stockEnabled) {
if (!item.sku?.stockEnabled) {
return "default";
}
const available = Number(item.availableQuantity || 0);
const min = Number(item.product.minStockQuantity || 0);
const min = Number(item.sku.minStockQuantity || 0);
return 0 < min && available < min ? "danger" : "success";
}
async function loadAll(): Promise<void> {
const productId = String(route.params.productId || "");
if (!productId) {
const skuId = String(route.params.skuId || "");
if (!skuId) {
return;
}
isLoading.value = true;
try {
const skuResult = await ProductAPI.skuDetail(skuId);
const [productResult, stockResult] = await Promise.all([
ProductAPI.detail(productId),
ProductAPI.stockDetail(productId)
ProductAPI.detail(skuResult.productId || ""),
ProductAPI.stockDetail(skuId)
]);
product.value = productResult;
stock.value = { ...stockResult, product: productResult };
sku.value = skuResult;
stock.value = { ...stockResult, sku: skuResult };
} finally {
isLoading.value = false;
}