Explorar el Código

feat(product): 添加商品描述字段并优化表单验证逻辑

- 新增商品描述文本域字段,限制500字符并显示字数统计
- 将促销标题字段改为包装规格,商品简介改为商品说明
- 添加重量和体积输入的数字格式验证,只允许数字和小数点
- 实现价格链校验逻辑,确保供应价 < 官网价 < 市场价
- 添加参考链接必填验证及中文字符过滤功能
- 优化税率显示为百分比格式,由税率编码自动带出
- 添加供应天数自动计算到期时间功能
- 更新轮播图组件为逗号分隔字符串格式管理
- 添加关键输入框引用用于校验失败时自动聚焦
- 实现三级分类联动填充采购信息功能
肖路 hace 4 días
padre
commit
1c0882682a

+ 1 - 1
src/api/external/product/types.ts

@@ -420,7 +420,7 @@ export interface ThirdProductVO {
   unitName?: string;
 
   /**
-   * 可用库存数
+   * 现有库存数
    */
   availableStock?: number;
 

+ 3 - 3
src/api/product/priceInventory/types.ts

@@ -35,7 +35,7 @@ export interface PriceInventoryVO {
   totalInventory: number;
 
   /**
-   * 当前可用库存
+   * 当前现有库存
    */
   nowInventory: number;
 
@@ -103,7 +103,7 @@ export interface PriceInventoryForm extends BaseEntity {
   totalInventory?: number;
 
   /**
-   * 当前可用库存
+   * 当前现有库存
    */
   nowInventory?: number;
 
@@ -167,7 +167,7 @@ export interface PriceInventoryQuery extends PageQuery {
   totalInventory?: number;
 
   /**
-   * 当前可用库存
+   * 当前现有库存
    */
   nowInventory?: number;
 

+ 3 - 3
src/api/product/warehouseInventory/types.ts

@@ -35,7 +35,7 @@ export interface WarehouseInventoryVO {
   warehouseName: string;
 
   /**
-   * 可用库存
+   * 现有库存
    */
   nowInventory: number;
 
@@ -93,7 +93,7 @@ export interface WarehouseInventoryForm extends BaseEntity {
   warehouseName?: string;
 
   /**
-   * 可用库存
+   * 现有库存
    */
   nowInventory?: number;
 
@@ -147,7 +147,7 @@ export interface WarehouseInventoryQuery extends PageQuery {
   warehouseName?: string;
 
   /**
-   * 可用库存
+   * 现有库存
    */
   nowInventory?: number;
 

+ 18 - 3
src/components/TaxCodeSelect/index.vue

@@ -68,9 +68,16 @@
                 <el-link :type="isRowDisabled(row) ? 'info' : 'primary'" :disabled="isRowDisabled(row)" :underline="false">{{ row.abbreviation }}</el-link>
               </template>
             </el-table-column>
-            <!-- <el-table-column label="说明" align="center" prop="remark" min-width="150" show-overflow-tooltip />
-            <el-table-column label="税率" align="center" prop="taxRate" width="80" />
-            <el-table-column label="用户选择比例" align="center" prop="selectRatio" width="110" /> -->
+            <el-table-column label="说明" align="center" prop="remark" min-width="150" show-overflow-tooltip>
+              <template #default="{ row }">
+                <el-link :type="isRowDisabled(row) ? 'info' : 'primary'" :disabled="isRowDisabled(row)" :underline="false">{{ row.remark }}</el-link>
+              </template>
+            </el-table-column>
+            <el-table-column label="税率" align="center" prop="explain" width="90">
+              <template #default="{ row }">
+                <el-link :type="isRowDisabled(row) ? 'info' : 'primary'" :disabled="isRowDisabled(row)" :underline="false">{{ formatTaxRate(row.taxrate) }}</el-link>
+              </template>
+            </el-table-column>
           </el-table>
 
           <!-- 分页 -->
@@ -203,6 +210,14 @@ const handleSearch = () => {
   getList();
 };
 
+/** 格式化税率为百分制显示 */
+const formatTaxRate = (val: any): string => {
+  if (val === null || val === undefined || val === '') return '';
+  const num = Number(val);
+  if (isNaN(num)) return String(val);
+  return `${Math.round(num * 100)}%`;
+};
+
 /** 判断行是否禁用:非叶子节点(isBottom === 0)不可选 */
 const isRowDisabled = (row: TaxCodeVO): boolean => {
   return Number((row as any)?.isBottom) === 0;

+ 259 - 80
src/views/product/base/add.vue

@@ -188,6 +188,18 @@
               <div class="form-item-tip">A10产品名称由系统自动拼接:品牌名 + 规格型号 + 产品分类(三级分类)+ 发票规格,无需手动填写</div>
             </el-form-item>
 
+                        <!-- 商品描述 -->
+            <el-form-item label="商品描述:">
+              <el-input
+                v-model="productForm.productDescription"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="500"
+                show-word-limit
+              />
+            </el-form-item>
+
             <!-- 规格型号 和 UPC(69)条码 -->
             <el-row :gutter="20">
               <el-col :span="12">
@@ -235,7 +247,7 @@
               </el-col>
 
               <el-col :span="12">
-                <el-form-item label="单位:">
+                <el-form-item label="单位:" required>
                   <el-select
                     v-model="productForm.unitId"
                     placeholder="请选择"
@@ -252,7 +264,7 @@
             <!-- 税率编码 、税率 和 币种 -->
             <el-row :gutter="20">
               <el-col :span="12">
-                <el-form-item label="税率编码:">
+                <el-form-item label="税率编码:" required>
                   <el-input
                     v-model="taxCodeNo"
                     placeholder="点击选择税率编码"
@@ -269,9 +281,7 @@
               </el-col>
               <el-col :span="12">
                 <el-form-item label="税率:" required>
-                  <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
-                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
-                  </el-select>
+                  <el-input :model-value="formatTaxRateDisplay(productForm.taxRate)" placeholder="由税率编码带出" readonly disabled class="w-full" />
                 </el-form-item>
               </el-col>
             </el-row>
@@ -308,19 +318,32 @@
 
             <!-- 促销标题 -->
             <el-form-item label="促销标题:">
-              <el-input v-model="productForm.promotionTitle" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
+              <el-input v-model="productForm.packagingSpec" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
             </el-form-item>
 
-            <!-- 商品简介 -->
-            <el-form-item label="商品简介:">
-              <el-input v-model="productForm.productDescription" type="textarea" :rows="3" placeholder="请输入商品简介" maxlength="500" show-word-limit />
+                        <!-- 商品说明 -->
+            <el-form-item label="商品说明:">
+              <el-input
+                v-model="productForm.productExplain"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品说明"
+                maxlength="500"
+                show-word-limit
+              />
             </el-form-item>
 
             <!-- 重量 和 体积 -->
             <el-row :gutter="20">
               <el-col :span="12">
                 <el-form-item label="商品重量:">
-                  <el-input v-model="productForm.productWeight" placeholder="0" maxlength="10" show-word-limit>
+                  <el-input
+                    v-model="productForm.productWeight"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                    @input="handleWeightInput"
+                  >
                     <template #append>
                       <el-select v-model="productForm.weightUnit" placeholder="请选择" style="width: 100px">
                         <el-option label="kg" value="kg" />
@@ -333,7 +356,13 @@
               </el-col>
               <el-col :span="12">
                 <el-form-item label="商品体积:">
-                  <el-input v-model="productForm.productVolume" placeholder="0" maxlength="10" show-word-limit>
+                  <el-input
+                    v-model="productForm.productVolume"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                    @input="handleVolumeInput"
+                  >
                     <template #append>
                       <el-select v-model="productForm.volumeUnit" placeholder="请选择" style="width: 80px">
                         <el-option label="m³" value="m3" />
@@ -347,12 +376,14 @@
             </el-row>
 
             <!-- 参考链接 -->
-            <el-form-item label="参考链接">
+            <el-form-item label="参考链接" prop="referenceLink" required>
               <el-input
+                ref="referenceLinkRef"
                 v-model="productForm.referenceLink"
                 type="textarea"
                 :rows="3"
                 placeholder="请输入参考链接"
+                @input="handleReferenceLinkInput"
               />
             </el-form-item>
 
@@ -389,17 +420,25 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="市场价:" prop="marketPrice" required>
-                  <el-input v-model="productForm.marketPrice" type="number" placeholder="请输入市场价" :min="0" @blur="formatPrice('marketPrice')" />
+                  <el-input
+                    ref="marketPriceRef"
+                    v-model="productForm.marketPrice"
+                    type="number"
+                    placeholder="请输入市场价"
+                    :min="0"
+                    @blur="handlePriceBlur('marketPrice')"
+                  />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
                 <el-form-item label="官网价:" prop="memberPrice" required>
                   <el-input
+                    ref="memberPriceRef"
                     v-model="productForm.memberPrice"
                     type="number"
                     placeholder="请输入平台售价"
                     :min="0"
-                    @blur="formatPrice('memberPrice')"
+                    @blur="handlePriceBlur('memberPrice')"
                   />
                 </el-form-item>
               </el-col>
@@ -412,7 +451,7 @@
               </el-col>
               <el-col :span="8">
                 <el-form-item label="备注:">
-                  <span class="currency-text">市场价>官网价</span>
+                  <span class="currency-text">供应价 &lt; 官网价 &lt; 市场价</span>
                 </el-form-item>
               </el-col>
             </el-row>
@@ -434,26 +473,33 @@
                     type="number"
                     placeholder="请输入供应价"
                     :min="0"
-                    @blur="formatPrice('supplyPrice')"
+                    @blur="handlePriceBlur('supplyPrice')"
                   />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="供应有效时间:">
-                  <el-date-picker
-                    v-model="productForm.supplyValidityPeriod"
-                    type="date"
-                    placeholder="请选择供应有效时间"
-                    value-format="YYYY-MM-DD"
-                    class="w-full"
+                <el-form-item label="供应时间(天):">
+                  <el-input
+                    v-model="productForm.supplyDays"
+                    type="number"
+                    placeholder="请输入供应天数"
+                    :min="1"
+                    @blur="calcSupplyExpireTime"
                   />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="是否包邮:">
+                <el-form-item label="到期时间:">
+                  <el-input v-model="productForm.supplyValidityPeriod" disabled placeholder="自动计算" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="一件代发:">
                   <el-radio-group v-model="productForm.supplyPostStatus">
-                    <el-radio :value="0">不包邮</el-radio>
-                    <el-radio :value="1">包邮</el-radio>
+                    <el-radio :value="0">不支持</el-radio>
+                    <el-radio :value="1">支持</el-radio>
                   </el-radio-group>
                 </el-form-item>
               </el-col>
@@ -559,14 +605,14 @@
           <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
             <!-- 商品主图 -->
             <el-form-item label="商品主图:" required>
-              <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
-              <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
+              <image-upload v-model="productForm.productImage" :limit="1" />
+              <div class="form-item-tip">建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品轮播图 -->
             <el-form-item label="商品轮播图:" required>
-              <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
-              <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
+              <image-upload v-model="carouselImages" :limit="20" />
+              <div class="form-item-tip">支持多张上传,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品详情 -->
@@ -714,9 +760,9 @@ import { useRoute, useRouter } from 'vue-router';
 import { ElMessage } from 'element-plus';
 import { Warning, ArrowRight, Check, Plus, CircleCheck, Search } from '@element-plus/icons-vue';
 import Editor from '@/components/Editor/index.vue';
-import UploadImage from '@/components/upload-image/index.vue';
 import TaxCodeSelect from '@/components/TaxCodeSelect/index.vue';
 import { categoryTreeVO, CategoryVO } from '@/api/product/category/types';
+import { getCategory } from '@/api/product/category';
 import { BrandVO } from '@/api/product/brand/types';
 import { BaseForm } from '@/api/product/base/types';
 import { ClassificationDiyForm } from '@/api/product/classificationDiy/types';
@@ -730,8 +776,7 @@ import {
   categoryAttributeList,
   getAfterSaleList,
   getServiceList,
-  getUnitList,
-  getTaxRateList
+  getUnitList
 } from '@/api/product/base';
 import {
   addBaseAudit,
@@ -754,6 +799,11 @@ const loading = ref(false);
 const submitLoading = ref(false);
 const productFormRef = ref();
 
+// 关键输入框 refs,用于校验失败时聚焦
+const referenceLinkRef = ref();
+const marketPriceRef = ref();
+const memberPriceRef = ref();
+
 // 服务保障和安装服务的多选框
 const serviceGuarantees = ref<(string | number)[]>([]);
 const installationServices = ref<string[]>([]);
@@ -761,24 +811,37 @@ const installationServices = ref<string[]>([]);
 // 商品详情选项卡
 const activeDetailTab = ref('pc');
 
-// 轮播图URL数组(UI管理用)
-const carouselImages = ref<string[]>([]);
-
-// 税率选项
-const taxRateOptions = ref<any[]>([]);
+// 轮播图(逗号分隔的 ossId 字符串,由 ImageUpload 管理)
+const carouselImages = ref<string>('');
 
 // 税率编码选择组件
 const taxCodeSelectRef = ref();
 // 已选的税率编码(显示用)
 const taxCodeNo = ref('');
 
+// 格式化税率显示(小数转百分比)
+const formatTaxRateDisplay = (val: any): string => {
+  if (val === null || val === undefined || val === '') return '';
+  const num = Number(val);
+  if (isNaN(num)) return String(val);
+  return `${Math.round(num * 100)}%`;
+};
+
 // 处理税率编码选择
 const handleTaxCodeSelect = async (row: any) => {
   (productForm as any).taxationId = row.id;
+  // 选择税率编码时自动带出税率
+  if (row.taxrate !== undefined && row.taxrate !== null) {
+    productForm.taxRate = Number(row.taxrate);
+  }
   try {
     const taxRes = await getTaxCode(row.id);
     if (taxRes.data) {
       taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+      // 使用详情接口返回的税率值(更准确)
+      if ((taxRes.data as any).taxrate !== undefined && (taxRes.data as any).taxrate !== null) {
+        productForm.taxRate = Number((taxRes.data as any).taxrate);
+      }
     } else {
       taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
     }
@@ -966,10 +1029,11 @@ const productForm = reactive<BaseForm>({
   purchasingPrice: undefined,
   maxPurchasePrice: undefined,
   supplyPrice: undefined,
+  supplyDays: undefined,
   supplyValidityPeriod: undefined,
   supplyPostStatus: undefined,
-  productNature: '1',
-  purchasingPersonnel: '1',
+  productNature: '',
+  purchasingPersonnel: '',
   pcDetail: undefined,
   mobileDetail: undefined,
   taxRate: undefined,
@@ -984,9 +1048,23 @@ const productRules = {
   itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
   brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
   marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
-  memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
+  memberPrice: [{ required: true, message: '官网价不能为空', trigger: 'blur' }],
   minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
-  // purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  maxPurchasePrice: [{ required: true, message: '最高采购价不能为空', trigger: 'blur' }],
+  referenceLink: [
+    { required: true, message: '参考链接不能为空', trigger: 'blur' },
+    {
+      validator: (_rule: any, value: any, callback: any) => {
+        if (value && /\u3000/.test(String(value))) {
+          callback(new Error('参考链接不能包含中文空格'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
   productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
   purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
   taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
@@ -1091,6 +1169,39 @@ const handleLevel3Search = async (keyword: string) => {
   }
 };
 
+// 从三级分类联动填充采购信息
+const fillPurchaseFromCategory = async (categoryId: string | number, category?: CategoryVO) => {
+  try {
+    let detail: CategoryVO | undefined = category;
+    if (!detail) {
+      const res = await getCategory(categoryId);
+      detail = res.data;
+    }
+    if (!detail) return;
+    (productForm as any).purchaseNo = (detail as any).purchaseNo || undefined;
+    (productForm as any).purchaseName = (detail as any).purchaseName || undefined;
+    (productForm as any).purchaseManagerNo = (detail as any).purchaseManagerNo || undefined;
+    (productForm as any).purchaseManagerName = (detail as any).purchaseManagerName || undefined;
+
+    // 联动产品经理下拉:通过 purchaseManagerNo 匹配 staffCode
+    if ((detail as any).purchaseManagerNo) {
+      const matchedManager = staffOptions.value.find((s) => s.staffCode === (detail as any).purchaseManagerNo);
+      if (matchedManager) {
+        productForm.productNature = String(matchedManager.staffId);
+      }
+    }
+    // 联动采购人员下拉:通过 purchaseNo 匹配 staffCode
+    if ((detail as any).purchaseNo) {
+      const matchedPurchase = staffOptions.value.find((s) => s.staffCode === (detail as any).purchaseNo);
+      if (matchedPurchase) {
+        productForm.purchasingPersonnel = String(matchedPurchase.staffId);
+      }
+    }
+  } catch (e) {
+    console.error('获取三级分类采购信息失败:', e);
+  }
+};
+
 // 选择三级分类搜索结果后,自动在树中定位
 const handleLevel3SearchSelect = async (categoryId: string | number) => {
   if (!categoryId) return;
@@ -1123,10 +1234,12 @@ const handleLevel3SearchSelect = async (categoryId: string | number) => {
   if (level3Node) {
     categoryForm.bottomCategoryId = level3Node.id;
     selectedLevel3Name.value = level3Node.label;
+    await fillPurchaseFromCategory(level3Node.id, selectedCategory);
     await loadCategoryAttributes(level3Node.id);
   } else {
     categoryForm.bottomCategoryId = selectedCategory.id;
     selectedLevel3Name.value = selectedCategory.categoryName;
+    await fillPurchaseFromCategory(selectedCategory.id, selectedCategory);
     await loadCategoryAttributes(selectedCategory.id);
   }
 
@@ -1176,6 +1289,9 @@ const selectLevel3 = async (item: categoryTreeVO) => {
   categoryForm.bottomCategoryId = item.id;
   selectedLevel3Name.value = item.label;
 
+  // 联动填充产品经理与采购人员(从三级分类详情获取)
+  await fillPurchaseFromCategory(item.id);
+
   // 加载该分类下的属性列表
   await loadCategoryAttributes(item.id);
 };
@@ -1257,7 +1373,7 @@ const handleSubmit = async () => {
       submitLoading.value = false;
       return;
     }
-    if (!carouselImages.value || carouselImages.value.length === 0) {
+    if (!carouselImages.value) {
       ElMessage.warning('请上传商品轮播图');
       submitLoading.value = false;
       return;
@@ -1268,18 +1384,30 @@ const handleSubmit = async () => {
       return;
     }
 
-    // 校验价格关系:市场价 > 官网价 > 最低售价
-    const midRange = parseFloat(String(productForm.marketPrice));
-    const standard = parseFloat(String(productForm.memberPrice));
-    const certificate = parseFloat(String(productForm.minSellingPrice));
-    if (!isNaN(midRange) && !isNaN(standard) && !isNaN(certificate)) {
-      if (!(midRange > standard)) {
-        ElMessage.warning('市场价必须大于官网价');
+    // 校验价格关系:供应价 < 官网价 < 市场价(任一字段为空时跳过对应校验)
+    const marketPriceNum = parseFloat(String(productForm.marketPrice));
+    const memberPriceNum = parseFloat(String(productForm.memberPrice));
+    const supplyPriceNum = parseFloat(String(productForm.supplyPrice));
+    // 官网价 < 市场价
+    if (!isNaN(memberPriceNum) && !isNaN(marketPriceNum)) {
+      if (!(memberPriceNum < marketPriceNum)) {
+        ElMessage.warning('官网价必须小于市场价');
         submitLoading.value = false;
         return;
       }
-      if (!(standard > certificate)) {
-        ElMessage.warning('官网价必须大于最低售价');
+    }
+    // 供应价 < 官网价
+    if (!isNaN(supplyPriceNum) && !isNaN(memberPriceNum)) {
+      if (!(supplyPriceNum < memberPriceNum)) {
+        ElMessage.warning('供应价必须小于官网价');
+        submitLoading.value = false;
+        return;
+      }
+    }
+    // 供应价 < 市场价(当官网价为空但供应价与市场价都存在时也校验)
+    if (!isNaN(supplyPriceNum) && !isNaN(marketPriceNum)) {
+      if (!(supplyPriceNum < marketPriceNum)) {
+        ElMessage.warning('供应价必须小于市场价');
         submitLoading.value = false;
         return;
       }
@@ -1290,8 +1418,8 @@ const handleSubmit = async () => {
       ...productForm,
       // 将服务保障ID数组转换为逗号分隔字符串
       serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
-      // 轮播图URL逗号分隔
-      imageUrl: carouselImages.value.join(','),
+      // 轮播图(ImageUpload 已是逗号分隔字符串)
+      imageUrl: carouselImages.value,
       // 将商品属性值转换为JSON字符串
       attributesList: JSON.stringify(productAttributesValues.value),
       isCustomize: customForm.isCustomize ? 1 : 0,
@@ -1343,6 +1471,41 @@ const handleUpcInput = () => {
   }
 };
 
+// 商品重量只允许数字和小数点(过滤中文及其他非法字符)
+const handleWeightInput = (val: string) => {
+  if (val !== undefined && val !== null) {
+    let v = String(val).replace(/[^\d.]/g, '');
+    // 只保留第一个小数点
+    const firstDot = v.indexOf('.');
+    if (firstDot !== -1) {
+      v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '');
+    }
+    productForm.productWeight = v;
+  }
+};
+
+// 商品体积只允许数字和小数点
+const handleVolumeInput = (val: string) => {
+  if (val !== undefined && val !== null) {
+    let v = String(val).replace(/[^\d.]/g, '');
+    const firstDot = v.indexOf('.');
+    if (firstDot !== -1) {
+      v = v.slice(0, firstDot + 1) + v.slice(firstDot + 1).replace(/\./g, '');
+    }
+    productForm.productVolume = v;
+  }
+};
+
+// 参考链接过滤中文空格(全角空格 U+3000)
+const handleReferenceLinkInput = (val: string) => {
+  if (val !== undefined && val !== null) {
+    const filtered = String(val).replace(/\u3000/g, '');
+    if (filtered !== val) {
+      productForm.referenceLink = filtered;
+    }
+  }
+};
+
 // 销量人气只允许输入整数
 const handleSalesVolumeInput = (val: string) => {
   if (val !== undefined && val !== null && val !== '') {
@@ -1376,6 +1539,44 @@ const formatPrice = (field: string) => {
   }
 };
 
+// 价格失焦校验:供应价 < 官网价 < 市场价
+const handlePriceBlur = (field: string) => {
+  formatPrice(field);
+  const supplyPrice = parseFloat(String(productForm.supplyPrice));
+  const memberPrice = parseFloat(String(productForm.memberPrice));
+  const marketPrice = parseFloat(String(productForm.marketPrice));
+
+  if (field === 'supplyPrice' && !isNaN(supplyPrice) && !isNaN(memberPrice) && supplyPrice >= memberPrice) {
+    ElMessage.warning('供应价必须小于官网价');
+  }
+  if (field === 'memberPrice') {
+    if (!isNaN(memberPrice) && !isNaN(supplyPrice) && memberPrice <= supplyPrice) {
+      ElMessage.warning('官网价必须大于供应价');
+    }
+    if (!isNaN(memberPrice) && !isNaN(marketPrice) && memberPrice >= marketPrice) {
+      ElMessage.warning('官网价必须小于市场价');
+    }
+  }
+  if (field === 'marketPrice' && !isNaN(marketPrice) && !isNaN(memberPrice) && marketPrice <= memberPrice) {
+    ElMessage.warning('市场价必须大于官网价');
+  }
+};
+
+// 根据供应天数自动计算到期时间
+const calcSupplyExpireTime = () => {
+  const days = parseInt(String(productForm.supplyDays));
+  if (!isNaN(days) && days > 0) {
+    const now = new Date();
+    now.setDate(now.getDate() + days);
+    const y = now.getFullYear();
+    const m = String(now.getMonth() + 1).padStart(2, '0');
+    const d = String(now.getDate()).padStart(2, '0');
+    productForm.supplyValidityPeriod = `${y}-${m}-${d}`;
+  } else {
+    productForm.supplyValidityPeriod = undefined;
+  }
+};
+
 // 格式化表格行中的价格为两位小数(不允许负数)
 const formatRowPrice = (row: any, field: string) => {
   const val = row[field];
@@ -1512,16 +1713,6 @@ const getStaffOptions = async () => {
   }
 };
 
-// 获取税率列表
-const getTaxRateOptions = async () => {
-  try {
-    const res = await getTaxRateList();
-    taxRateOptions.value = res.rows || [];
-  } catch (error) {
-    console.error('获取税率列表失败:', error);
-  }
-};
-
 // 加载分类属性列表
 const loadCategoryAttributes = async (categoryId: string | number) => {
   try {
@@ -1608,16 +1799,9 @@ const loadProductDetail = async () => {
         }
       }
 
-      // 回显税率 - 在税率选项中查找匹配的值(处理浮点数精度问题
+      // 回显税率(直接使用接口返回值,由税率编码带出,无需下拉匹配
       if (res.data.taxRate !== undefined && res.data.taxRate !== null) {
-        const apiTaxRate = Number(res.data.taxRate);
-        // 使用精度容差比较浮点数
-        const matchedOption = taxRateOptions.value.find((opt) => Math.abs(Number(opt.taxrate) - apiTaxRate) < 0.0001);
-        if (matchedOption) {
-          productForm.taxRate = matchedOption.taxrate;
-        } else {
-          productForm.taxRate = apiTaxRate;
-        }
+        productForm.taxRate = Number(res.data.taxRate);
       }
 
       // 回显单位 - 确保类型与下拉选项的id一致(数字类型)
@@ -1644,15 +1828,11 @@ const loadProductDetail = async () => {
 
       // 回显售后服务 - 确保类型与下拉选项的id一致(数字类型)
       if (res.data.afterSalesService !== undefined && res.data.afterSalesService !== null) {
-        productForm.afterSalesService = String(res.data.afterSalesService);
+        productForm.afterSalesService = Number(res.data.afterSalesService);
       }
 
       // 回显轮播图
-      if (res.data.imageUrl) {
-        carouselImages.value = res.data.imageUrl.split(',').filter((url: string) => url.trim());
-      } else {
-        carouselImages.value = [];
-      }
+      carouselImages.value = res.data.imageUrl || '';
 
       // 回显分类选择
       categoryForm.topCategoryId = res.data.topCategoryId;
@@ -1797,7 +1977,6 @@ onMounted(async () => {
   await getUnitOptions();
   await getAfterSalesOptions();
   await getServiceGuaranteeOptions();
-  await getTaxRateOptions();
   // 先加载商品详情(如果是编辑模式)
   await loadProductDetail();
   // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个

+ 2 - 2
src/views/product/base/index.vue

@@ -174,7 +174,7 @@
 <!--                <span>{{ scope.row.totalInventory ?? '-' }}</span>-->
 <!--              </div>-->
 <!--              <div>-->
-<!--                <span class="text-gray-500">可用库存:</span>-->
+<!--                <span class="text-gray-500">现有库存:</span>-->
 <!--                <span>{{ scope.row.nowInventory ?? '-' }}</span>-->
 <!--              </div>-->
 <!--              <div>-->
@@ -678,7 +678,7 @@ const inventoryForm = reactive<PriceInventoryForm>({
 
 const inventoryRules = {
   totalInventory: [{ required: true, message: '总库存不能为空', trigger: 'blur' }],
-  nowInventory: [{ required: true, message: '当前可用库存不能为空', trigger: 'blur' }],
+  nowInventory: [{ required: true, message: '当前现有库存不能为空', trigger: 'blur' }],
   virtualInventory: [{ required: true, message: '虚拟库存不能为空', trigger: 'blur' }]
 };
 

+ 241 - 62
src/views/product/baseAudit/add.vue

@@ -174,6 +174,18 @@
               <div class="form-item-tip">A10产品名称由系统自动拼接:品牌名 + 规格型号 + 产品分类(三级分类)+ 发票规格,无需手动填写</div>
             </el-form-item>
 
+                        <!-- 商品描述 -->
+            <el-form-item label="商品描述:">
+              <el-input
+                v-model="productForm.productDescription"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品描述"
+                maxlength="500"
+                show-word-limit
+              />
+            </el-form-item>
+
             <!-- 规格型号 和 UPC(69)条码 -->
             <el-row :gutter="20">
               <el-col :span="12">
@@ -255,9 +267,13 @@
               </el-col>
               <el-col :span="12">
                 <el-form-item label="税率:" required>
-                  <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
-                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
-                  </el-select>
+                  <el-input
+                    :model-value="formatTaxRateDisplay(productForm.taxRate)"
+                    placeholder="由税率编码自动带出"
+                    readonly
+                    disabled
+                    class="w-full"
+                  />
                 </el-form-item>
               </el-col>
             </el-row>
@@ -297,16 +313,29 @@
               <el-input v-model="productForm.promotionTitle" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
             </el-form-item>
 
-            <!-- 商品简介 -->
-            <el-form-item label="商品简介:">
-              <el-input v-model="productForm.productDescription" type="textarea" :rows="3" placeholder="请输入商品简介" maxlength="500" show-word-limit />
+                        <!-- 商品说明 -->
+            <el-form-item label="商品说明:">
+              <el-input
+                v-model="productForm.productExplain"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品说明"
+                maxlength="500"
+                show-word-limit
+              />
             </el-form-item>
 
             <!-- 重量 和 体积 -->
             <el-row :gutter="20">
               <el-col :span="12">
                 <el-form-item label="商品重量:">
-                  <el-input v-model="productForm.productWeight" placeholder="0" maxlength="10" show-word-limit>
+                  <el-input
+                    v-model="productForm.productWeight"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                    @input="handleNumericInput('productWeight')"
+                  >
                     <template #append>
                       <el-select v-model="productForm.weightUnit" placeholder="请选择" style="width: 100px">
                         <el-option label="kg" value="kg" />
@@ -319,7 +348,13 @@
               </el-col>
               <el-col :span="12">
                 <el-form-item label="商品体积:">
-                  <el-input v-model="productForm.productVolume" placeholder="0" maxlength="10" show-word-limit>
+                  <el-input
+                    v-model="productForm.productVolume"
+                    placeholder="0"
+                    maxlength="10"
+                    show-word-limit
+                    @input="handleNumericInput('productVolume')"
+                  >
                     <template #append>
                       <el-select v-model="productForm.volumeUnit" placeholder="请选择" style="width: 80px">
                         <el-option label="m³" value="m3" />
@@ -333,8 +368,15 @@
             </el-row>
 
             <!-- 参考链接 -->
-            <el-form-item label="参考链接">
-              <el-input v-model="productForm.referenceLink" type="textarea" :rows="3" placeholder="请输入参考链接" />
+            <el-form-item label="参考链接" prop="referenceLink" required>
+              <el-input
+                ref="referenceLinkRef"
+                v-model="productForm.referenceLink"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入参考链接(不能包含中文空格)"
+                @input="handleReferenceLinkInput"
+              />
             </el-form-item>
 
             <!-- 售后服务 -->
@@ -370,17 +412,25 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="市场价:" prop="marketPrice" required>
-                  <el-input v-model="productForm.marketPrice" type="number" placeholder="请输入市场价" :min="0" @blur="formatPrice('marketPrice')" />
+                  <el-input
+                    ref="marketPriceRef"
+                    v-model="productForm.marketPrice"
+                    type="number"
+                    placeholder="请输入市场价"
+                    :min="0"
+                    @blur="handlePriceBlur('marketPrice')"
+                  />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
                 <el-form-item label="官网价:" prop="memberPrice" required>
                   <el-input
+                    ref="memberPriceRef"
                     v-model="productForm.memberPrice"
                     type="number"
                     placeholder="请输入平台售价"
                     :min="0"
-                    @blur="formatPrice('memberPrice')"
+                    @blur="handlePriceBlur('memberPrice')"
                   />
                 </el-form-item>
               </el-col>
@@ -388,12 +438,12 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
-                  <el-input v-model="productForm.minOrderQuantity" min="1" type="number" placeholder="请输入最低起订量" />
+                  <el-input v-model="productForm.minOrderQuantity" type="number" placeholder="请输入最低起订量" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
                 <el-form-item label="备注:">
-                  <span class="currency-text">市场价>官网价</span>
+                  <span class="currency-text">供应价 &lt; 官网价 &lt; 市场价</span>
                 </el-form-item>
               </el-col>
             </el-row>
@@ -411,30 +461,38 @@
               <el-col :span="8">
                 <el-form-item label="供应价:">
                   <el-input
+                    ref="supplyPriceRef"
                     v-model="productForm.supplyPrice"
                     type="number"
                     placeholder="请输入供应价"
                     :min="0"
-                    @blur="formatPrice('supplyPrice')"
+                    @blur="handlePriceBlur('supplyPrice')"
                   />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="供应有效时间:">
-                  <el-date-picker
+                <el-form-item label="供应时间(天):">
+                  <el-input
                     v-model="productForm.supplyValidityPeriod"
-                    type="date"
-                    placeholder="请选择供应有效时间"
-                    value-format="YYYY-MM-DD"
-                    class="w-full"
+                    type="number"
+                    placeholder="请输入供应天数"
+                    :min="0"
+                    @input="calcSupplyExpiryDate"
                   />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
-                <el-form-item label="是否包邮:">
+                <el-form-item label="到期时间:">
+                  <el-input v-model="supplyExpiryDate" disabled placeholder="自动计算" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="一件代发:">
                   <el-radio-group v-model="productForm.supplyPostStatus">
-                    <el-radio :value="0">不包邮</el-radio>
-                    <el-radio :value="1">包邮</el-radio>
+                    <el-radio :value="0">不支持</el-radio>
+                    <el-radio :value="1">支持</el-radio>
                   </el-radio-group>
                 </el-form-item>
               </el-col>
@@ -539,13 +597,13 @@
           <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
             <!-- 商品主图 -->
             <el-form-item label="商品主图:" required>
-              <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
+              <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="" />
               <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品轮播图 -->
             <el-form-item label="商品轮播图:" required>
-              <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
+              <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="" />
               <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
             </el-form-item>
 
@@ -710,8 +768,7 @@ import {
   categoryAttributeList,
   getAfterSaleList,
   getServiceList,
-  getUnitList,
-  getTaxRateList
+  getUnitList
 } from '@/api/product/base';
 import {
   addBaseAudit,
@@ -734,18 +791,30 @@ const loading = ref(false);
 const submitLoading = ref(false);
 const productFormRef = ref();
 
+// 价格与参考链接输入框引用,用于校验失败时聚焦
+const marketPriceRef = ref();
+const memberPriceRef = ref();
+const supplyPriceRef = ref();
+const referenceLinkRef = ref();
+
 // 服务保障和安装服务的多选框
 const serviceGuarantees = ref<(string | number)[]>([]);
 const installationServices = ref<string[]>([]);
 
+// 供应到期时间(根据供应天数自动计算)
+const supplyExpiryDate = ref('');
+
 // 商品详情选项卡
 const activeDetailTab = ref('pc');
 
 // 轮播图URL数组(UI管理用)
 const carouselImages = ref<string[]>([]);
 
-// 税率选项
-const taxRateOptions = ref<any[]>([]);
+// 税率显示格式化
+const formatTaxRateDisplay = (val: any) => {
+  if (val === undefined || val === null || val === '') return '';
+  return `${(Number(val) * 100).toFixed(0)}%`;
+};
 
 // 税率编码选择组件
 const taxCodeSelectRef = ref();
@@ -759,12 +828,16 @@ const handleTaxCodeSelect = async (row: any) => {
     const taxRes = await getTaxCode(row.id);
     if (taxRes.data) {
       taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+      // 自动带出税率(只读,不可手动修改)
+      productForm.taxRate = (taxRes.data as any).taxrate;
     } else {
       taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+      productForm.taxRate = row.taxrate;
     }
   } catch (e) {
     console.error('获取税率编码详情失败:', e);
     taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+    productForm.taxRate = row.taxrate;
   }
   // 同时将显示值存入 form,方便编辑回显时直接读取
   (productForm as any).taxationNo = taxCodeNo.value;
@@ -963,10 +1036,24 @@ const productRules = {
   // productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
   itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
   brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
+  referenceLink: [
+    { required: true, message: '参考链接不能为空', trigger: 'blur' },
+    {
+      validator: (_rule: any, value: string, callback: any) => {
+        if (value && /[\u3000\u4e00-\u9fa5]/.test(value)) {
+          callback(new Error('参考链接不能包含中文或中文空格'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur'
+    }
+  ],
   marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
   memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
   minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
-  // purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  maxPurchasePrice: [{ required: true, message: '最高采购价不能为空', trigger: 'blur' }],
   productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
   purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
   taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
@@ -1248,23 +1335,68 @@ const handleSubmit = async () => {
       return;
     }
 
-    // 校验价格关系:市场价 > 官网价 > 最低售价
-    const midRange = parseFloat(String(productForm.marketPrice));
-    const standard = parseFloat(String(productForm.memberPrice));
-    const certificate = parseFloat(String(productForm.minSellingPrice));
-    if (!isNaN(midRange) && !isNaN(standard) && !isNaN(certificate)) {
-      if (!(midRange > standard)) {
-        ElMessage.warning('市场价必须大于官网价');
+    // 校验价格链:市场价 > 官网价,且均不为 0
+    const focusInput = async (refObj: any) => {
+      await nextTick();
+      try {
+        refObj?.value?.focus?.();
+      } catch (e) {
+        // 忽略聚焦异常
+      }
+    };
+    const priceFields: Array<{ key: string; label: string; ref: any }> = [
+      { key: 'marketPrice', label: '市场价', ref: marketPriceRef },
+      { key: 'memberPrice', label: '官网价', ref: memberPriceRef }
+    ];
+    // 非空与非零校验
+    for (const f of priceFields) {
+      const raw = (productForm as any)[f.key];
+      if (raw === undefined || raw === null || raw === '') {
+        ElMessage.warning(`${f.label}不能为空`);
+        await focusInput(f.ref);
+        submitLoading.value = false;
+        return;
+      }
+      const num = parseFloat(String(raw));
+      if (isNaN(num) || num <= 0) {
+        ElMessage.warning(`${f.label}不能为 0`);
+        await focusInput(f.ref);
         submitLoading.value = false;
         return;
       }
-      if (!(standard > certificate)) {
-        ElMessage.warning('官网价必须大于最低售价');
+    }
+    const marketP = parseFloat(String(productForm.marketPrice));
+    const memberP = parseFloat(String(productForm.memberPrice));
+    if (!(marketP > memberP)) {
+      ElMessage.warning('市场价必须大于官网价');
+      await focusInput(marketPriceRef);
+      submitLoading.value = false;
+      return;
+    }
+    // 供应价校验(如果填写了)
+    const supplyPriceNum = parseFloat(String(productForm.supplyPrice));
+    if (!isNaN(supplyPriceNum) && supplyPriceNum > 0) {
+      if (!(supplyPriceNum < memberP)) {
+        ElMessage.warning('供应价必须小于官网价');
         submitLoading.value = false;
         return;
       }
     }
 
+    // 校验参考链接:必填且不能含中文或中文空格
+    if (!productForm.referenceLink || !String(productForm.referenceLink).trim()) {
+      ElMessage.warning('参考链接不能为空');
+      await focusInput(referenceLinkRef);
+      submitLoading.value = false;
+      return;
+    }
+    if (/[\u3000\u4e00-\u9fa5]/.test(String(productForm.referenceLink))) {
+      ElMessage.warning('参考链接不能包含中文或中文空格');
+      await focusInput(referenceLinkRef);
+      submitLoading.value = false;
+      return;
+    }
+
     // 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
     const submitProductData: any = {
       ...productForm,
@@ -1324,6 +1456,27 @@ const handleUpcInput = () => {
   }
 };
 
+// 参考链接输入过滤:移除中文字符与中文全角空格(U+3000)
+const handleReferenceLinkInput = () => {
+  if (productForm.referenceLink) {
+    productForm.referenceLink = productForm.referenceLink.replace(/[\u3000\u4e00-\u9fa5]/g, '');
+  }
+};
+
+// 重量/体积输入过滤:仅保留数字与小数点
+const handleNumericInput = (field: 'productWeight' | 'productVolume') => {
+  const val = productForm[field];
+  if (val !== undefined && val !== null && val !== '') {
+    let str = String(val).replace(/[^\d.]/g, '');
+    // 仅保留首个小数点
+    const firstDot = str.indexOf('.');
+    if (firstDot !== -1) {
+      str = str.slice(0, firstDot + 1) + str.slice(firstDot + 1).replace(/\./g, '');
+    }
+    productForm[field] = str;
+  }
+};
+
 // 销量人气只允许输入整数
 const handleSalesVolumeInput = (val: string) => {
   if (val !== undefined && val !== null && val !== '') {
@@ -1357,6 +1510,45 @@ const formatPrice = (field: string) => {
   }
 };
 
+// 价格链校验:供应价 < 官网价 < 市场价,失焦时触发
+const handlePriceBlur = (field: string) => {
+  formatPrice(field);
+  const supply = parseFloat(String(productForm.supplyPrice));
+  const member = parseFloat(String(productForm.memberPrice));
+  const market = parseFloat(String(productForm.marketPrice));
+
+  // 只有当对应字段有值时才校验
+  if (!isNaN(member) && member > 0 && !isNaN(market) && market > 0) {
+    if (member >= market) {
+      ElMessage.warning('官网价必须小于市场价');
+      nextTick(() => memberPriceRef.value?.focus?.());
+      return;
+    }
+  }
+  if (!isNaN(supply) && supply > 0 && !isNaN(member) && member > 0) {
+    if (supply >= member) {
+      ElMessage.warning('供应价必须小于官网价');
+      nextTick(() => supplyPriceRef.value?.focus?.());
+      return;
+    }
+  }
+};
+
+// 计算供应到期时间
+const calcSupplyExpiryDate = () => {
+  const days = parseInt(String(productForm.supplyValidityPeriod));
+  if (!isNaN(days) && days > 0) {
+    const now = new Date();
+    now.setDate(now.getDate() + days);
+    const y = now.getFullYear();
+    const m = String(now.getMonth() + 1).padStart(2, '0');
+    const d = String(now.getDate()).padStart(2, '0');
+    supplyExpiryDate.value = `${y}-${m}-${d}`;
+  } else {
+    supplyExpiryDate.value = '';
+  }
+};
+
 // 格式化表格行中的价格为两位小数(不允许负数)
 const formatRowPrice = (row: any, field: string) => {
   const val = row[field];
@@ -1452,6 +1644,10 @@ const getUnitOptions = async () => {
   try {
     const res = await getUnitList();
     unitOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && unitOptions.value.length > 0 && !productForm.unitId) {
+      productForm.unitId = unitOptions.value[0].id;
+    }
   } catch (error) {
     console.error('获取单位列表失败:', error);
   }
@@ -1493,16 +1689,6 @@ const getStaffOptions = async () => {
   }
 };
 
-// 获取税率列表
-const getTaxRateOptions = async () => {
-  try {
-    const res = await getTaxRateList();
-    taxRateOptions.value = res.rows || [];
-  } catch (error) {
-    console.error('获取税率列表失败:', error);
-  }
-};
-
 // 加载分类属性列表
 const loadCategoryAttributes = async (categoryId: string | number) => {
   try {
@@ -1518,13 +1704,13 @@ const loadCategoryAttributes = async (categoryId: string | number) => {
           // 下拉选择
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.productAttributesName] = options[0];
+            productAttributesValues.value[attr.id] = options[0];
           }
         } else if (attr.entryMethod === '3' && attr.attributesList) {
           // 多选
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.productAttributesName] = [options[0]];
+            productAttributesValues.value[attr.id] = [options[0]];
           }
         }
       });
@@ -1589,16 +1775,9 @@ const loadProductDetail = async () => {
         }
       }
 
-      // 回显税率 - 在税率选项中查找匹配的值(处理浮点数精度问题)
+      // 回显税率 - 直接设置数值
       if (res.data.productBaseVo.taxRate !== undefined && res.data.productBaseVo.taxRate !== null) {
-        const apiTaxRate = Number(res.data.productBaseVo.taxRate);
-        // 使用精度容差比较浮点数
-        const matchedOption = taxRateOptions.value.find((opt) => Math.abs(Number(opt.taxrate) - apiTaxRate) < 0.0001);
-        if (matchedOption) {
-          productForm.taxRate = matchedOption.taxrate;
-        } else {
-          productForm.taxRate = apiTaxRate;
-        }
+        productForm.taxRate = Number(res.data.productBaseVo.taxRate);
       }
 
       // 回显单位 - 确保类型与下拉选项的id一致(数字类型)
@@ -1815,7 +1994,7 @@ onMounted(async () => {
   await getUnitOptions();
   await getAfterSalesOptions();
   await getServiceGuaranteeOptions();
-  await getTaxRateOptions();
+
   // 先加载商品详情(如果是编辑模式)
   await loadProductDetail();
   // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个

+ 5 - 5
src/views/product/priceInventory/index.vue

@@ -22,8 +22,8 @@
             <el-form-item label="总库存量" prop="totalInventory">
               <el-input v-model="queryParams.totalInventory" placeholder="请输入总库存量" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="当前可用库存" prop="nowInventory">
-              <el-input v-model="queryParams.nowInventory" placeholder="请输入当前可用库存" clearable @keyup.enter="handleQuery" />
+            <el-form-item label="当前现有库存" prop="nowInventory">
+              <el-input v-model="queryParams.nowInventory" placeholder="请输入当前现有库存" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="虚拟库存" prop="virtualInventory">
               <el-input v-model="queryParams.virtualInventory" placeholder="请输入虚拟库存" clearable @keyup.enter="handleQuery" />
@@ -77,7 +77,7 @@
         <el-table-column label="采购价格" align="center" prop="purchasingPrice" />
         <el-table-column label="最高采购价格" align="center" prop="maxPurchasePrice" />
         <el-table-column label="总库存量" align="center" prop="totalInventory" />
-        <el-table-column label="当前可用库存" align="center" prop="nowInventory" />
+        <el-table-column label="当前现有库存" align="center" prop="nowInventory" />
         <el-table-column label="虚拟库存" align="center" prop="virtualInventory" />
         <el-table-column label="最小起订数量" align="center" prop="minOrderQuantity" />
         <el-table-column label="税率" align="center" prop="taxRate" />
@@ -118,8 +118,8 @@
         <el-form-item label="总库存量" prop="totalInventory">
           <el-input v-model="form.totalInventory" placeholder="请输入总库存量" />
         </el-form-item>
-        <el-form-item label="当前可用库存" prop="nowInventory">
-          <el-input v-model="form.nowInventory" placeholder="请输入当前可用库存" />
+        <el-form-item label="当前现有库存" prop="nowInventory">
+          <el-input v-model="form.nowInventory" placeholder="请输入当前现有库存" />
         </el-form-item>
         <el-form-item label="虚拟库存" prop="virtualInventory">
           <el-input v-model="form.virtualInventory" placeholder="请输入虚拟库存" />