diff --git a/src/pages/product/ProductStockDetail.vue b/src/pages/product/ProductStockDetail.vue index 655b355..c0c5a32 100644 --- a/src/pages/product/ProductStockDetail.vue +++ b/src/pages/product/ProductStockDetail.vue @@ -55,8 +55,8 @@ - - + + { } async function openRecordList(): Promise { - if (product.value?.id) { + if (product.value?.id && sku.value?.id) { await router.push({ name: "ProductStockRecordList", query: { productId: product.value.id, + skuId: sku.value.id, transitionDepth: String(getCurrentRouteDepth() + 1) } }); @@ -211,11 +212,12 @@ async function openRecordList(): Promise { } async function openReservedRecordList(): Promise { - if (product.value?.id) { + if (product.value?.id && sku.value?.id) { await router.push({ name: "ProductStockRecordList", query: { productId: product.value.id, + skuId: sku.value.id, operationType: "ORDER_RESERVE", activeReservationOnly: "true", transitionDepth: String(getCurrentRouteDepth() + 1) diff --git a/src/pages/product/ProductStockRecordList.vue b/src/pages/product/ProductStockRecordList.vue index 8d11fc2..057a416 100644 --- a/src/pages/product/ProductStockRecordList.vue +++ b/src/pages/product/ProductStockRecordList.vue @@ -20,70 +20,103 @@ @refresh="refresh" @scrolltolower="loadMore" > - - - + + + +
+
+
@@ -94,9 +127,9 @@ import { PassTimeLabel } from "timi-web"; import { UserAPI } from "timi-web-api"; import ProductAPI from "@/api/ProductAPI"; import { useNavBarStore } from "@/store/navBarStore"; -import type { StockOperationType, StockRecord, StockRecordDirection } from "@/types/Product"; +import type { Product, ProductSku, StockOperationType, StockRecord, StockRecordDirection } from "@/types/Product"; import { getErrorMessage } from "@/utils/error"; -import { getProductQuantityText, getStockOperationText } from "@/utils/product"; +import { getProductPhotoURL, getProductQuantityText, getProductTypeText, getStockOperationText } from "@/utils/product"; import { Message } from "@/utils/message"; import { permissionCodes } from "@/utils/permission"; import { getCustomerPhotoURL } from "@/utils/customer"; @@ -118,6 +151,12 @@ const loading = ref(false); const isRefreshing = ref(false); const noMore = ref(false); const productId = computed(() => getQueryText("productId")); +const skuId = computed(() => getQueryText("skuId")); +const filteredProduct = ref(); +const filteredSku = ref(); +const filteredSkuUnitId = ref(""); +const filteredSkuUnitList = computed(() => (filteredSku.value?.unitList || []).filter(unit => !!unit.id)); +const filteredSkuPhotoURL = computed(() => filteredSku.value ? getProductPhotoURL(filteredSku.value) || (filteredProduct.value ? getProductPhotoURL(filteredProduct.value) : "") : ""); const activeReservationRequested = getQueryText("activeReservationOnly") === "true"; const operationType = ref(getQueryText("operationType") as OperationFilter || (activeReservationRequested ? "ORDER_RESERVE" : "ALL")); const direction = ref(getQueryText("direction") as DirectionFilter || "ALL"); @@ -132,7 +171,7 @@ const emptyText = computed(() => { if (activeReservationOnly.value) { return "暂无有效的预占流水"; } - return keyword.value.trim() || productId.value || operationType.value !== "ALL" || direction.value !== "ALL" ? "未找到匹配的库存流水" : "暂无库存流水"; + return keyword.value.trim() || productId.value || skuId.value || operationType.value !== "ALL" || direction.value !== "ALL" ? "未找到匹配的库存流水" : "暂无库存流水"; }); const loadStateText = computed(() => loading.value ? "正在加载" : noMore.value ? "已经到底了" : "上拉加载更多"); const operationOptions = [ @@ -159,6 +198,10 @@ function getQueryText(key: string): string { return Array.isArray(value) ? String(value[0] || "") : String(value || ""); } +function getDefaultFilteredSkuUnitId(): string { + return filteredSkuUnitList.value.find(unit => unit.isBase)?.id || filteredSkuUnitList.value[0]?.id || ""; +} + function getOperationIcon(type?: StockOperationType): string { return ({ INITIAL_IN: "import", @@ -235,6 +278,7 @@ async function loadMore(force = false): Promise { try { const result = await ProductAPI.recordList({ productId: productId.value || undefined, + skuId: skuId.value || undefined, keyword: keyword.value.trim() || undefined, operationType: operationType.value === "ALL" ? undefined : operationType.value, activeReservationOnly: activeReservationOnly.value || undefined, @@ -257,6 +301,23 @@ async function loadMore(force = false): Promise { } } +async function loadSkuFilter(): Promise { + if (!skuId.value) { + filteredProduct.value = undefined; + filteredSku.value = undefined; + filteredSkuUnitId.value = ""; + return; + } + const sku = await ProductAPI.skuDetail(skuId.value); + const product = sku.productId ? await ProductAPI.detail(sku.productId) : undefined; + if (skuId.value !== getQueryText("skuId")) { + return; + } + filteredSku.value = sku; + filteredProduct.value = product; + filteredSkuUnitId.value = getDefaultFilteredSkuUnitId(); +} + async function selectOperationType(value: DropdownValue): Promise { if (!Array.isArray(value)) { operationType.value = String(value) as OperationFilter; @@ -292,7 +353,7 @@ async function refresh(): Promise { onMounted(async () => { navBarStore.clearRight(); try { - await reload(); + await Promise.all([loadSkuFilter(), reload()]); } catch (error) { Message.error(getErrorMessage(error, "库存流水加载失败")); } @@ -372,83 +433,113 @@ onUnmounted(() => { } } - .list { - margin: 0; + .list-wrap { + gap: 1rem; + display: flex; + flex-direction: column; + + .sku { + --td-tag-square-border-radius: .5rem; - .item { padding: 10px; - min-width: 0; :deep(.t-cell__title) { - min-width: 0; - width: 0; - } - - :deep(.t-cell__title-text) { - min-width: 0; - width: 100%; + display: flex; + align-items: center; } .avatar { - width: 3.5rem; - height: 3.5rem; - } - - .icon { - width: 2.25rem; - height: 2.25rem; - display: inline-flex; - background: var(--td-bg-color-secondarycontainer); - align-items: center; - border-radius: 50%; - justify-content: center; + width: 2rem; + height: 2rem; + border-radius: .5rem !important; } .title { gap: .5rem; + display: flex; min-width: 0; - max-width: 100%; - flex: 1; - display: flex; - font-size: .75rem; align-items: center; - white-space: nowrap; - - .actor { - color: var(--td-text-color-secondary); - flex: 0 0 auto; - } - - .operation { - flex: 0 0 auto; - } - - .product-name { - min-width: 0; - flex: 0 1 auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - .quantity { - color: var(--td-text-color-secondary); - flex: 0 0 auto; - font-weight: 400; - } } + } - .description { - gap: .5rem; - display: flex; - font-size: .75rem; - align-items: center; - justify-content: space-between; + .list { + margin: 0; - .delta { + .item { + padding: 10px; + min-width: 0; - .value { - font-size: 1.25rem; + :deep(.t-cell__title) { + min-width: 0; + width: 0; + } + + :deep(.t-cell__title-text) { + min-width: 0; + width: 100%; + } + + .avatar { + width: 3.5rem; + height: 3.5rem; + } + + .icon { + width: 2.25rem; + height: 2.25rem; + display: inline-flex; + background: var(--td-bg-color-secondarycontainer); + align-items: center; + border-radius: 50%; + justify-content: center; + } + + .title { + gap: .5rem; + min-width: 0; + max-width: 100%; + flex: 1; + display: flex; + font-size: .75rem; + align-items: center; + white-space: nowrap; + + .actor { + color: var(--td-text-color-secondary); + flex: 0 0 auto; + } + + .operation { + flex: 0 0 auto; + } + + .product-name { + min-width: 0; + flex: 0 1 auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .quantity { + color: var(--td-text-color-secondary); + flex: 0 0 auto; + font-weight: 400; + } + } + + .description { + gap: .5rem; + display: flex; + font-size: .75rem; + align-items: center; + justify-content: space-between; + + .delta { + + .value { + font-size: 1.25rem; + } } } } diff --git a/src/types/Product.ts b/src/types/Product.ts index 6439c2f..9d7a711 100644 --- a/src/types/Product.ts +++ b/src/types/Product.ts @@ -192,6 +192,7 @@ export type StockRecord = { export type StockRecordPage = { storeId?: string; productId?: string; + skuId?: string; keyword?: string; bizType?: StockBizType; bizId?: string;