From a4b3ee325c7354579d495bc74a777e494e5ec38c Mon Sep 17 00:00:00 2001
From: quanwei <419654421@qq.com>
Date: Fri, 06 Feb 2026 18:18:44 +0800
Subject: [PATCH] 商品可以价格面议 选择走访时显示输入走访企业名 分会添加活动时要总会审核 分类添加人数限制,添加活动选择了填写人数限制的分类时活动名额下显示该分类人数限制为15 同一个企业30天内只能走访一次,在30天内走访同一个企业时提示该企业已被走访xx天后才可以从新走访

---
 mobile/pages/user/my_shop/product_add.vue |  231 +++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 181 insertions(+), 50 deletions(-)

diff --git a/mobile/pages/user/my_shop/product_add.vue b/mobile/pages/user/my_shop/product_add.vue
index 77f0505..eaad43a 100644
--- a/mobile/pages/user/my_shop/product_add.vue
+++ b/mobile/pages/user/my_shop/product_add.vue
@@ -75,6 +75,17 @@
 				</view>
 
 				<view class="form-item">
+					<view class="item-label">价格面议</view>
+					<view class="radio-group">
+						<view class="radio-item" :class="{ active: form.model.is_price_negotiable === 1 }"
+							@click="form.model.is_price_negotiable = 1">是</view>
+						<view class="radio-item" :class="{ active: form.model.is_price_negotiable === 0 }"
+							@click="form.model.is_price_negotiable = 0">否</view>
+					</view>
+					<view class="form-tips">开启后,移动端将显示"价格面议"而非具体价格</view>
+				</view>
+
+				<view class="form-item">
 					<view class="item-label">商品图片</view>
 					<view class="uploader">
 						<view class="uploader-item" v-for="(item, index) in form.model.image" :key="index">
@@ -84,7 +95,8 @@
 						<view class="uploader-add" @click="handleUpload('image')">+</view>
 					</view>
 					<!-- 上传组件 -->
-					<Upload v-if="showUpload && uploadType === 'image'" :isupload="showUpload && uploadType === 'image'" @getImgs="onUploadComplete" type='frontid'>上传图片</Upload>
+					<Upload v-if="showUpload && uploadType === 'image'" :isupload="showUpload && uploadType === 'image'"
+						@getImgs="onUploadComplete" type='frontid'>上传图片</Upload>
 				</view>
 
 
@@ -99,8 +111,8 @@
 						</view>
 					</view>
 					<!-- 上传组件 -->
-				<upload v-if="showUpload && uploadType === 'video'" :num="1" file_type="video"
-					@getImgs="onUploadComplete"></upload>
+					<upload v-if="showUpload && uploadType === 'video'" :num="1" file_type="video"
+						@getImgs="onUploadComplete"></upload>
 				</view>
 
 				<view class="form-item">
@@ -132,6 +144,8 @@
 							@click="form.model.is_virtual = 1">虚拟商品</view>
 						<view class="radio-item" :class="{ active: form.model.is_virtual === 2 }"
 							@click="form.model.is_virtual = 2">券商品</view>
+						<view class="radio-item" :class="{ active: form.model.is_virtual === 3 }"
+							@click="form.model.is_virtual = 3">服务商品</view>
 					</view>
 				</view>
 
@@ -164,7 +178,7 @@
 				</view>
 
 				<!-- 虚拟商品设置 -->
-				<view class="form-item" v-if="form.model.is_virtual === 1">
+				<view class="form-item" v-if="form.model.is_virtual === 1 || form.model.is_virtual === 3">
 					<view class="item-label">发货类型</view>
 					<view class="radio-group">
 						<view class="radio-item" :class="{ active: form.model.virtual_auto === 1 }"
@@ -179,7 +193,13 @@
 					<input class="item-input" v-model="form.model.virtual_content" placeholder="请输入虚拟物品内容" />
 				</view>
 
-				<view class="form-item" v-if="form.model.is_virtual === 1 && form.model.virtual_auto === 0">
+				<view class="form-item" v-if=" form.model.is_virtual === 3">
+					<view class="item-label">服务商品内容</view>
+					<input class="item-input" v-model="form.model.service_content" placeholder="请输入服务商品内容" />
+				</view>
+
+				<view class="form-item"
+					v-if="(form.model.is_virtual === 1 || form.model.is_virtual === 3)&& form.model.virtual_auto === 0">
 					<view class="item-label">支持线下核销</view>
 					<view class="radio-group">
 						<view class="radio-item" :class="{ active: form.model.is_verify === 1 }"
@@ -190,7 +210,7 @@
 				</view>
 
 				<view class="form-item"
-					v-if="form.model.is_virtual === 1 && form.model.virtual_auto === 0 && form.model.is_verify === 1">
+					v-if="(form.model.is_virtual === 1 || form.model.is_virtual === 3) && form.model.virtual_auto === 0 && form.model.is_verify === 1">
 					<view class="item-label">核销到期类型</view>
 					<view class="radio-group">
 						<view class="radio-item" :class="{ active: form.model.verify_type === 10 }"
@@ -201,14 +221,14 @@
 				</view>
 
 				<view class="form-item"
-					v-if="form.model.is_virtual === 1 && form.model.virtual_auto === 0 && form.model.is_verify === 1 && form.model.verify_type === 10">
+					v-if="(form.model.is_virtual === 1 || form.model.is_virtual === 3) && form.model.virtual_auto === 0 && form.model.is_verify === 1 && form.model.verify_type === 10">
 					<view class="item-label">核销有效天数</view>
 					<input class="item-input" v-model="form.model.verify_day" placeholder="请输入有效天数,0或留空表示永久有效"
 						type="number" />
 				</view>
 
 				<view class="form-item"
-					v-if="form.model.is_virtual === 1 && form.model.virtual_auto === 0 && form.model.is_verify === 1 && form.model.verify_type === 20">
+					v-if="(form.model.is_virtual === 1 || form.model.is_virtual === 3) && form.model.virtual_auto === 0 && form.model.is_verify === 1 && form.model.verify_type === 20">
 					<view class="item-label">核销有效时间</view>
 					<picker mode="daterange" class="item-picker" @change="verifyTimeChange" :value="verifyTimeArray">
 						<view class="picker-text">{{ verifyTimeText || '请选择核销有效时间' }}</view>
@@ -254,7 +274,8 @@
 
 					<view class="form-item">
 						<view class="item-label">产品价格</view>
-						<input class="item-input" v-model="form.model.sku.product_price" placeholder="请输入产品价格" type="digit" />
+						<input class="item-input" v-model="form.model.sku.product_price" placeholder="请输入产品价格"
+							type="digit" />
 					</view>
 
 					<view class="form-item">
@@ -265,7 +286,8 @@
 
 					<view class="form-item">
 						<view class="item-label">库存数量</view>
-						<input class="item-input" v-model="form.model.sku.stock_num" placeholder="请输入库存数量" type="number" />
+						<input class="item-input" v-model="form.model.sku.stock_num" placeholder="请输入库存数量"
+							type="number" />
 					</view>
 
 					<view class="form-item">
@@ -307,7 +329,9 @@
 						<view class="item-label">SKU列表</view>
 						<view class="sku-list">
 							<view class="sku-item" v-for="(sku, index) in form.model.spec_many.spec_list" :key="index">
-								<view class="sku-info">{{ sku.spec_text }}</view>
+								<view class="sku-info">
+									<text v-for="(row, rowIdx) in sku.rows" :key="rowIdx">{{ row.spec_value }}{{ rowIdx < sku.rows.length - 1 ? ' ' : '' }}</text>
+								</view>
 								<view class="form-item">
 									<view class="item-label">规格图片</view>
 									<view class="uploader">
@@ -323,11 +347,14 @@
 								</view>
 								<view class="sku-inputs">
 									<input class="sku-input" v-model="sku.spec_form.product_no" placeholder="产品编码" />
-									<input class="sku-input" v-model="sku.spec_form.product_price" placeholder="产品价格" type="digit" />
+									<input class="sku-input" v-model="sku.spec_form.product_price" placeholder="产品价格"
+										type="digit" />
 									<input class="sku-input" v-model="sku.spec_form.line_price" placeholder="产品划线价"
 										type="digit" />
-									<input class="sku-input" v-model="sku.spec_form.stock_num" placeholder="库存数量" type="number" />
-									<input class="sku-input" v-model="sku.spec_form.product_weight" placeholder="商品重量(Kg)" type="digit" />
+									<input class="sku-input" v-model="sku.spec_form.stock_num" placeholder="库存数量"
+										type="number" />
+									<input class="sku-input" v-model="sku.spec_form.product_weight"
+										placeholder="商品重量(Kg)" type="digit" />
 								</view>
 							</view>
 						</view>
@@ -340,15 +367,15 @@
 				<view class="form-item">
 					<view class="item-label">详情类型</view>
 					<view class="radio-group">
-						<view class="radio-item" :class="{ active: form.model.content_type === 10 }"
-							@click="form.model.content_type = 10">图文</view>
-						<view class="radio-item" :class="{ active: form.model.content_type === 20 }"
-							@click="form.model.content_type = 20">纯图</view>
+						<view class="radio-item" :class="{ active: form.model.is_picture == 0 }"
+							@click="form.model.is_picture = 0">图文</view>
+						<view class="radio-item" :class="{ active: form.model.is_picture === 1 }"
+							@click="form.model.is_picture = 1">纯图</view>
 					</view>
 				</view>
 
 				<!-- 图文类型 -->
-				<view v-if="form.model.content_type === 10" class="form-item">
+				<view v-if="form.model.is_picture === 0" class="form-item">
 					<view class="item-label">商品详情</view>
 					<view class="editor">
 						<!-- 富文本编辑器 -->
@@ -439,7 +466,7 @@
 				</view>
 
 				<!-- 纯图类型 -->
-				<view v-else-if="form.model.content_type === 20" class="form-item">
+				<view v-else-if="form.model.is_picture == 1" class="form-item">
 					<view class="item-label">商品详情图片</view>
 					<view class="uploader">
 						<view class="uploader-item" v-for="(item, index) in form.model.contentImage" :key="index">
@@ -450,7 +477,8 @@
 					</view>
 					<view class="editor-tips">提示:最多上传20张图片</view>
 					<!-- 上传组件 -->
-					<upload v-if="showUpload && uploadType === 'content'" :isupload="showUpload && uploadType === 'content'" :num="20 - form.model.contentImage.length"
+					<upload v-if="showUpload && uploadType === 'content'"
+						:isupload="showUpload && uploadType === 'content'" :num="20 - form.model.contentImage.length"
 						@getImgs="onUploadComplete"></upload>
 				</view>
 			</view>
@@ -535,7 +563,8 @@
 						selling_point: '',
 						spec_type: 10,
 						deduct_stock_type: 20,
-						content_type: 10,
+						is_picture: 0,
+						service_content: '',
 						sku: {
 							product_no: '',
 							product_price: '',
@@ -751,12 +780,52 @@
 					placeholderText: '例如:颜色',
 					success: (res) => {
 						if (res.confirm && res.content.trim()) {
-							this.form.model.spec_many.spec_attr.push({
-								name: res.content.trim(),
-								values: []
+							const specName = res.content.trim();
+							// 继续输入第一个规格值
+							uni.showModal({
+								title: '添加第一个规格值',
+								content: '',
+								editable: true,
+								placeholderText: '例如:红色',
+								success: (valueRes) => {
+									if (valueRes.confirm && valueRes.content.trim()) {
+										const specValue = valueRes.content.trim();
+										// 调用API添加规格组和第一个规格值
+										this._post('supplier.product.spec/addSpec', {
+											spec_name: specName,
+											spec_value: specValue
+										}, (response) => {
+											if (response.code === 1 && response.data) {
+												this.form.model.spec_many.spec_attr.push({
+													group_id: response.data.spec_id,
+													name: specName,
+													values: [specValue],
+													items: [{
+														item_id: response.data.spec_value_id,
+														spec_value: specValue
+													}]
+												});
+												// 生成SKU列表
+												this.generateSkuList();
+												uni.showToast({
+													title: '添加成功',
+													icon: 'success'
+												});
+											} else {
+												uni.showToast({
+													title: response.msg || '添加失败',
+													icon: 'none'
+												});
+											}
+										});
+									} else if (valueRes.confirm) {
+										uni.showToast({
+											title: '请输入规格值',
+											icon: 'none'
+										});
+									}
+								}
 							});
-							// 生成SKU列表
-							this.generateSkuList();
 						}
 					}
 				});
@@ -778,10 +847,32 @@
 					placeholderText: '例如:红色',
 					success: (res) => {
 						if (res.confirm && res.content.trim()) {
+							const specValue = res.content.trim();
 							const attr = this.form.model.spec_many.spec_attr[attrIndex];
-							attr.values.push(res.content.trim());
-							// 重新生成SKU列表
-							this.generateSkuList();
+							// 调用API添加规格值
+							this._post('supplier.product.spec/addSpecValue', {
+								spec_id: attr.group_id,
+								spec_value: specValue
+							}, (response) => {
+								if (response.code === 1 && response.data) {
+									attr.values.push(specValue);
+									attr.items.push({
+										item_id: response.data.spec_value_id,
+										spec_value: specValue
+									});
+									// 重新生成SKU列表
+									this.generateSkuList();
+									uni.showToast({
+										title: '添加成功',
+										icon: 'success'
+									});
+								} else {
+									uni.showToast({
+										title: response.msg || '添加失败',
+										icon: 'none'
+									});
+								}
+							});
 						}
 					}
 				});
@@ -790,11 +881,14 @@
 			// 删除规格值
 			deleteSpecValue(attrIndex, valueIndex) {
 				const attr = this.form.model.spec_many.spec_attr[attrIndex];
-				if (attr.values.length > 1) {
+				//if (attr.values.length > 1) {
 					attr.values.splice(valueIndex, 1);
+					if (attr.items && attr.items.length > valueIndex) {
+						attr.items.splice(valueIndex, 1);
+					}
 					// 重新生成SKU列表
 					this.generateSkuList();
-				}
+				//}
 			},
 
 			// 生成SKU列表
@@ -810,17 +904,37 @@
 
 				// 生成SKU列表
 				const skuList = combinations.map(comb => {
-					const spec_text = comb.map((val, idx) => `${attrs[idx].name}:${val}`).join('; ');
+					const rows = [];
+					const specSkuIdAttr = [];
+
+					comb.forEach((val, idx) => {
+						const attr = attrs[idx];
+						// 查找对应的 item_id
+						const itemIndex = attr.values.indexOf(val);
+						const itemId = (attr.items && attr.items[itemIndex]) ? attr.items[itemIndex].item_id : 0;
+
+						rows.push({
+							item_id: itemId,
+							spec_value: val
+						});
+						specSkuIdAttr.push(itemId);
+					});
+
+					const spec_sku_id = specSkuIdAttr.join('_');
+
 					return {
-						spec_text,
+						product_sku_id: 0,
+						spec_sku_id,
+						rows,
 						image: [],
-						product_no: '',
-						price: '',
-						line_price: '',
-						stock: '',
-						weight: '',
-						cost_price: '',
-						bar_code: ''
+						spec_form: {
+							product_no: '',
+							product_price: '',
+							line_price: '',
+							stock_num: '',
+							product_weight: '',
+							bar_code: ''
+						}
 					};
 				});
 
@@ -851,15 +965,15 @@
 			save() {
 				let self = this;
 				// 先获取富文本编辑器内容
-				if (self.form.model.content_type === 10 && self.editorCtx) {
+				if (self.form.model.is_picture == 0 && self.editorCtx) {
 					// 图文类型,获取编辑器内容
-					self.editorCtx.getContents({ 
+					self.editorCtx.getContents({
 						success: (res) => {
 							// 将编辑器内容赋值给表单字段
 							self.form.model.content = res.html;
 							// 继续执行保存逻辑
 							self.doSave();
-						}, 
+						},
 						fail: () => {
 							// 获取内容失败,继续执行保存逻辑
 							self.doSave();
@@ -871,7 +985,7 @@
 					self.doSave();
 				}
 			},
-			
+
 			// 实际保存逻辑
 			doSave() {
 				let self = this;
@@ -930,6 +1044,7 @@
 
 					for (let i = 0; i < self.form.model.spec_many.spec_list.length; i++) {
 						const sku = self.form.model.spec_many.spec_list[i];
+						console.log(sku);
 						if (!sku.spec_form.product_price || parseFloat(sku.spec_form.product_price) <= 0) {
 							uni.showToast({
 								title: '请输入有效的SKU价格',
@@ -949,7 +1064,7 @@
 				}
 
 				// 根据详情类型验证内容
-				if (self.form.model.content_type === 10) {
+				if (self.form.model.is_picture === 0) {
 					// 图文类型,验证富文本内容
 					if (!self.form.model.content || self.form.model.content.trim() === '') {
 						uni.showToast({
@@ -958,7 +1073,7 @@
 						});
 						return;
 					}
-				} else if (self.form.model.content_type === 20) {
+				} else if (self.form.model.is_picture == 1) {
 					// 纯图类型,验证详情图片
 					if (self.form.model.contentImage.length === 0) {
 						uni.showToast({
@@ -969,11 +1084,26 @@
 					}
 				}
 
+				// 转换spec_attr数据格式为后端期望的格式
+				let submitData = JSON.parse(JSON.stringify(self.form.model));
+				if (submitData.spec_type == 20 && submitData.spec_many && submitData.spec_many.spec_attr) {
+					submitData.spec_many.spec_attr = submitData.spec_many.spec_attr.map(attr => {
+						return {
+							group_id: attr.group_id || 0,
+							name: attr.name,
+							spec_items: attr.values.map((val, idx) => ({
+								item_id: (attr.items && attr.items[idx] && attr.items[idx].item_id) ? attr.items[idx].item_id : 0,
+								spec_value: val
+							}))
+						};
+					});
+				}
+
 				self.loading = true;
 				self._post('supplier.product/add', {
-					params: JSON.stringify(self.form.model)
+					params: JSON.stringify(submitData)
 				}, (res) => {
-					if (res.code === 0) {
+					if (res.code === 1) {
 						uni.showToast({
 							title: '保存成功'
 						});
@@ -1294,7 +1424,8 @@
 
 	.radio-group {
 		display: flex;
-		gap: 40rpx;
+		gap: 15rpx;
+		flex-wrap: wrap;
 	}
 
 	.radio-item {

--
Gitblit v1.9.2