add ProductStockPackageSummary.vue
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
<product-stock-package-summary :sku="sku" :stock="stock" />
|
||||
<t-cell title="查看库存流水" arrow @click="openRecordList" />
|
||||
<t-cell v-if="0 < Number(stock.reservedQuantity || 0)" title="查看库存预占流水" arrow @click="openReservedRecordList" />
|
||||
</t-cell-group>
|
||||
@@ -100,6 +101,7 @@ import { shallowRef } from "vue";
|
||||
import { useUserStore } from "timi-web-api";
|
||||
import ProductAPI from "@/api/ProductAPI";
|
||||
import ProductProfile from "@/pages/product/component/ProductProfile.vue";
|
||||
import ProductStockPackageSummary from "@/pages/product/component/ProductStockPackageSummary.vue";
|
||||
import ProductStockOperationPopup from "@/pages/product/ProductStockOperationPopup.vue";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { Product, ProductSku, ProductStock, ProductSkuUnit, StockOperationType } from "@/types/Product";
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<template v-if="packageUnits.length">
|
||||
<t-cell class="packages" title="整包装换算">
|
||||
<template #note>
|
||||
<div class="options" role="group" aria-label="选择整包装换算单位">
|
||||
<t-button
|
||||
v-for="unit in packageUnits"
|
||||
:key="unit.id"
|
||||
size="extra-small"
|
||||
:theme="selectedUnit?.id === unit.id ? 'primary' : 'default'"
|
||||
:variant="selectedUnit?.id === unit.id ? 'base' : 'outline'"
|
||||
:aria-label="`按${unit.name || '未命名单位'}换算`"
|
||||
:aria-pressed="selectedUnit?.id === unit.id"
|
||||
@click="selectedUnitId = unit.id || ''"
|
||||
>
|
||||
<span v-text="`按${unit.name || '未命名单位'}`" />
|
||||
</t-button>
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
<t-cell title="库存折合" :note="summary.onHand" />
|
||||
<t-cell title="可用折合" :note="summary.available" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ProductSku, ProductStock } from "@/types/Product";
|
||||
import { splitBaseQuantityIntoPackages } from "@/utils/package";
|
||||
|
||||
const props = defineProps<{ sku: ProductSku; stock: ProductStock }>();
|
||||
|
||||
// ---------- 整包装换算 ----------
|
||||
|
||||
const selectedUnitId = ref("");
|
||||
const baseUnits = computed(() => (props.sku.unitList || []).filter(unit => unit.isBase));
|
||||
const packageUnits = computed(() => (props.sku.unitList || [])
|
||||
.filter(unit => unit.id && !unit.isBase && 1 < Number(unit.baseQuantityPerUnit))
|
||||
.sort((left, right) => Number(right.baseQuantityPerUnit) - Number(left.baseQuantityPerUnit)));
|
||||
const selectedUnit = computed(() => packageUnits.value.find(unit => unit.id === selectedUnitId.value) || packageUnits.value[0]);
|
||||
const summary = computed(() => ({
|
||||
onHand: getSummaryText(props.stock.onHandQuantity),
|
||||
available: getSummaryText(props.stock.availableQuantity)
|
||||
}));
|
||||
|
||||
/** 使用原始基础库存换算,保留基础单位余数,异常数据不按零库存展示。 */
|
||||
function getSummaryText(quantity?: number | string): string {
|
||||
const baseUnit = baseUnits.value[0];
|
||||
const unit = selectedUnit.value;
|
||||
if (quantity === undefined || baseUnits.value.length !== 1 || !baseUnit?.name
|
||||
|| Number(baseUnit.baseQuantityPerUnit) !== 1 || !unit?.name) {
|
||||
return "无法换算";
|
||||
}
|
||||
const result = splitBaseQuantityIntoPackages(quantity, unit.baseQuantityPerUnit);
|
||||
if (!result) {
|
||||
return "无法换算";
|
||||
}
|
||||
const parts: string[] = [];
|
||||
if (result.whole !== "0") {
|
||||
parts.push(`${result.whole} ${unit.name}`);
|
||||
}
|
||||
if (result.remainder !== "0" || !parts.length) {
|
||||
parts.push(`${result.remainder} ${baseUnit.name}`);
|
||||
}
|
||||
return parts.join(" ");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.packages {
|
||||
.options {
|
||||
--td-button-border-radius: .5rem;
|
||||
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -100,6 +100,29 @@ export function convertBaseQuantityToPackageQuantity(baseQuantity: number | stri
|
||||
return formatDecimal(rounded, 3, 3);
|
||||
}
|
||||
|
||||
/** 将非负基础数量精确拆分为整包装和基础单位余数,不对余数取整。 */
|
||||
export function splitBaseQuantityIntoPackages(baseQuantity: number | string, baseQuantityPerUnit: number | string): { whole: string; remainder: string } | undefined {
|
||||
const quantityParts = toDecimalParts(baseQuantity, 3);
|
||||
const unitParts = toDecimalParts(baseQuantityPerUnit, 6);
|
||||
if (!quantityParts || !unitParts) {
|
||||
return undefined;
|
||||
}
|
||||
const scale = Math.max(quantityParts.scale, unitParts.scale);
|
||||
const factor = 10n ** BigInt(scale);
|
||||
const quantity = quantityParts.value * 10n ** BigInt(scale - quantityParts.scale);
|
||||
const unit = unitParts.value * 10n ** BigInt(scale - unitParts.scale);
|
||||
if (unit <= factor) {
|
||||
return undefined;
|
||||
}
|
||||
const remainder = quantity % unit;
|
||||
const integer = (remainder / factor).toString();
|
||||
const fraction = (remainder % factor).toString().padStart(scale, "0").replace(/0+$/, "");
|
||||
return {
|
||||
whole: (quantity / unit).toString(),
|
||||
remainder: fraction ? `${integer}.${fraction}` : integer
|
||||
};
|
||||
}
|
||||
|
||||
/** 精确相加两个最多三位小数的数量。 */
|
||||
export function addPackageQuantity(left: number | string, right: number | string): string | undefined {
|
||||
const leftParts = toQuantityParts(left);
|
||||
|
||||
Reference in New Issue
Block a user