|
|
@@ -188,6 +188,19 @@
|
|
|
<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">
|
|
|
@@ -253,7 +266,7 @@
|
|
|
<!-- 税率编码 、税率 和 币种 -->
|
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="税率编码:">
|
|
|
+ <el-form-item label="税率编码:" required>
|
|
|
<el-input
|
|
|
v-model="taxCodeNo"
|
|
|
placeholder="点击选择税率编码"
|
|
|
@@ -270,9 +283,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>
|
|
|
@@ -312,13 +323,14 @@
|
|
|
<el-input v-model="productForm.promotionTitle" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
|
|
|
</el-form-item>
|
|
|
|
|
|
- <!-- 商品简介 -->
|
|
|
- <el-form-item label="商品简介:">
|
|
|
+
|
|
|
+ <!-- 商品说明 -->
|
|
|
+ <el-form-item label="商品说明:">
|
|
|
<el-input
|
|
|
- v-model="productForm.productDescription"
|
|
|
+ v-model="productForm.productExplain"
|
|
|
type="textarea"
|
|
|
:rows="3"
|
|
|
- placeholder="请输入商品简介"
|
|
|
+ placeholder="请输入商品说明"
|
|
|
maxlength="500"
|
|
|
show-word-limit
|
|
|
/>
|
|
|
@@ -328,7 +340,13 @@
|
|
|
<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" />
|
|
|
@@ -341,7 +359,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" />
|
|
|
@@ -355,8 +379,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>
|
|
|
|
|
|
<!-- 主供应商 -->
|
|
|
@@ -404,12 +435,20 @@
|
|
|
<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="formatPrice('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="请输入平台售价"
|
|
|
@@ -421,6 +460,7 @@
|
|
|
<el-col :span="8">
|
|
|
<el-form-item label="最低售价:" prop="minSellingPrice" required>
|
|
|
<el-input
|
|
|
+ ref="minSellingPriceRef"
|
|
|
v-model="productForm.minSellingPrice"
|
|
|
type="number"
|
|
|
placeholder="请输入最低售价"
|
|
|
@@ -438,7 +478,7 @@
|
|
|
</el-col>
|
|
|
<el-col :span="8">
|
|
|
<el-form-item label="备注:">
|
|
|
- <span class="currency-text">市场价>官网价>最低售价</span>
|
|
|
+ <span class="currency-text">市场价>官网价≥最低售价≥最高采购价≥采购价</span>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
@@ -456,6 +496,7 @@
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="采购价:" prop="purchasingPrice" required>
|
|
|
<el-input
|
|
|
+ ref="purchasingPriceRef"
|
|
|
v-model="productForm.purchasingPrice"
|
|
|
type="number"
|
|
|
placeholder="请输入采购价"
|
|
|
@@ -465,8 +506,9 @@
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="最高采购价:">
|
|
|
+ <el-form-item label="最高采购价:" prop="maxPurchasePrice" required>
|
|
|
<el-input
|
|
|
+ ref="maxPurchasePriceRef"
|
|
|
v-model="productForm.maxPurchasePrice"
|
|
|
type="number"
|
|
|
placeholder="请输入最高采购价"
|
|
|
@@ -612,14 +654,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>
|
|
|
|
|
|
<!-- 商品详情 -->
|
|
|
@@ -628,9 +670,6 @@
|
|
|
<el-tab-pane label="电脑端详情" name="pc">
|
|
|
<Editor v-model="productForm.pcDetail" :height="400" />
|
|
|
</el-tab-pane>
|
|
|
- <el-tab-pane label="移动端详情" name="mobile">
|
|
|
- <Editor v-model="productForm.mobileDetail" :height="400" />
|
|
|
- </el-tab-pane>
|
|
|
</el-tabs>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
@@ -767,9 +806,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';
|
|
|
@@ -783,8 +822,7 @@ import {
|
|
|
categoryAttributeList,
|
|
|
getAfterSaleList,
|
|
|
getServiceList,
|
|
|
- getUnitList,
|
|
|
- getTaxRateList
|
|
|
+ getUnitList
|
|
|
} from '@/api/product/base';
|
|
|
import { addBaseAudit, updateBaseAudit, getBaseAudit } from '@/api/product/baseAudit';
|
|
|
import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
|
|
|
@@ -803,6 +841,14 @@ const loading = ref(false);
|
|
|
const submitLoading = ref(false);
|
|
|
const productFormRef = ref();
|
|
|
|
|
|
+// 关键输入框 refs,用于校验失败时聚焦
|
|
|
+const referenceLinkRef = ref();
|
|
|
+const marketPriceRef = ref();
|
|
|
+const memberPriceRef = ref();
|
|
|
+const minSellingPriceRef = ref();
|
|
|
+const maxPurchasePriceRef = ref();
|
|
|
+const purchasingPriceRef = ref();
|
|
|
+
|
|
|
// 服务保障和安装服务的多选框
|
|
|
const serviceGuarantees = ref<(string | number)[]>([]);
|
|
|
const installationServices = ref<string[]>([]);
|
|
|
@@ -810,24 +856,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.taxrate !== undefined && taxRes.data.taxrate !== null) {
|
|
|
+ productForm.taxRate = Number(taxRes.data.taxrate);
|
|
|
+ }
|
|
|
} else {
|
|
|
taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
|
|
|
}
|
|
|
@@ -1013,8 +1072,12 @@ const productForm = reactive<BaseForm>({
|
|
|
minSellingPrice: undefined,
|
|
|
purchasingPrice: undefined,
|
|
|
maxPurchasePrice: undefined,
|
|
|
- productNature: '1',
|
|
|
- purchasingPersonnel: '1',
|
|
|
+ productNature: '',
|
|
|
+ purchasingPersonnel: '',
|
|
|
+ purchaseNo: undefined,
|
|
|
+ purchaseName: undefined,
|
|
|
+ purchaseManagerNo: undefined,
|
|
|
+ purchaseManagerName: undefined,
|
|
|
pcDetail: undefined,
|
|
|
mobileDetail: undefined,
|
|
|
taxRate: undefined,
|
|
|
@@ -1030,9 +1093,23 @@ const productRules = {
|
|
|
brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
|
|
|
supplierNo: [{ 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' }],
|
|
|
+ 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' }],
|
|
|
@@ -1169,10 +1246,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);
|
|
|
}
|
|
|
|
|
|
@@ -1222,10 +1301,46 @@ const selectLevel3 = async (item: categoryTreeVO) => {
|
|
|
categoryForm.bottomCategoryId = item.id;
|
|
|
selectedLevel3Name.value = item.label;
|
|
|
|
|
|
+ // 联动填充产品经理与采购人员(从三级分类详情获取)
|
|
|
+ await fillPurchaseFromCategory(item.id);
|
|
|
+
|
|
|
// 加载该分类下的属性列表
|
|
|
await loadCategoryAttributes(item.id);
|
|
|
};
|
|
|
|
|
|
+// 从三级分类联动填充采购信息
|
|
|
+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.purchaseNo = detail.purchaseNo || undefined;
|
|
|
+ productForm.purchaseName = detail.purchaseName || undefined;
|
|
|
+ productForm.purchaseManagerNo = detail.purchaseManagerNo || undefined;
|
|
|
+ productForm.purchaseManagerName = detail.purchaseManagerName || undefined;
|
|
|
+
|
|
|
+ // 联动产品经理下拉:通过 purchaseManagerNo 匹配 staffCode
|
|
|
+ if (detail.purchaseManagerNo) {
|
|
|
+ const matchedManager = staffOptions.value.find((s) => s.staffCode === detail!.purchaseManagerNo);
|
|
|
+ if (matchedManager) {
|
|
|
+ productForm.productNature = String(matchedManager.staffId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 联动采购人员下拉:通过 purchaseNo 匹配 staffCode
|
|
|
+ if (detail.purchaseNo) {
|
|
|
+ const matchedPurchase = staffOptions.value.find((s) => s.staffCode === detail!.purchaseNo);
|
|
|
+ if (matchedPurchase) {
|
|
|
+ productForm.purchasingPersonnel = String(matchedPurchase.staffId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('获取三级分类采购信息失败:', e);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 获取分类路径
|
|
|
const getCategoryPath = () => {
|
|
|
const parts = [];
|
|
|
@@ -1303,7 +1418,7 @@ const handleSubmit = async () => {
|
|
|
submitLoading.value = false;
|
|
|
return;
|
|
|
}
|
|
|
- if (!carouselImages.value || carouselImages.value.length === 0) {
|
|
|
+ if (!carouselImages.value) {
|
|
|
ElMessage.warning('请上传商品轮播图');
|
|
|
submitLoading.value = false;
|
|
|
return;
|
|
|
@@ -1314,21 +1429,88 @@ 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('市场价必须大于官网价');
|
|
|
- submitLoading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
- if (!(standard > certificate)) {
|
|
|
- ElMessage.warning('官网价必须大于最低售价');
|
|
|
- submitLoading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
+ // 校验价格关系:市场价 > 官网价 ≥ 最低售价 ≥ 最高采购价 ≥ 采购价,且均不能为 0
|
|
|
+ const market = parseFloat(String(productForm.marketPrice ?? ''));
|
|
|
+ const member = parseFloat(String(productForm.memberPrice ?? ''));
|
|
|
+ const minSell = parseFloat(String(productForm.minSellingPrice ?? ''));
|
|
|
+ const maxPurch = parseFloat(String(productForm.maxPurchasePrice ?? ''));
|
|
|
+ const purch = parseFloat(String(productForm.purchasingPrice ?? ''));
|
|
|
+
|
|
|
+ const focusField = (refObj: any) => {
|
|
|
+ nextTick(() => {
|
|
|
+ refObj.value?.focus?.();
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 均必须大于 0
|
|
|
+ if (isNaN(market) || market <= 0) {
|
|
|
+ ElMessage.warning('市场价必须大于 0');
|
|
|
+ focusField(marketPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isNaN(member) || member <= 0) {
|
|
|
+ ElMessage.warning('官网价必须大于 0');
|
|
|
+ focusField(memberPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isNaN(minSell) || minSell <= 0) {
|
|
|
+ ElMessage.warning('最低售价必须大于 0');
|
|
|
+ focusField(minSellingPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isNaN(maxPurch) || maxPurch <= 0) {
|
|
|
+ ElMessage.warning('最高采购价必须大于 0');
|
|
|
+ focusField(maxPurchasePriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (isNaN(purch) || purch <= 0) {
|
|
|
+ ElMessage.warning('采购价必须大于 0');
|
|
|
+ focusField(purchasingPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 链式大小校验
|
|
|
+ if (!(market > member)) {
|
|
|
+ ElMessage.warning('市场价必须大于官网价');
|
|
|
+ focusField(marketPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!(member >= minSell)) {
|
|
|
+ ElMessage.warning('官网价必须大于等于最低售价');
|
|
|
+ focusField(minSellingPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!(minSell >= maxPurch)) {
|
|
|
+ ElMessage.warning('最低售价必须大于等于最高采购价');
|
|
|
+ focusField(maxPurchasePriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!(maxPurch >= purch)) {
|
|
|
+ ElMessage.warning('最高采购价必须大于等于采购价');
|
|
|
+ focusField(purchasingPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 采购类价格需低于官网价
|
|
|
+ if (!(maxPurch < member)) {
|
|
|
+ ElMessage.warning('最高采购价需低于官网价');
|
|
|
+ focusField(maxPurchasePriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!(purch < member)) {
|
|
|
+ ElMessage.warning('采购价需低于官网价');
|
|
|
+ focusField(purchasingPriceRef);
|
|
|
+ submitLoading.value = false;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
// 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
|
|
|
@@ -1336,8 +1518,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,
|
|
|
@@ -1389,6 +1571,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 !== '') {
|
|
|
@@ -1558,16 +1775,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 {
|
|
|
@@ -1654,16 +1861,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一致(数字类型)
|
|
|
@@ -1694,11 +1894,7 @@ const loadProductDetail = async () => {
|
|
|
}
|
|
|
|
|
|
// 回显轮播图
|
|
|
- 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;
|
|
|
@@ -1843,7 +2039,6 @@ onMounted(async () => {
|
|
|
await getUnitOptions();
|
|
|
await getAfterSalesOptions();
|
|
|
await getServiceGuaranteeOptions();
|
|
|
- await getTaxRateOptions();
|
|
|
// 先加载商品详情(如果是编辑模式)
|
|
|
await loadProductDetail();
|
|
|
// 再加载下拉选项,这样如果详情中没有值,会自动设置第一个
|