add unit transfer
This commit is contained in:
@@ -9,6 +9,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</product-profile>
|
</product-profile>
|
||||||
<t-cell-group theme="card" title="库存" v-entrance-transition="70">
|
<t-cell-group theme="card" title="库存" v-entrance-transition="70">
|
||||||
|
<t-cell
|
||||||
|
v-if="1 < unitList.length"
|
||||||
|
title="显示单位"
|
||||||
|
:note="selectedUnitName"
|
||||||
|
arrow
|
||||||
|
@click="openUnitPicker"
|
||||||
|
/>
|
||||||
<t-cell class="empty-cell-title-margin">
|
<t-cell class="empty-cell-title-margin">
|
||||||
<template #title>
|
<template #title>
|
||||||
<span></span>
|
<span></span>
|
||||||
@@ -18,20 +25,20 @@
|
|||||||
<div class="main">
|
<div class="main">
|
||||||
<span class="label">库存</span>
|
<span class="label">库存</span>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<strong class="text" v-text="getProductQuantityText(stock.onHandQuantity)" />
|
<strong class="text" v-text="displayStock.onHandQuantity" />
|
||||||
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
|
<span class="unit" v-text="selectedUnitName" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="available">
|
<div class="available">
|
||||||
<span class="label">可用</span>
|
<span class="label">可用</span>
|
||||||
<strong class="value" v-text="getProductQuantityText(stock.availableQuantity)" />
|
<strong class="value" v-text="displayStock.availableQuantity" />
|
||||||
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
|
<span class="unit" v-text="selectedUnitName" />
|
||||||
</div>
|
</div>
|
||||||
<div class="reserved">
|
<div class="reserved">
|
||||||
<span class="label">预占</span>
|
<span class="label">预占</span>
|
||||||
<strong class="value" v-text="getProductQuantityText(stock.reservedQuantity)" />
|
<strong class="value" v-text="displayStock.reservedQuantity" />
|
||||||
<span class="unit" v-text="getProductBaseUnitName(sku) || '基础单位'" />
|
<span class="unit" v-text="selectedUnitName" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,22 +81,33 @@
|
|||||||
:operation="operationType"
|
:operation="operationType"
|
||||||
@success="loadAll"
|
@success="loadAll"
|
||||||
/>
|
/>
|
||||||
|
<t-popup v-model="unitPickerVisible" placement="bottom">
|
||||||
|
<t-picker
|
||||||
|
v-model="unitPickerDraft"
|
||||||
|
:columns="unitPickerOptions"
|
||||||
|
title="选择库存展示单位"
|
||||||
|
@cancel="unitPickerVisible = false"
|
||||||
|
@confirm="confirmUnit"
|
||||||
|
/>
|
||||||
|
</t-popup>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { shallowRef } from "vue";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import ProductAPI from "@/api/ProductAPI";
|
import ProductAPI from "@/api/ProductAPI";
|
||||||
import ProductProfile from "@/pages/product/component/ProductProfile.vue";
|
import ProductProfile from "@/pages/product/component/ProductProfile.vue";
|
||||||
import ProductStockOperationPopup from "@/pages/product/ProductStockOperationPopup.vue";
|
import ProductStockOperationPopup from "@/pages/product/ProductStockOperationPopup.vue";
|
||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { Product, ProductSku, ProductStock, StockOperationType } from "@/types/Product";
|
import type { Product, ProductSku, ProductStock, ProductSkuUnit, StockOperationType } from "@/types/Product";
|
||||||
import { getErrorMessage } from "@/utils/error";
|
import { getErrorMessage } from "@/utils/error";
|
||||||
import { getProductBaseUnitName, getProductQuantityText } from "@/utils/product";
|
import { convertBaseQuantityToPackageQuantity } from "@/utils/package";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
import { permissionCodes } from "@/utils/permission";
|
import { permissionCodes } from "@/utils/permission";
|
||||||
|
|
||||||
type ManualStockOperation = Extract<StockOperationType, "MANUAL_IN" | "MANUAL_OUT" | "STOCKTAKE_GAIN" | "STOCKTAKE_LOSS">;
|
type ManualStockOperation = Extract<StockOperationType, "MANUAL_IN" | "MANUAL_OUT" | "STOCKTAKE_GAIN" | "STOCKTAKE_LOSS">;
|
||||||
|
type PickerValue = string | number;
|
||||||
|
|
||||||
defineOptions({ name: "ProductStockDetail" });
|
defineOptions({ name: "ProductStockDetail" });
|
||||||
|
|
||||||
@@ -103,7 +121,21 @@ const stock = ref<ProductStock>();
|
|||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const operationVisible = ref(false);
|
const operationVisible = ref(false);
|
||||||
const operationType = ref<ManualStockOperation>("MANUAL_IN");
|
const operationType = ref<ManualStockOperation>("MANUAL_IN");
|
||||||
|
const selectedUnitId = shallowRef("");
|
||||||
|
const unitPickerVisible = shallowRef(false);
|
||||||
|
const unitPickerDraft = ref<PickerValue[]>([]);
|
||||||
const canOperate = computed(() => userStore.hasPermissions(permissionCodes.productStock.create));
|
const canOperate = computed(() => userStore.hasPermissions(permissionCodes.productStock.create));
|
||||||
|
const unitList = computed(() => (sku.value?.unitList || []).filter(unit => !!unit.id));
|
||||||
|
const selectedUnit = computed<ProductSkuUnit | undefined>(() => unitList.value.find(unit => unit.id === selectedUnitId.value)
|
||||||
|
|| unitList.value.find(unit => unit.isBase)
|
||||||
|
|| unitList.value[0]);
|
||||||
|
const selectedUnitName = computed(() => selectedUnit.value?.name || "基础单位");
|
||||||
|
const unitPickerOptions = computed(() => [unitList.value.map(unit => ({ label: unit.name || "未命名单位", value: unit.id || "" }))]);
|
||||||
|
const displayStock = computed(() => ({
|
||||||
|
onHandQuantity: getStockQuantityText(stock.value?.onHandQuantity),
|
||||||
|
availableQuantity: getStockQuantityText(stock.value?.availableQuantity),
|
||||||
|
reservedQuantity: getStockQuantityText(stock.value?.reservedQuantity)
|
||||||
|
}));
|
||||||
const operationList: Array<{ text: string; icon: string; type: ManualStockOperation; theme: "primary" | "danger" }> = [
|
const operationList: Array<{ text: string; icon: string; type: ManualStockOperation; theme: "primary" | "danger" }> = [
|
||||||
{ text: "手工入库", icon: "import", type: "MANUAL_IN", theme: "primary" },
|
{ text: "手工入库", icon: "import", type: "MANUAL_IN", theme: "primary" },
|
||||||
{ text: "手工出库", icon: "export", type: "MANUAL_OUT", theme: "danger" },
|
{ text: "手工出库", icon: "export", type: "MANUAL_OUT", theme: "danger" },
|
||||||
@@ -129,6 +161,34 @@ function getStockTheme(item: ProductStock): "danger" | "success" | "default" {
|
|||||||
return 0 < min && available < min ? "danger" : "success";
|
return 0 < min && available < min ? "danger" : "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStockQuantityText(value?: number | string): string {
|
||||||
|
return convertBaseQuantityToPackageQuantity(value ?? "0", selectedUnit.value?.baseQuantityPerUnit ?? "1") || "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDefaultUnit(nextSku: ProductSku): void {
|
||||||
|
const nextUnitList = nextSku.unitList || [];
|
||||||
|
if (selectedUnitId.value && nextUnitList.some(unit => unit.id === selectedUnitId.value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectedUnitId.value = nextUnitList.find(unit => unit.isBase)?.id || nextUnitList[0]?.id || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function openUnitPicker(): void {
|
||||||
|
if (1 >= unitList.value.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unitPickerDraft.value = [selectedUnit.value?.id || ""];
|
||||||
|
unitPickerVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmUnit(value: PickerValue[]): void {
|
||||||
|
const unitId = String(value[0] || "");
|
||||||
|
if (unitList.value.some(unit => unit.id === unitId)) {
|
||||||
|
selectedUnitId.value = unitId;
|
||||||
|
}
|
||||||
|
unitPickerVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadAll(): Promise<void> {
|
async function loadAll(): Promise<void> {
|
||||||
const skuId = String(route.params.skuId || "");
|
const skuId = String(route.params.skuId || "");
|
||||||
if (!skuId) {
|
if (!skuId) {
|
||||||
@@ -143,6 +203,7 @@ async function loadAll(): Promise<void> {
|
|||||||
]);
|
]);
|
||||||
product.value = productResult;
|
product.value = productResult;
|
||||||
sku.value = skuResult;
|
sku.value = skuResult;
|
||||||
|
setDefaultUnit(skuResult);
|
||||||
stock.value = { ...stockResult, sku: skuResult };
|
stock.value = { ...stockResult, sku: skuResult };
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user