fix ProductEdit.vue style

This commit is contained in:
Timi
2026-09-03 16:09:56 +08:00
parent 2acdf46d5d
commit 1e7441e5d7
11 changed files with 160 additions and 114 deletions
+4
View File
@@ -38,6 +38,10 @@ html.theme-dark body {
box-sizing: border-box; box-sizing: border-box;
} }
.loading {
margin: 1rem auto;
}
.cell-button { .cell-button {
--td-button-border-radius: 1rem; --td-button-border-radius: 1rem;
+2 -2
View File
@@ -13,7 +13,7 @@
<t-input v-model.trim="keyword" class="search" borderless clearable placeholder="搜索产品名称或编码" /> <t-input v-model.trim="keyword" class="search" borderless clearable placeholder="搜索产品名称或编码" />
<div class="list"> <div class="list">
<t-cell-group v-if="filteredProductList.length" theme="card"> <t-cell-group v-if="filteredProductList.length" theme="card">
<t-cell v-for="product in filteredProductList" :key="product.id" class="product" :title="product.name" :description="`${product.code || '-'} · ${product.baseUnit || '-'}`"> <t-cell v-for="product in filteredProductList" :key="product.id" class="product" :title="product.name" :description="`${product.code || '-'} · ${getProductBaseUnitName(product) || '-'}`">
<template #rightIcon> <template #rightIcon>
<div class="units"> <div class="units">
<t-button <t-button
@@ -43,7 +43,7 @@ import ProductAPI from "@/api/ProductAPI";
import type { Product, ProductUnit } from "@/types/Product"; import type { Product, ProductUnit } from "@/types/Product";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { formatProductAmount } from "@/utils/product"; import { formatProductAmount, getProductBaseUnitName } from "@/utils/product";
interface Props { interface Props {
modelValue: { productId: string; productUnitId: string } | undefined; modelValue: { productId: string; productUnitId: string } | undefined;
+1 -1
View File
@@ -371,9 +371,9 @@ onUnmounted(() => navBarStore.clearRight());
.item { .item {
gap: .5rem; gap: .5rem;
border: 1px dashed var(--td-component-border);
padding: .5rem 1rem; padding: .5rem 1rem;
margin-bottom: .75rem; margin-bottom: .75rem;
border: 1px dashed var(--td-component-border);
border-radius: 1rem; border-radius: 1rem;
.header { .header {
+8 -19
View File
@@ -1,8 +1,8 @@
<template> <template>
<div v-can="permissionCodes.product.read" class="product-detail"> <div v-can="permissionCodes.product.read" class="product-detail">
<t-loading v-if="isLoading" size="32px" /> <t-loading v-if="isLoading" class="loading" size="32px" />
<template v-if="product"> <template v-if="product">
<card-container class="info"> <card-container class="info" v-entrance-transition>
<div class="content"> <div class="content">
<div class="left"> <div class="left">
<t-image <t-image
@@ -38,10 +38,9 @@
</div> </div>
</div> </div>
</card-container> </card-container>
<t-cell-group title="基本信息" theme="card"> <t-cell-group title="基本信息" theme="card" v-entrance-transition="70">
<t-cell title="产品分类" :note="product.category?.name || '未分类'" /> <t-cell title="产品分类" :note="product.category?.name || '未分类'" />
<t-cell title="基础单位" :note="product.baseUnit || '-'" /> <t-cell title="基础单位" :note="getProductBaseUnitName(product) || '-'" />
<t-cell title="参考成本" :note="formatProductAmount(product.referenceCost)" />
<t-cell title="库存管理" :note="product.stockEnabled ? '启用' : '关闭'" /> <t-cell title="库存管理" :note="product.stockEnabled ? '启用' : '关闭'" />
<t-cell title="状态" :note="getProductStatusText(product.status)" /> <t-cell title="状态" :note="getProductStatusText(product.status)" />
<t-cell title="备注" :note="Text.display(product.remark)"></t-cell> <t-cell title="备注" :note="Text.display(product.remark)"></t-cell>
@@ -56,7 +55,7 @@
/> />
</t-cell> </t-cell>
</t-cell-group> </t-cell-group>
<t-cell-group title="业务单位" theme="card" class="units"> <t-cell-group title="业务单位" theme="card" class="units" v-entrance-transition="140">
<t-cell <t-cell
v-for="unit in product.unitList || []" v-for="unit in product.unitList || []"
:key="unit.id || unit.name" :key="unit.id || unit.name"
@@ -68,7 +67,7 @@
v-if="!unit.isBase" v-if="!unit.isBase"
theme="default" theme="default"
size="small" size="small"
:content="`${unit.baseQuantityPerUnit} ${product.baseUnit}`" :content="`${unit.baseQuantityPerUnit} ${getProductBaseUnitName(product)}`"
/> />
</div> </div>
</template> </template>
@@ -119,11 +118,12 @@ import PhotoScroller from "@/components/PhotoScroller.vue";
import { useNavBarStore } from "@/store/navBarStore"; import { useNavBarStore } from "@/store/navBarStore";
import type { NavBarAction } from "@/types/NavBarAction"; import type { NavBarAction } from "@/types/NavBarAction";
import type { PhotoItem } from "@/types/Photo"; import type { PhotoItem } from "@/types/Photo";
import type { Product, ProductUnit } from "@/types/Product"; import type { Product } from "@/types/Product";
import { getAttachmentReadURL } from "@/utils/attachment"; import { getAttachmentReadURL } from "@/utils/attachment";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { import {
formatProductAmount, formatProductAmount,
getProductBaseUnitName,
getProductPhotoURL, getProductPhotoURL,
getProductStatusText, getProductStatusText,
getProductStatusTheme, getProductStatusTheme,
@@ -164,17 +164,6 @@ const actionList = computed<NavBarAction[]>(() => {
return list; return list;
}); });
function getUnitUsageText(unit: ProductUnit): string {
const usage: string[] = [];
if (unit.allowSale) {
usage.push("可销售");
}
if (unit.allowPurchase) {
usage.push("可采购");
}
return usage.length ? usage.join("、") : "暂无业务用途";
}
async function load(): Promise<void> { async function load(): Promise<void> {
const id = String(route.params.id || ""); const id = String(route.params.id || "");
if (!id) { if (!id) {
+132 -81
View File
@@ -1,32 +1,46 @@
<template> <template>
<div v-can="savePermissionCode" class="product-edit"> <div v-can="savePermissionCode" class="product-edit">
<t-form class="form" label-align="left" label-width="6rem"> <t-form class="form" label-align="left" label-width="6rem" v-entrance-transition>
<t-form-item label="产品编码" required-mark> <t-form-item label="产品编码" required-mark help="使用英语、拼音或唯一数字">
<t-input v-model.trim="form.code" borderless maxlength="64" placeholder="门店内唯一编码" /> <t-input
v-model="form.code"
borderless
maxlength="64"
placeholder="门店内唯一编码"
/>
</t-form-item> </t-form-item>
<t-form-item label="产品名称" required-mark> <t-form-item label="产品名称" required-mark>
<t-input v-model.trim="form.name" borderless maxlength="128" placeholder="请输入产品名称" /> <t-input
v-model="form.name"
borderless
maxlength="128"
placeholder="请输入产品名称"
/>
</t-form-item> </t-form-item>
<t-form-item label="产品类型" arrow content-align="right" @click="openTypePicker"> <t-form-item
label="产品类型"
arrow
content-align="right"
@click="openTypePicker"
>
<span class="picker-text" v-text="getProductTypeText(form.productType)" /> <span class="picker-text" v-text="getProductTypeText(form.productType)" />
</t-form-item> </t-form-item>
<t-form-item label="产品分类" arrow content-align="right" @click="openCategoryPicker"> <t-form-item label="产品分类" arrow content-align="right" @click="openCategoryPicker">
<span class="picker-text" v-text="selectedCategoryName" /> <span class="picker-text" v-text="selectedCategoryName" />
</t-form-item> </t-form-item>
<t-form-item label="基础单位" required-mark> <t-form-item label="库存管理" content-align="right">
<t-input v-model.trim="form.baseUnit" borderless maxlength="32" placeholder="例如 次、瓶" /> <t-switch size="small" v-model="form.stockEnabled" :disabled="form.productType === 'SERVICE'" />
</t-form-item> </t-form-item>
<t-form-item label="参考成本(元)"> <t-form-item label="允许小数" content-align="right">
<t-input v-model.trim="referenceCostText" type="number" align="right" borderless placeholder="非负金额" /> <t-switch size="small" v-model="form.allowDecimalQuantity" />
</t-form-item> </t-form-item>
<t-form-item label="库存管理"> <t-form-item v-if="form.stockEnabled" label="最低库存" help="基础单位数量,最多三位小数">
<t-checkbox v-model="form.stockEnabled" :disabled="form.productType === 'SERVICE'">管理库存</t-checkbox> <t-input
</t-form-item> v-model="minStockQuantityText"
<t-form-item label="允许小数"> type="number"
<t-checkbox v-model="form.allowDecimalQuantity">业务数量允许小数</t-checkbox> align="right"
</t-form-item> borderless
<t-form-item v-if="form.stockEnabled" label="最低库存"> />
<t-input v-model.trim="minStockQuantityText" type="number" align="right" borderless placeholder="基础单位数量,最多三位小数" />
</t-form-item> </t-form-item>
<t-form-item label="状态" arrow content-align="right" @click="openStatusPicker"> <t-form-item label="状态" arrow content-align="right" @click="openStatusPicker">
<span class="picker-text" v-text="getProductStatusText(form.status)" /> <span class="picker-text" v-text="getProductStatusText(form.status)" />
@@ -34,55 +48,88 @@
<t-form-item label="备注"> <t-form-item label="备注">
<t-textarea v-model.trim="form.remark" borderless maxlength="255" autosize placeholder="可填写产品说明" /> <t-textarea v-model.trim="form.remark" borderless maxlength="255" autosize placeholder="可填写产品说明" />
</t-form-item> </t-form-item>
<t-form-item label-width="0">
<photo-scroller
v-model="attachmentUrlList"
v-model:photo-items="attachmentItemList"
title="附件列表"
:edit-mode="true"
:loading="isAttachmentUploading"
:show-viewer-actions="false"
:add-actions="attachmentAddActions"
@add-photo="openAttachmentPicker('PHOTO')"
@add-other="openAttachmentPicker('OTHER')"
/>
</t-form-item>
</t-form> </t-form>
<card-container
<card-container title="产品附件" description="可上传产品图片或其他产品文件" v-entrance-transition="70"> title="产品单位"
<photo-scroller description="必须有且仅有一个基础单位;销售和库存业务只能选择允许的单位"
v-model="attachmentUrlList" v-entrance-transition="70"
v-model:photo-items="attachmentItemList" >
title="附件列表" <div class="units">
:edit-mode="true" <t-form
:loading="isAttachmentUploading" class="unit"
:show-viewer-actions="false" v-for="(unit, index) in form.unitList"
:add-actions="attachmentAddActions" :key="unit.clientKey"
@add-photo="openAttachmentPicker('PHOTO')" >
@add-other="openAttachmentPicker('OTHER')" <t-form-item label="单位名称" required-mark>
/> <t-input v-model.trim="unit.name" borderless maxlength="32" placeholder="如 瓶、次" />
<input ref="attachmentInputRef" class="file-input" type="file" accept="image/*" @change="handleAttachmentSelected" /> <!-- <t-button v-if="1 < form.unitList.length" theme="danger" variant="text" size="small" @click="removeUnit(index)">删除</t-button>-->
</card-container> <t-button
theme="danger"
<card-container title="产品单位" description="必须保留一个基础单位;销售和库存业务只能选择允许的单位" v-entrance-transition="70"> variant="text"
<div class="unit-list"> size="extra-small"
<div v-for="(unit, index) in form.unitList" :key="unit.clientKey" class="unit-item"> content="删除"
<div class="unit-header"> @click="removeUnit(index)"
<strong v-text="`单位 ${index + 1}`" /> />
<t-button v-if="1 < form.unitList.length" theme="danger" variant="text" size="small" @click="removeUnit(index)">删除</t-button> </t-form-item>
</div> <t-form-item label="基础单位" content-align="right">
<t-form label-width="6rem"> <t-switch
<t-form-item label="单位名称" required-mark> size="small"
<t-input v-model.trim="unit.name" borderless maxlength="32" placeholder="例如 瓶、盒、次" /> v-model="unit.isBase"
</t-form-item> :disabled="form.unitList.length === 1"
<t-form-item label="基础单位数量" required-mark> @change="setBaseUnit(index)"
<t-input v-model.trim="unit.baseQuantityPerUnit" type="number" align="right" borderless placeholder="每个单位对应的基础单位数量" /> />
</t-form-item> </t-form-item>
<t-form-item label="建议售价(元)"> <t-form-item
<t-input v-model.trim="unit.salePriceText" type="number" align="right" borderless placeholder="非负金额" /> label="基础单位数量"
</t-form-item> help="每个单位对应的基础单位数量"
<t-form-item label="基础单位"> label-width="7rem"
<t-checkbox v-model="unit.isBase" @change="setBaseUnit(index)">基础单位</t-checkbox> required-mark
</t-form-item> >
<t-form-item label="允许采购"> <t-input
<t-checkbox v-model="unit.allowPurchase">入库可用</t-checkbox> v-model="unit.baseQuantityPerUnit"
</t-form-item> type="number"
<t-form-item label="允许销售"> align="right"
<t-checkbox v-model="unit.allowSale">销售和套餐可用</t-checkbox> borderless
</t-form-item> :disabled="form.unitList.length === 1"
</t-form> />
</div> </t-form-item>
<t-button class="add-unit" theme="primary" variant="dashed" block @click="addUnit">添加业务单位</t-button> <t-form-item label="建议售价">
<t-input
v-model="unit.salePriceText"
type="number"
align="right"
borderless
placeholder="金额(元)"
/>
</t-form-item>
<t-form-item label="允许采购" content-align="right">
<t-switch size="small" v-model="unit.allowPurchase" />
</t-form-item>
<t-form-item label="允许销售" content-align="right">
<t-switch size="small" v-model="unit.allowSale" />
</t-form-item>
</t-form>
<t-button
class="cell-button"
theme="primary"
content="添加业务单位"
@click="addUnit"
/>
</div> </div>
</card-container> </card-container>
<t-popup v-model="typePickerVisible" placement="bottom"> <t-popup v-model="typePickerVisible" placement="bottom">
<t-picker v-model="typeDraft" :columns="productTypeOptions" title="产品类型" @cancel="typePickerVisible = false" @confirm="confirmType" /> <t-picker v-model="typeDraft" :columns="productTypeOptions" title="产品类型" @cancel="typePickerVisible = false" @confirm="confirmType" />
</t-popup> </t-popup>
@@ -92,6 +139,7 @@
<t-popup v-model="statusPickerVisible" placement="bottom"> <t-popup v-model="statusPickerVisible" placement="bottom">
<t-picker v-model="statusDraft" :columns="statusOptions" title="产品状态" @cancel="statusPickerVisible = false" @confirm="confirmStatus" /> <t-picker v-model="statusDraft" :columns="statusOptions" title="产品状态" @cancel="statusPickerVisible = false" @confirm="confirmStatus" />
</t-popup> </t-popup>
<input ref="attachmentInputRef" class="file-input" type="file" accept="image/*" @change="handleAttachmentSelected" />
</div> </div>
</template> </template>
@@ -131,14 +179,12 @@ const form = reactive<Product & { unitList: ProductUnitForm[] }>({
code: "", code: "",
name: "", name: "",
productType: "GOODS", productType: "GOODS",
baseUnit: "",
stockEnabled: true, stockEnabled: true,
allowDecimalQuantity: false, allowDecimalQuantity: false,
status: "ACTIVE", status: "ACTIVE",
unitList: [] unitList: []
}); });
const referenceCostText = ref(""); const minStockQuantityText = ref("0");
const minStockQuantityText = ref("");
const categoryList = ref<ProductCategory[]>([]); const categoryList = ref<ProductCategory[]>([]);
const typePickerVisible = ref(false); const typePickerVisible = ref(false);
const categoryPickerVisible = ref(false); const categoryPickerVisible = ref(false);
@@ -331,19 +377,14 @@ function parseOptionalAmount(value: string): number | null | undefined {
} }
function validate(): boolean { function validate(): boolean {
if (!form.code.trim() || !form.name.trim() || !form.baseUnit.trim()) { if (!form.code.trim() || !form.name.trim()) {
Message.warning("产品编码、名称和基础单位不能为空"); Message.warning("产品编码和名称不能为空");
return false; return false;
} }
if (form.productType === "SERVICE" && form.stockEnabled) { if (form.productType === "SERVICE" && form.stockEnabled) {
Message.warning("服务产品不能管理库存"); Message.warning("服务产品不能管理库存");
return false; return false;
} }
const referenceCost = parseOptionalAmount(referenceCostText.value);
if (referenceCost === undefined) {
Message.warning("价格必须是合法的非负元金额,最多两位小数");
return false;
}
if (form.stockEnabled && !normalizeProductQuantity(minStockQuantityText.value || "0")) { if (form.stockEnabled && !normalizeProductQuantity(minStockQuantityText.value || "0")) {
Message.warning("最低库存必须是非负数量,最多三位小数"); Message.warning("最低库存必须是非负数量,最多三位小数");
return false; return false;
@@ -377,10 +418,6 @@ function validate(): boolean {
Message.warning("基础单位数量必须为 1"); Message.warning("基础单位数量必须为 1");
return false; return false;
} }
if (name !== form.baseUnit.trim()) {
Message.warning("基础单位名称必须与产品基础单位一致");
return false;
}
} }
} }
if (baseCount !== 1) { if (baseCount !== 1) {
@@ -404,11 +441,9 @@ async function save(): Promise<void> {
code: form.code.trim(), code: form.code.trim(),
name: form.name.trim(), name: form.name.trim(),
productType: form.productType, productType: form.productType,
baseUnit: form.baseUnit.trim(),
stockEnabled: form.stockEnabled, stockEnabled: form.stockEnabled,
allowDecimalQuantity: form.allowDecimalQuantity, allowDecimalQuantity: form.allowDecimalQuantity,
status: form.status, status: form.status,
referenceCost: parseOptionalAmount(referenceCostText.value)!,
minStockQuantity: form.stockEnabled ? minStockQuantity : null, minStockQuantity: form.stockEnabled ? minStockQuantity : null,
remark: form.remark?.trim() || null, remark: form.remark?.trim() || null,
attachmentList: buildAttachmentList(), attachmentList: buildAttachmentList(),
@@ -456,7 +491,6 @@ async function load(): Promise<void> {
code: result.code || "", code: result.code || "",
name: result.name || "", name: result.name || "",
productType: result.productType || "GOODS", productType: result.productType || "GOODS",
baseUnit: result.baseUnit || "",
stockEnabled: !!result.stockEnabled, stockEnabled: !!result.stockEnabled,
allowDecimalQuantity: !!result.allowDecimalQuantity, allowDecimalQuantity: !!result.allowDecimalQuantity,
status: result.status || "ACTIVE", status: result.status || "ACTIVE",
@@ -471,7 +505,6 @@ async function load(): Promise<void> {
previewUrl: getAttachmentReadURL(item.id || ""), previewUrl: getAttachmentReadURL(item.id || ""),
url: getAttachmentReadURL(item.id || "", 90) url: getAttachmentReadURL(item.id || "", 90)
})); }));
referenceCostText.value = result.referenceCost === null || result.referenceCost === undefined ? "" : `${Math.floor(result.referenceCost / 100)}.${String(result.referenceCost % 100).padStart(2, "0")}`;
minStockQuantityText.value = result.minStockQuantity === null || result.minStockQuantity === undefined ? "" : String(result.minStockQuantity); minStockQuantityText.value = result.minStockQuantity === null || result.minStockQuantity === undefined ? "" : String(result.minStockQuantity);
if (!form.unitList.length) { if (!form.unitList.length) {
addUnit(); addUnit();
@@ -508,6 +541,24 @@ onUnmounted(() => navBarStore.clearRight());
overflow: hidden; overflow: hidden;
} }
.units {
padding: 1rem 0;
.unit {
gap: .5rem;
border: 1px dashed var(--td-component-border);
padding: .5rem 0;
margin-bottom: .75rem;
border-radius: 1rem;
.header {
display: flex;
align-items: center;
justify-content: space-between;
}
}
}
.unit-list { .unit-list {
padding: 1rem; padding: 1rem;
-1
View File
@@ -81,7 +81,6 @@ import NavBarActionMenu from "@/components/NavBarActionMenu.vue";
import { useNavBarStore } from "@/store/navBarStore"; import { useNavBarStore } from "@/store/navBarStore";
import type { NavBarAction } from "@/types/NavBarAction"; import type { NavBarAction } from "@/types/NavBarAction";
import type { Product, ProductCategory, ProductStatus, ProductType } from "@/types/Product"; import type { Product, ProductCategory, ProductStatus, ProductType } from "@/types/Product";
import { getAttachmentReadURL } from "@/utils/attachment";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { getProductPhotoURL, getProductStatusText, getProductStatusTheme, getProductTypeText } from "@/utils/product"; import { getProductPhotoURL, getProductStatusText, getProductStatusTheme, getProductTypeText } from "@/utils/product";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
+4 -4
View File
@@ -10,13 +10,13 @@
</div> </div>
<div class="hero-meta"> <div class="hero-meta">
<span v-text="product.code || '无产品编码'" /> <span v-text="product.code || '无产品编码'" />
<strong v-text="`${getProductQuantityText(stock.availableQuantity)} ${product.baseUnit || ''}`" /> <strong v-text="`${getProductQuantityText(stock.availableQuantity)} ${getProductBaseUnitName(product)}`" />
</div> </div>
</section> </section>
<card-container title="库存余额"> <card-container title="库存余额">
<t-cell-group theme="card" class="summary"> <t-cell-group theme="card" class="summary">
<t-cell title="产品" :note="product.name || '未命名产品'" /> <t-cell title="产品" :note="product.name || '未命名产品'" />
<t-cell title="基础单位" :note="product.baseUnit || '-'" /> <t-cell title="基础单位" :note="getProductBaseUnitName(product) || '-'" />
<t-cell title="实际库存" :note="getProductQuantityText(stock.onHandQuantity)" /> <t-cell title="实际库存" :note="getProductQuantityText(stock.onHandQuantity)" />
<t-cell title="预占库存" :note="getProductQuantityText(stock.reservedQuantity)" /> <t-cell title="预占库存" :note="getProductQuantityText(stock.reservedQuantity)" />
<t-cell title="可用库存" :note="getProductQuantityText(stock.availableQuantity)" /> <t-cell title="可用库存" :note="getProductQuantityText(stock.availableQuantity)" />
@@ -43,7 +43,7 @@
</template> </template>
<template #description> <template #description>
<div class="record-description"> <div class="record-description">
<span v-text="`${record.quantity || 0} ${record.unitSnapshot || product.baseUnit || ''} · 基础单位 ${record.baseQuantity || 0}`" /> <span v-text="`${record.quantity || 0} ${record.unitSnapshot || getProductBaseUnitName(product)} · 基础单位 ${record.baseQuantity || 0}`" />
<span class="gray" v-text="record.reason || record.remark || '无备注'" /> <span class="gray" v-text="record.reason || record.remark || '无备注'" />
</div> </div>
</template> </template>
@@ -72,7 +72,7 @@ import ProductStockOperationDialog from "@/pages/product/ProductStockOperationDi
import { useNavBarStore } from "@/store/navBarStore"; import { useNavBarStore } from "@/store/navBarStore";
import type { Product, ProductStock, StockOperationType, StockRecord } from "@/types/Product"; import type { Product, ProductStock, StockOperationType, StockRecord } from "@/types/Product";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { getProductQuantityText, getStockOperationText } from "@/utils/product"; import { getProductBaseUnitName, getProductQuantityText, getStockOperationText } from "@/utils/product";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
+2 -2
View File
@@ -13,7 +13,7 @@
</div> </div>
</template> </template>
<template #note> <template #note>
<span v-text="`${getProductQuantityText(item.availableQuantity)} ${item.product?.baseUnit || ''}`" /> <span v-text="`${getProductQuantityText(item.availableQuantity)} ${getProductBaseUnitName(item.product)}`" />
</template> </template>
<template #description> <template #description>
<div class="description"> <div class="description">
@@ -35,7 +35,7 @@ import ProductAPI from "@/api/ProductAPI";
import { useNavBarStore } from "@/store/navBarStore"; import { useNavBarStore } from "@/store/navBarStore";
import type { ProductStock } from "@/types/Product"; import type { ProductStock } from "@/types/Product";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { getProductQuantityText } from "@/utils/product"; import { getProductBaseUnitName, getProductQuantityText } from "@/utils/product";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import { permissionCodes } from "@/utils/permission"; import { permissionCodes } from "@/utils/permission";
@@ -10,7 +10,7 @@
<div class="operation-dialog"> <div class="operation-dialog">
<div class="summary"> <div class="summary">
<strong v-text="props.product.name || '未命名产品'" /> <strong v-text="props.product.name || '未命名产品'" />
<span class="gray" v-text="`当前可用库存:${getProductQuantityText(props.stock?.availableQuantity)} ${props.product.baseUnit || ''}`" /> <span class="gray" v-text="`当前可用库存:${getProductQuantityText(props.stock?.availableQuantity)} ${getProductBaseUnitName(props.product)}`" />
</div> </div>
<t-form label-align="left" label-width="5rem"> <t-form label-align="left" label-width="5rem">
<t-form-item label="业务单位" required-mark @click="openUnitPicker"> <t-form-item label="业务单位" required-mark @click="openUnitPicker">
@@ -39,7 +39,7 @@ import { watch } from "vue";
import ProductAPI from "@/api/ProductAPI"; import ProductAPI from "@/api/ProductAPI";
import type { Product, ProductStock, ProductUnit, ProductStockChangeRequest, StockOperationType } from "@/types/Product"; import type { Product, ProductStock, ProductUnit, ProductStockChangeRequest, StockOperationType } from "@/types/Product";
import { getErrorMessage } from "@/utils/error"; import { getErrorMessage } from "@/utils/error";
import { createProductRequestNo, getProductQuantityText, getStockOperationText } from "@/utils/product"; import { createProductRequestNo, getProductBaseUnitName, getProductQuantityText, getStockOperationText } from "@/utils/product";
import { Message } from "@/utils/message"; import { Message } from "@/utils/message";
import type { PhotoItem } from "@/types/Photo"; import type { PhotoItem } from "@/types/Photo";
-2
View File
@@ -40,11 +40,9 @@ export type Product = {
code: string; code: string;
name: string; name: string;
productType: ProductType; productType: ProductType;
baseUnit: string;
stockEnabled: boolean; stockEnabled: boolean;
allowDecimalQuantity: boolean; allowDecimalQuantity: boolean;
status?: ProductStatus; status?: ProductStatus;
referenceCost?: number | null;
minStockQuantity?: number | string | null; minStockQuantity?: number | string | null;
sort?: number; sort?: number;
remark?: string | null; remark?: string | null;
+5
View File
@@ -89,3 +89,8 @@ export function getProductQuantityText(value?: number | string | null): string {
} }
return normalizeProductQuantity(value) || "0"; return normalizeProductQuantity(value) || "0";
} }
/** 从产品单位列表中获取基础单位名称。 */
export function getProductBaseUnitName(product?: Product): string {
return product?.unitList?.find(unit => unit.isBase)?.name || "";
}