|
@@ -247,7 +247,7 @@
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="单位:">
|
|
|
|
|
|
|
+ <el-form-item label="单位:" disabled>
|
|
|
<el-select
|
|
<el-select
|
|
|
v-model="productForm.unitId"
|
|
v-model="productForm.unitId"
|
|
|
placeholder="请选择"
|
|
placeholder="请选择"
|
|
@@ -535,8 +535,8 @@
|
|
|
<el-form ref="purchaseInfoFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
|
|
<el-form ref="purchaseInfoFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="产品经理:" prop="productNature" required>
|
|
|
|
|
- <el-select v-model="productForm.productNature" placeholder="请选择" clearable class="w-full" value-key="staffId">
|
|
|
|
|
|
|
+ <el-form-item label="产品经理:" prop="purchaseManagerNo" required>
|
|
|
|
|
+ <el-select v-model="productForm.purchaseManagerNo" placeholder="请选择" clearable class="w-full" value-key="staffId">
|
|
|
<el-option
|
|
<el-option
|
|
|
v-for="option in staffOptions"
|
|
v-for="option in staffOptions"
|
|
|
:key="option.staffId"
|
|
:key="option.staffId"
|
|
@@ -547,8 +547,8 @@
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="采购人员:" prop="purchasingPersonnel" required>
|
|
|
|
|
- <el-select v-model="productForm.purchasingPersonnel" placeholder="请选择" clearable class="w-full" value-key="staffId">
|
|
|
|
|
|
|
+ <el-form-item label="采购人员:" prop="purchaseNo" required>
|
|
|
|
|
+ <el-select v-model="productForm.purchaseNo" placeholder="请选择" clearable class="w-full" value-key="staffId">
|
|
|
<el-option
|
|
<el-option
|
|
|
v-for="option in staffOptions"
|
|
v-for="option in staffOptions"
|
|
|
:key="option.staffId"
|
|
:key="option.staffId"
|
|
@@ -659,13 +659,13 @@
|
|
|
<el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
|
|
<el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
|
|
|
<!-- 商品主图 -->
|
|
<!-- 商品主图 -->
|
|
|
<el-form-item label="商品主图:" required>
|
|
<el-form-item label="商品主图:" required>
|
|
|
- <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
|
|
|
|
|
|
|
+ <image-upload v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
|
|
|
<div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
|
|
<div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<!-- 商品轮播图 -->
|
|
<!-- 商品轮播图 -->
|
|
|
<el-form-item label="商品轮播图:" required>
|
|
<el-form-item label="商品轮播图:" required>
|
|
|
- <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
|
|
|
|
|
|
|
+ <image-upload v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
|
|
|
<div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
|
|
<div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
|
|
@@ -834,6 +834,7 @@ import {
|
|
|
updateBaseAudit,
|
|
updateBaseAudit,
|
|
|
getBaseAudit,
|
|
getBaseAudit,
|
|
|
} from '@/api/product/baseAudit';
|
|
} from '@/api/product/baseAudit';
|
|
|
|
|
+import { listByIds } from '@/api/system/oss';
|
|
|
import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
|
|
import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
|
|
|
import { getTaxCode } from '@/api/system/taxCode';
|
|
import { getTaxCode } from '@/api/system/taxCode';
|
|
|
import { listBrand, getBrand } from '@/api/product/brand';
|
|
import { listBrand, getBrand } from '@/api/product/brand';
|
|
@@ -1076,8 +1077,8 @@ const productForm = reactive<BaseForm>({
|
|
|
minSellingPrice: undefined,
|
|
minSellingPrice: undefined,
|
|
|
purchasingPrice: undefined,
|
|
purchasingPrice: undefined,
|
|
|
maxPurchasePrice: undefined,
|
|
maxPurchasePrice: undefined,
|
|
|
- productNature: '1',
|
|
|
|
|
- purchasingPersonnel: '1',
|
|
|
|
|
|
|
+ purchaseManagerNo: undefined,
|
|
|
|
|
+ purchaseNo: undefined,
|
|
|
pcDetail: undefined,
|
|
pcDetail: undefined,
|
|
|
mobileDetail: undefined,
|
|
mobileDetail: undefined,
|
|
|
taxRate: undefined,
|
|
taxRate: undefined,
|
|
@@ -1110,8 +1111,8 @@ const productRules = {
|
|
|
minSellingPrice: [{ 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' }],
|
|
maxPurchasePrice: [{ required: true, message: '最高采购价不能为空', trigger: 'blur' }],
|
|
|
- productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
|
|
|
|
|
- purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
|
|
|
|
|
|
|
+ purchaseManagerNo: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
|
|
|
|
|
+ purchaseNo: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
|
|
|
taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
|
|
taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
|
|
|
minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }]
|
|
minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }]
|
|
|
};
|
|
};
|
|
@@ -1205,7 +1206,7 @@ const handleLevel3Search = async (keyword: string) => {
|
|
|
}
|
|
}
|
|
|
level3SearchLoading.value = true;
|
|
level3SearchLoading.value = true;
|
|
|
try {
|
|
try {
|
|
|
- const res = await categoryList({ classLevel: 3, categoryName: keyword, pageNum: 1, pageSize: 50 });
|
|
|
|
|
|
|
+ const res = await categoryList({ classLevel: 3, categoryName: keyword, platform: 0, pageNum: 1, pageSize: 50 });
|
|
|
level3SearchOptions.value = (res as any).data || (res as any).rows || [];
|
|
level3SearchOptions.value = (res as any).data || (res as any).rows || [];
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('搜索三级分类失败:', error);
|
|
console.error('搜索三级分类失败:', error);
|
|
@@ -1481,13 +1482,60 @@ const handleSubmit = async () => {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 将主图ossId转为url
|
|
|
|
|
+ let productImageUrl = '';
|
|
|
|
|
+ if (productForm.productImage) {
|
|
|
|
|
+ if (Array.isArray(productForm.productImage)) {
|
|
|
|
|
+ // 未重新上传,保留原URL
|
|
|
|
|
+ productImageUrl = productForm.productImage[0];
|
|
|
|
|
+ } else if (typeof productForm.productImage === 'string' && (productForm.productImage.startsWith('http://') || productForm.productImage.startsWith('https://'))) {
|
|
|
|
|
+ // ImageUpload emit后可能是URL字符串,直接使用
|
|
|
|
|
+ productImageUrl = productForm.productImage;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const mainRes = await listByIds(productForm.productImage);
|
|
|
|
|
+ if (mainRes.data && mainRes.data.length > 0) {
|
|
|
|
|
+ productImageUrl = mainRes.data[0].url;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 将轮播图ossId转为url
|
|
|
|
|
+ let imageUrlStr = '';
|
|
|
|
|
+ const carouselVal = carouselImages.value as any;
|
|
|
|
|
+ if (carouselVal && carouselVal.length > 0) {
|
|
|
|
|
+ if (typeof carouselVal === 'string' && (carouselVal.startsWith('http://') || carouselVal.startsWith('https://'))) {
|
|
|
|
|
+ // ImageUpload emit后可能是逗号分隔的URL字符串,直接使用
|
|
|
|
|
+ imageUrlStr = carouselVal;
|
|
|
|
|
+ } else if (Array.isArray(carouselVal)) {
|
|
|
|
|
+ const firstItem = carouselVal[0];
|
|
|
|
|
+ if (firstItem && (typeof firstItem === 'string' && (firstItem.startsWith('http://') || firstItem.startsWith('https://')))) {
|
|
|
|
|
+ // 未重新上传,保留原URL
|
|
|
|
|
+ imageUrlStr = carouselVal.join(',');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // ossId数组
|
|
|
|
|
+ const carouselRes = await listByIds(carouselVal.join(','));
|
|
|
|
|
+ if (carouselRes.data && carouselRes.data.length > 0) {
|
|
|
|
|
+ imageUrlStr = carouselRes.data.map((item) => item.url).join(',');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 用户重新上传了,carouselVal是ossId字符串
|
|
|
|
|
+ const carouselRes = await listByIds(carouselVal);
|
|
|
|
|
+ if (carouselRes.data && carouselRes.data.length > 0) {
|
|
|
|
|
+ imageUrlStr = carouselRes.data.map((item) => item.url).join(',');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
|
|
// 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
|
|
|
const submitProductData: any = {
|
|
const submitProductData: any = {
|
|
|
...productForm,
|
|
...productForm,
|
|
|
|
|
+ // 主图存url
|
|
|
|
|
+ productImage: productImageUrl,
|
|
|
// 将服务保障ID数组转换为逗号分隔字符串
|
|
// 将服务保障ID数组转换为逗号分隔字符串
|
|
|
serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
|
|
serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
|
|
|
- // 轮播图URL逗号分隔
|
|
|
|
|
- imageUrl: carouselImages.value.join(','),
|
|
|
|
|
|
|
+ // 轮播图存url
|
|
|
|
|
+ imageUrl: imageUrlStr,
|
|
|
// 将商品属性值转换为JSON字符串
|
|
// 将商品属性值转换为JSON字符串
|
|
|
attributesList: JSON.stringify(productAttributesValues.value),
|
|
attributesList: JSON.stringify(productAttributesValues.value),
|
|
|
isCustomize: customForm.isCustomize ? 1 : 0,
|
|
isCustomize: customForm.isCustomize ? 1 : 0,
|
|
@@ -1592,6 +1640,51 @@ const formatPrice = (field: string) => {
|
|
|
(productForm as any)[field] = num.toFixed(2);
|
|
(productForm as any)[field] = num.toFixed(2);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ // 失焦时校验价格链:市场价 > 官网价 ≥ 最低售价 ≥ 最高采购价 ≥ 采购价
|
|
|
|
|
+ validatePriceChain(field);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 价格链失焦校验
|
|
|
|
|
+const 成 = (field: string) => {
|
|
|
|
|
+ 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));
|
|
|
|
|
+
|
|
|
|
|
+ switch (field) {
|
|
|
|
|
+ case 'marketPrice':
|
|
|
|
|
+ if (!isNaN(market) && !isNaN(member) && !(market > member)) {
|
|
|
|
|
+ ElMessage.warning('市场价必须大于官网价');
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'memberPrice':
|
|
|
|
|
+ if (!isNaN(market) && !isNaN(member) && !(market > member)) {
|
|
|
|
|
+ ElMessage.warning('官网价必须小于市场价');
|
|
|
|
|
+ } else if (!isNaN(member) && !isNaN(minSell) && !(member >= minSell)) {
|
|
|
|
|
+ ElMessage.warning('官网价必须大于等于最低售价');
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'minSellingPrice':
|
|
|
|
|
+ if (!isNaN(member) && !isNaN(minSell) && !(member >= minSell)) {
|
|
|
|
|
+ ElMessage.warning('最低售价必须小于等于官网价');
|
|
|
|
|
+ } else if (!isNaN(minSell) && !isNaN(maxPurch) && !(minSell >= maxPurch)) {
|
|
|
|
|
+ ElMessage.warning('最低售价必须大于等于最高采购价');
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'maxPurchasePrice':
|
|
|
|
|
+ if (!isNaN(minSell) && !isNaN(maxPurch) && !(minSell >= maxPurch)) {
|
|
|
|
|
+ ElMessage.warning('最高采购价必须小于等于最低售价');
|
|
|
|
|
+ } else if (!isNaN(maxPurch) && !isNaN(purch) && !(maxPurch >= purch)) {
|
|
|
|
|
+ ElMessage.warning('最高采购价必须大于等于采购价');
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ case 'purchasingPrice':
|
|
|
|
|
+ if (!isNaN(maxPurch) && !isNaN(purch) && !(maxPurch >= purch)) {
|
|
|
|
|
+ ElMessage.warning('采购价必须小于等于最高采购价');
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// 格式化表格行中的价格为两位小数(不允许负数)
|
|
// 格式化表格行中的价格为两位小数(不允许负数)
|
|
@@ -1610,7 +1703,7 @@ const formatRowPrice = (row: any, field: string) => {
|
|
|
// 获取分类树
|
|
// 获取分类树
|
|
|
const getCategoryTree = async () => {
|
|
const getCategoryTree = async () => {
|
|
|
try {
|
|
try {
|
|
|
- const res = await categoryTree();
|
|
|
|
|
|
|
+ const res = await categoryTree({ platform: 0 });
|
|
|
categoryOptions.value = res.data || [];
|
|
categoryOptions.value = res.data || [];
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('获取分类树失败:', error);
|
|
console.error('获取分类树失败:', error);
|
|
@@ -1726,8 +1819,8 @@ const getStaffOptions = async () => {
|
|
|
staffOptions.value = dataList;
|
|
staffOptions.value = dataList;
|
|
|
console.log('采购人员列表:', staffOptions.value);
|
|
console.log('采购人员列表:', staffOptions.value);
|
|
|
// 如果有选项且当前没有选中值,设置第一个为默认值
|
|
// 如果有选项且当前没有选中值,设置第一个为默认值
|
|
|
- if (staffOptions.value.length > 0 && !productForm.purchasingPersonnel) {
|
|
|
|
|
- productForm.purchasingPersonnel = String(staffOptions.value[0].staffId);
|
|
|
|
|
|
|
+ if (staffOptions.value.length > 0 && !productForm.purchaseNo) {
|
|
|
|
|
+ productForm.purchaseNo = String(staffOptions.value[0].staffId);
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('获取采购人员列表失败:', error);
|
|
console.error('获取采购人员列表失败:', error);
|
|
@@ -1799,13 +1892,13 @@ const loadProductDetail = async () => {
|
|
|
Object.assign(productForm, res.data.productBaseVo);
|
|
Object.assign(productForm, res.data.productBaseVo);
|
|
|
|
|
|
|
|
// 回显产品经理 - 确保转换为字符串类型以匹配下拉框的value
|
|
// 回显产品经理 - 确保转换为字符串类型以匹配下拉框的value
|
|
|
- if (res.data.productBaseVo.productNature !== undefined && res.data.productBaseVo.productNature !== null) {
|
|
|
|
|
- productForm.productNature = String(res.data.productBaseVo.productNature);
|
|
|
|
|
|
|
+ if (res.data.productBaseVo.purchaseManagerNo !== undefined && res.data.productBaseVo.purchaseManagerNo !== null) {
|
|
|
|
|
+ productForm.purchaseManagerNo = String(res.data.productBaseVo.purchaseManagerNo);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 回显采购人员 - 确保转换为字符串类型以匹配下拉框的value
|
|
// 回显采购人员 - 确保转换为字符串类型以匹配下拉框的value
|
|
|
- if (res.data.productBaseVo.purchasingPersonnel !== undefined && res.data.productBaseVo.purchasingPersonnel !== null) {
|
|
|
|
|
- productForm.purchasingPersonnel = String(res.data.productBaseVo.purchasingPersonnel);
|
|
|
|
|
|
|
+ if (res.data.productBaseVo.purchaseNo !== undefined && res.data.productBaseVo.purchaseNo !== null) {
|
|
|
|
|
+ productForm.purchaseNo = String(res.data.productBaseVo.purchaseNo);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 回显税率编码显示值
|
|
// 回显税率编码显示值
|
|
@@ -1822,16 +1915,9 @@ const loadProductDetail = async () => {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 回显税率 - 在税率选项中查找匹配的值(处理浮点数精度问题)
|
|
|
|
|
|
|
+ // 回显税率 - 税率由税率编码自动带出(只读),直接赋值
|
|
|
if (res.data.productBaseVo.taxRate !== undefined && res.data.productBaseVo.taxRate !== null) {
|
|
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一致(数字类型)
|
|
// 回显单位 - 确保类型与下拉选项的id一致(数字类型)
|
|
@@ -1861,6 +1947,11 @@ const loadProductDetail = async () => {
|
|
|
productForm.afterSalesService = Number(res.data.productBaseVo.afterSalesService);
|
|
productForm.afterSalesService = Number(res.data.productBaseVo.afterSalesService);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 回显主图 - 包装为数组让ImageUpload走数组分支直接用URL回显
|
|
|
|
|
+ if (res.data.productBaseVo.productImage) {
|
|
|
|
|
+ productForm.productImage = [res.data.productBaseVo.productImage] as any;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 回显轮播图
|
|
// 回显轮播图
|
|
|
if (res.data.productBaseVo.imageUrl) {
|
|
if (res.data.productBaseVo.imageUrl) {
|
|
|
carouselImages.value = res.data.productBaseVo.imageUrl.split(',').filter((url: string) => url.trim());
|
|
carouselImages.value = res.data.productBaseVo.imageUrl.split(',').filter((url: string) => url.trim());
|