付晓文。 1 bulan lalu
induk
melakukan
037c000d2a

+ 3 - 2
api/merchantDish.js

@@ -104,8 +104,9 @@ export function mapPackageToListItem(item) {
 		image,
 		belong: belongingToKey(item.productBelonging),
 		desc: item.dishName || item.mealBoxNames || '',
-		price: item.sellingPrice != null ? item.sellingPrice : item.price,
-		originPrice: item.originalPrice != null ? item.originalPrice : item.sellingPrice,
+		// 售价 price,原价 sellingPrice
+		price: item.price != null ? item.price : item.sellingPrice,
+		originPrice: item.sellingPrice != null ? item.sellingPrice : item.originalPrice,
 		stock: item.dailyStock != null ? item.dailyStock : 0,
 		sales: item.sales != null ? item.sales : 0,
 		status,

+ 92 - 10
pages/merchantDish/dish/form.vue

@@ -84,7 +84,7 @@
 			<view class="section-title">销售规格</view>
 			<view class="row">
 				<text class="label required">销售规格</text>
-				<u-radio-group v-model="form.specMode">
+				<u-radio-group :value="form.specMode" @change="onSpecModeChange">
 					<u-radio name="single" active-color="#449AFF">单规格</u-radio>
 					<u-radio name="multi" active-color="#449AFF" :disabled="specLocked">多规格</u-radio>
 				</u-radio-group>
@@ -132,7 +132,13 @@
 		</view>
 
 		<view class="footer">
-			<u-button type="primary" shape="circle" :disabled="!canNext" @click="onNext">下一步</u-button>
+			<u-button
+				:key="'next-' + form.specMode + '-' + (canNext ? 1 : 0)"
+				type="primary"
+				shape="circle"
+				:disabled="!canNext"
+				@click="onNext"
+			>下一步</u-button>
 		</view>
 
 		<!-- 规格操作菜单 -->
@@ -160,10 +166,14 @@
 					编辑规格值
 					<text class="dialog-close" @click="valueListShow = false">×</text>
 				</view>
-				<view class="vp-item" v-for="(v, i) in editingValues" :key="v.id" @click="editOneValue(i)">
-					<text>{{ v.name }}</text>
-					<u-icon name="edit-pen" color="#999" size="32"></u-icon>
+				<view class="vp-item" v-for="(v, i) in editingValues" :key="v.id">
+					<text class="vp-name" @click="editOneValue(i)">{{ v.name }}</text>
+					<view class="vp-ops">
+						<u-icon name="edit-pen" color="#999" size="32" @click="editOneValue(i)"></u-icon>
+						<u-icon name="trash" color="#FF4D4F" size="32" @click="removeSpecValue(i)"></u-icon>
+					</view>
 				</view>
+				<view class="vp-add" @click="addSpecValue">+ 添加规格值</view>
 				<view class="vp-confirm" @click="confirmValues">确定</view>
 			</view>
 		</view>
@@ -279,7 +289,7 @@ export default {
 			categorySheet: [],
 			typeSheet: PRODUCT_TYPES.map((i) => ({ text: i.label, value: i.value })),
 			specMenuShow: false,
-			specMenuList: [{ text: '编辑规格名' }, { text: '编辑规格值' }],
+			specMenuList: [{ text: '编辑规格名' }, { text: '编辑规格值' }, { text: '删除规格', color: '#FF4D4F' }],
 			activeSpecIndex: 0,
 			nameModal: false,
 			editingName: '',
@@ -433,8 +443,14 @@ export default {
 					this.form.cover = item.productImage || '';
 					this.form.detail = item.detailImages || '';
 					this.form.isCombo = Number(item.isPackage) === 1 ? 1 : 0;
+					// 售价 price,原价 sellingPrice
 					this.form.price = item.price != null ? String(item.price) : '';
-					this.form.sellingPrice = item.sellingPrice != null ? String(item.sellingPrice) : '';
+					this.form.sellingPrice =
+						item.sellingPrice != null
+							? String(item.sellingPrice)
+							: item.originalPrice != null
+								? String(item.originalPrice)
+								: '';
 					this.form.stock = item.dailyStock != null ? String(item.dailyStock) : '';
 					this.form.costPrice = item.costPrice != null ? String(item.costPrice) : '';
 					this.form.specMode = Number(item.salesSpecType) === 2 ? 'multi' : 'single';
@@ -500,9 +516,18 @@ export default {
 			this.form.isCombo = val === undefined ? this.form.isCombo : val;
 			this.applySpecLock();
 		},
+		onSpecModeChange(val) {
+			// 同步更新,避免 u-radio-group 双向绑定延迟导致下一步按钮置灰滞后
+			const mode = val || 'single';
+			if (this.specLocked && mode === 'multi') {
+				this.$set(this.form, 'specMode', 'single');
+				return;
+			}
+			this.$set(this.form, 'specMode', mode);
+		},
 		applySpecLock() {
 			if (this.specLocked) {
-				this.form.specMode = 'single';
+				this.$set(this.form, 'specMode', 'single');
 			}
 		},
 		pickCategory() {
@@ -534,12 +559,28 @@ export default {
 				this.$nextTick(() => {
 					this.nameModal = true;
 				});
-			} else {
+			} else if (index === 1) {
 				this.editingValues = JSON.parse(JSON.stringify(spec.values || []));
 				this.$nextTick(() => {
 					this.valueListShow = true;
 				});
+			} else if (index === 2) {
+				this.deleteSpec();
+			}
+		},
+		deleteSpec() {
+			if ((this.form.specs || []).length <= 1) {
+				uni.showToast({ title: '至少保留一个规格', icon: 'none' });
+				return;
 			}
+			uni.showModal({
+				content: '确定删除该规格?',
+				confirmColor: '#FF4D4F',
+				success: (res) => {
+					if (!res.confirm) return;
+					this.form.specs.splice(this.activeSpecIndex, 1);
+				}
+			});
 		},
 		saveSpecName() {
 			if (!this.editingName.trim()) {
@@ -554,6 +595,22 @@ export default {
 			this.editingValueName = (this.editingValues[i] && this.editingValues[i].name) || '';
 			this.valueEditModal = true;
 		},
+		addSpecValue() {
+			const id = `v${Date.now()}`;
+			this.editingValues.push({
+				id,
+				name: `选项${this.editingValues.length + 1}`,
+				selected: true
+			});
+			this.editOneValue(this.editingValues.length - 1);
+		},
+		removeSpecValue(i) {
+			if (this.editingValues.length <= 1) {
+				uni.showToast({ title: '至少保留一个规格值', icon: 'none' });
+				return;
+			}
+			this.editingValues.splice(i, 1);
+		},
 		saveOneValue() {
 			if (!this.editingValueName.trim()) {
 				uni.showToast({ title: '请输入规格值', icon: 'none' });
@@ -563,6 +620,10 @@ export default {
 			this.valueEditModal = false;
 		},
 		confirmValues() {
+			if (!this.editingValues.length) {
+				uni.showToast({ title: '请至少添加一个规格值', icon: 'none' });
+				return;
+			}
 			this.form.specs[this.activeSpecIndex].values = this.editingValues;
 			this.valueListShow = false;
 		},
@@ -811,9 +872,30 @@ export default {
 		border-bottom: 1rpx solid #f0f0f0;
 		font-size: 28rpx;
 		color: #333;
+		.vp-name {
+			flex: 1;
+			min-width: 0;
+		}
+		.vp-ops {
+			display: flex;
+			align-items: center;
+			gap: 28rpx;
+			margin-left: 20rpx;
+			flex-shrink: 0;
+		}
+	}
+	.vp-add {
+		margin: 24rpx 40rpx 0;
+		height: 72rpx;
+		line-height: 72rpx;
+		text-align: center;
+		border: 1rpx dashed #449aff;
+		border-radius: 12rpx;
+		color: #449aff;
+		font-size: 28rpx;
 	}
 	.vp-confirm {
-		margin: 30rpx 40rpx 10rpx;
+		margin: 24rpx 40rpx 10rpx;
 		height: 80rpx;
 		line-height: 80rpx;
 		text-align: center;

+ 11 - 5
pages/merchantDish/dish/sale-time.vue

@@ -58,7 +58,7 @@
 			<u-button type="primary" shape="circle" :disabled="!canSubmit || submitting" @click="onSubmit">提交审核</u-button>
 		</view>
 
-		<u-calendar v-model="calendarShow" mode="date" @change="onCalendar"></u-calendar>
+		<u-calendar v-model="calendarShow" mode="date" max-date="2050-12-31" @change="onCalendar"></u-calendar>
 	</view>
 </template>
 
@@ -144,9 +144,11 @@ export default {
 			this.calendarShow = true;
 		},
 		onCalendar(e) {
-			const val = e.result || e;
+			const val = (e && e.result) || e;
+			if (!val || typeof val !== 'string') return;
 			if (this.calendarField === 'start') this.startDate = val;
 			else this.endDate = val;
+			this.calendarShow = false;
 		},
 		goPrev() {
 			uni.navigateBack();
@@ -213,8 +215,9 @@ export default {
 			});
 			const skus = (draft.combos || []).map((c, i) => ({
 				specOptionNames: (c.label || '').replace(/\s*\|\s*/g, '/'),
+				// 售价 price,原价 sellingPrice
 				price: Number(c.price || 0),
-				sellingPrice: Number(c.sellingPrice || 0),
+				sellingPrice: Number(c.sellingPrice != null ? c.sellingPrice : c.originPrice || 0),
 				stock: Number(c.stock || 0),
 				costPrice: c.costPrice !== '' && c.costPrice != null ? Number(c.costPrice) : undefined,
 				status: 1,
@@ -244,8 +247,9 @@ export default {
 				payload.id = draft.editId;
 			}
 			if (!isMulti) {
+				// 售价 price,原价 sellingPrice
 				payload.price = Number(draft.price || 0);
-				payload.originalPrice = Number(draft.sellingPrice || 0);
+				payload.sellingPrice = Number(draft.sellingPrice || 0);
 				payload.dailyStock = Number(draft.stock || 0);
 				if (draft.costPrice !== '' && draft.costPrice != null) {
 					payload.costPrice = Number(draft.costPrice);
@@ -257,7 +261,9 @@ export default {
 				// 多规格时取第一档作为展示价/库存兜底
 				const first = (draft.combos || [])[0] || {};
 				payload.price = Number(first.price || 0);
-				payload.originalPrice = Number(first.sellingPrice || 0);
+				payload.sellingPrice = Number(
+					first.sellingPrice != null ? first.sellingPrice : first.originPrice || 0
+				);
 				payload.dailyStock = Number(first.stock || 0);
 			}
 			if (Number(draft.isCombo) === 1) {

+ 13 - 9
pages/merchantDish/dish/spec-detail.vue

@@ -17,7 +17,7 @@
 					</view>
 					<view class="row">
 						<text class="label required">原价 (元)</text>
-						<input v-model="item.originPrice" type="digit" placeholder="请输入" :disabled="batchMode" />
+						<input v-model="item.sellingPrice" type="digit" placeholder="请输入" :disabled="batchMode" />
 					</view>
 					<view class="row">
 						<text class="label required">每日库存</text>
@@ -51,7 +51,7 @@
 				</view>
 				<view class="row">
 					<text class="label required">原价 (元)</text>
-					<input v-model="batchForm.originPrice" type="digit" placeholder="请输入" />
+					<input v-model="batchForm.sellingPrice" type="digit" placeholder="请输入" />
 				</view>
 				<view class="row">
 					<text class="label required">每日库存</text>
@@ -77,7 +77,7 @@ export default {
 			batchMode: false,
 			selected: [],
 			batchShow: false,
-			batchForm: { price: '', originPrice: '', stock: '', costPrice: '' }
+			batchForm: { price: '', sellingPrice: '', stock: '', costPrice: '' }
 		};
 	},
 	computed: {
@@ -85,12 +85,16 @@ export default {
 			return this.combos.length > 0 && this.selected.length === this.combos.length;
 		},
 		canNext() {
-			return this.combos.every((i) => i.price !== '' && i.originPrice !== '' && i.stock !== '');
+			return this.combos.every((i) => i.price !== '' && i.sellingPrice !== '' && i.stock !== '');
 		}
 	},
 	onLoad() {
 		const draft = uni.getStorageSync(DRAFT_KEY) || {};
-		this.combos = (draft.combos || []).map((i) => ({ ...i }));
+		// 兼容旧草稿 originPrice → sellingPrice
+		this.combos = (draft.combos || []).map((i) => ({
+			...i,
+			sellingPrice: i.sellingPrice != null && i.sellingPrice !== '' ? i.sellingPrice : i.originPrice || ''
+		}));
 		if (!this.combos.length) {
 			uni.showToast({ title: '请先选择规格', icon: 'none' });
 			setTimeout(() => uni.navigateBack(), 500);
@@ -116,18 +120,18 @@ export default {
 				uni.showToast({ title: '请先选择规格', icon: 'none' });
 				return;
 			}
-			this.batchForm = { price: '', originPrice: '', stock: '', costPrice: '' };
+			this.batchForm = { price: '', sellingPrice: '', stock: '', costPrice: '' };
 			this.batchShow = true;
 		},
 		applyBatch() {
-			const { price, originPrice, stock, costPrice } = this.batchForm;
-			if (price === '' || originPrice === '' || stock === '') {
+			const { price, sellingPrice, stock, costPrice } = this.batchForm;
+			if (price === '' || sellingPrice === '' || stock === '') {
 				uni.showToast({ title: '请完善必填项', icon: 'none' });
 				return;
 			}
 			this.selected.forEach((i) => {
 				this.combos[i].price = price;
-				this.combos[i].originPrice = originPrice;
+				this.combos[i].sellingPrice = sellingPrice;
 				this.combos[i].stock = stock;
 				this.combos[i].costPrice = costPrice;
 			});

+ 1 - 1
pages/merchantDish/utils.js

@@ -16,7 +16,7 @@ export function buildSpecCombos(specs) {
 						label: prev.label ? `${prev.label} | ${v.name}` : v.name,
 						keys: [...(prev.keys || []), v.id],
 						price: '',
-						originPrice: '',
+						sellingPrice: '',
 						stock: '',
 						costPrice: ''
 					});