ソースを参照

Merge branch 'master' of http://39.101.143.165:8090/hxl13994548489/LocalLivingServicesUniapp

caiyuqin 2 ヶ月 前
コミット
64c7b40ed1

+ 22 - 1
common/router/modules/routes.js

@@ -614,7 +614,28 @@ const routes = [{
 		path: '/pages/groupMeal/goods/form',
 		name: 'groupMealGoodsForm',
 		meta: {
-			title: '编辑商品',
+			title: '编辑商品1',
+		},
+	},
+	{
+		path: '/pages/groupMeal/goods/form-step1',
+		name: 'groupMealGoodsFormStep2',
+		meta: {
+			title: '编辑商品1',
+		},
+	},
+	{
+		path: '/pages/groupMeal/goods/form-day',
+		name: 'groupMealGoodsFormDay',
+		meta: {
+			title: '编辑商品售卖时间',
+		},
+	},
+	{
+		path: '/pages/groupMeal/goods/form-step2',
+		name: 'groupMealGoodsFormStep2',
+		meta: {
+			title: '编辑商品2',
 		},
 	},
 	{

+ 1 - 1
components/upload/index.vue

@@ -340,4 +340,4 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-</style>
+</style>

+ 9 - 1
pages.json

@@ -478,7 +478,15 @@
 			"style": {}
 		},
 		{
-			"path": "pages/groupMeal/goods/form",
+			"path": "pages/groupMeal/goods/form-step1",
+			"style": {}
+		},
+		{
+			"path": "pages/groupMeal/goods/form-day",
+			"style": {}
+		},
+		{
+			"path": "pages/groupMeal/goods/form-step2",
 			"style": {}
 		},
 		{

+ 178 - 0
pages/groupMeal/goods/form-day.vue

@@ -0,0 +1,178 @@
+<template>
+	<view class="page-container">
+		<u-navbar title="新建团餐" :autoBack="true" :placeholder="true"></u-navbar>
+		<view class="content">
+			<view class="title">
+				售卖时间
+			</view>
+			<view class="tips">
+				商品售卖周天(可多选)
+				<span>*</span>
+			</view>
+			<view class="day-list">
+				<view class="day-item" v-for="item in dayList" :key="item.code" @click="selectDay(item)"
+					:class="{ 'selected': isDaySelected(item) }">
+					{{item.name}}
+				</view>
+			</view>
+			<view class="footer-bar">
+				<u-button :custom-style="{
+					width: '324rpx',
+					fontWeight: '400',
+					fontSize: '28rpx',
+					color: '#449AFF',
+					border: '1rpx solid #449AFF',
+					background: '#fff',
+					marginRight: '24rpx'
+				}" @click="goBack">上一步</u-button>
+				<u-button :custom-style="{
+					width: '324rpx',
+					fontWeight: '400',
+					fontSize: '28rpx',
+					color: '#449AFF',
+					border: '1rpx solid #449AFF',
+					background: '#fff',
+					marginRight: '24rpx'
+				}" @click="goNextStep">下一步</u-button>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				form: {
+					dayCodes: []
+				},
+				dayList: [],
+				selectedDayCodes: []
+			}
+		},
+		methods: {
+			goBack() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			async getDayList() {
+				const res = await this.$http.get('/groupmeal/gmMerchantPackage/getSaleDay')
+				if (res.data.code === 200 && res.data.result) {
+					this.dayList = res.data.result
+				}
+			},
+			normalizeDayCodes(codes) {
+				const dayCodes = Array.isArray(codes) ? codes : String(codes || '').split(',');
+				return dayCodes.map(code => String(code).trim()).filter(Boolean);
+			},
+			isDaySelected(item) {
+				return this.selectedDayCodes.includes(String(item.code));
+			},
+			selectDay(item) {
+				const code = String(item.code);
+				const index = this.selectedDayCodes.indexOf(code);
+				if (index > -1) {
+					this.selectedDayCodes.splice(index, 1);
+				} else {
+					this.selectedDayCodes.push(code);
+				}
+				this.form.dayCodes = [...this.selectedDayCodes];
+			},
+			goNextStep() {
+				const dayCodes = this.normalizeDayCodes(this.selectedDayCodes);
+				if (!dayCodes.length) {
+					this.$u.toast('请选择日期');
+					return;
+				}
+				this.selectedDayCodes = dayCodes;
+				this.form.dayCodes = [...dayCodes];
+				const goods = uni.getStorageSync('gm_goods') || {};
+				goods.saleDays = dayCodes.join(',');
+				uni.setStorageSync('gm_goods', goods);
+				uni.navigateTo({
+					url: '/pages/groupMeal/goods/form-step2'
+				});
+			}
+		},
+		async onShow() {
+			await this.getDayList()
+			const goods = uni.getStorageSync('gm_goods') || {}
+			if (goods.saleDays) {
+				this.selectedDayCodes = this.normalizeDayCodes(goods.saleDays)
+				this.form.dayCodes = [...this.selectedDayCodes]
+			} else if (goods.dayCodes?.length) {
+				this.selectedDayCodes = this.normalizeDayCodes(goods.dayCodes)
+				this.form.dayCodes = [...this.selectedDayCodes]
+			}
+		},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.page-container {
+		min-height: 100vh;
+		background-color: #f5f5f5;
+
+		.content {
+			margin: 24rpx;
+			padding: 16rpx;
+			.title{
+				font-weight: 600;
+				font-size: 30rpx;
+				color: #333333;
+				margin-bottom: 18rpx;
+				height: 44rpx;
+				line-height: 44rpx;
+			}
+			.tips{
+				height: 40rpx;
+				line-height: 40rpx;
+				font-weight: 400;
+				font-size: 28rpx;
+				color: #333333;
+				span{
+					color: red;
+				}
+			}
+		}
+	}
+
+	.day-list {
+		display: flex;
+		flex-wrap: wrap;
+		padding: 16rpx;
+		.day-item {
+			margin: 0 38rpx 16rpx 0;
+			width: 132rpx;
+			height: 58rpx;
+			font-size: 26rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			box-sizing: border-box;
+			&:nth-child(4n) {
+				margin-right: 0;
+			}
+			&.selected {
+				color: #2983EC;
+				font-weight: bold;
+				border: 1rpx solid #2983EC;
+				background: rgba(68,154,255,0.1);
+				border-radius: 8rpx;
+			}
+		}
+	}
+
+	.footer-bar {
+		::v-deep .u-btn {
+			width: 324rpx;
+			height: 76rpx;
+			border-radius: 54rpx;
+
+			&::after {
+				display: none;
+			}
+		}
+	}
+</style>

+ 526 - 0
pages/groupMeal/goods/form-step1.vue

@@ -0,0 +1,526 @@
+<template>
+	<view class="page-container">
+		<u-navbar title="新建团餐" :autoBack="true" :placeholder="true"></u-navbar>
+		
+		<view class="form-card">
+			<view class="section-title">基础信息</view>
+			<u-form :model="form" ref="uForm" :rules="rules" 
+				labelPosition="left" labelWidth="auto"
+				:error-type="['toast', 'border-bottom']"
+			> 
+				<u-form-item prop="category" label="商品分类" required borderBottom @click="show = true">
+					<u-input v-model="form.categoryName" class="form-input" disabled placeholder="去设置" height="41" inputAlign="right"></u-input>
+					<u-icon slot="right" name="arrow-right" color="#C7C6CA"></u-icon>
+				</u-form-item>
+				
+				<u-form-item prop="name" label="商品名称" required borderBottom>
+					<u-input v-model="form.name" placeholder="请输入商品名称" height="41" inputAlign="right"></u-input>
+				</u-form-item>
+				
+				<u-form-item prop="specialCategoryId" label="特色分类" required borderBottom @click="showSpecial = true">
+					<u-input v-model="form.specialCategoryName" class="form-input" disabled placeholder="去设置" height="41" inputAlign="right"></u-input>
+					<u-icon slot="right" name="arrow-right" color="#C7C6CA"></u-icon>
+				</u-form-item>
+				
+				<u-form-item prop="productImage" label="商品封面图" required labelPosition="top">
+					<view class="upload-content">
+						<view class="upload-tip">支持jpg、png格式,建议尺寸为750px*750px,上传1-4张</view>
+						<u-upload
+							:action="uploadAction"
+							:file-list="fileList.cover"
+							:form-data="uploadFormData"
+							:max-count="4"
+							:multiple="true"
+							width="140"
+							height="140"
+							preview-full-image
+							@on-success="onCoverSuccess"
+							@on-remove="onCoverRemove"
+						/>
+					</view>
+				</u-form-item>
+
+				<u-form-item prop="detailImages" label="商品详情图" required borderBottom labelPosition="top">
+					<view class="upload-content">
+						<view class="upload-tip">支持jpg、png格式,最多20张</view>
+						<u-upload
+							:action="uploadAction"
+							:file-list="fileList.detail"
+							:form-data="uploadFormData"
+							:max-count="20"
+							:multiple="true"
+							width="140"
+							height="140"
+							preview-full-image
+							@on-success="onDetailSuccess"
+							@on-remove="onDetailRemove"
+						/>
+					</view>
+				</u-form-item>
+
+				<u-form-item prop="price" label="团餐价" required borderBottom>
+					<u-input type="digit" height="41" :clearable="false" 
+						inputAlign="right"
+						placeholder="请输入团餐价" 
+						v-model.number="form.price"
+						@input="onInput($event, 'price')"
+					></u-input>
+					<text slot="right" class="input-suffix">元</text>
+				</u-form-item>
+				
+				<u-form-item prop="sellingPrice" label="售价" required :borderBottom="true">
+					<u-input type="digit" height="41" :clearable="false" 
+					 	inputAlign="right" placeholder="请输入商品售价"
+						v-model.number="form.sellingPrice"
+						@input="onInput($event, 'sellingPrice')"
+					></u-input>
+					<text slot="right" class="input-suffix">元</text>
+				</u-form-item>
+				
+				<u-form-item prop="dailyStock" label="库存" required :borderBottom="true">
+					<u-input type="number" height="41" :clearable="false" 
+					 	inputAlign="right" placeholder="请输入每日库存"
+						v-model="form.dailyStock"
+					></u-input>
+					<text slot="right" class="input-suffix">份</text>
+				</u-form-item>
+				<view class="tips">每日库存最大量的设置,当天售完后关闭该团餐</view>
+			</u-form>
+		</view>
+
+		<view class="footer-bar">
+			<u-button type="primary" @click="goNextStep">下一步</u-button>
+		</view>
+		<!-- 商品分类选择 -->
+		<u-action-sheet :list="actionSheetList" v-model="show" @click="actionSheetCallback"></u-action-sheet>
+		<!-- 特色分类 -->
+		<u-action-sheet :list="categoryList" v-model="showSpecial" @click="specialCategoryCallback"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+import Upload from '@/components/upload/index.vue';
+import configService from '@/common/service/config.service';
+export default {
+	components: {
+		Upload,
+	},
+	data() {
+			return {
+				show: false,
+				showSpecial: false,
+				uploadAction: configService.apiUrl + '/sys/common/upload',
+				imageHttp: configService.apiUrl + '/',
+			uploadFormData: { biz: 'temp' },
+				form: {
+					name: '',
+					category: '',
+					categoryName: '',
+					specialCategoryId: '',
+					specialCategoryName: '',
+					productImage: '',
+					detailImages: '',
+					price: '',
+					sellingPrice: '',
+					dailyStock:null,
+					saleDays:''
+				},
+			categoryList:[],
+			query: {},
+			fileList: {
+				cover: [],
+				detail: [],
+			},
+			actionSheetList: [
+				{
+					text: '早餐',
+					value: '1'
+				},
+				{
+					text: '午餐',
+					value: '2'
+				},
+				{
+					text: '晚餐',
+					value: '3'
+				}
+			],
+			rules: {
+				name: [
+					{
+						required: true,
+						message: '请输入名称',
+						trigger: ['blur', 'change']
+					}
+				],
+				category: [
+					{
+						required: true,
+						message: '请选择商品分类',
+						trigger: ['blur', 'change']
+					}
+				],
+				specialCategoryId: [
+					{
+						required: true,
+						message: '请选择特色分类',
+						trigger: ['blur', 'change']
+					}
+				],
+				price: [
+					{
+						required: true,
+						type: 'number',
+						message: '请输入团餐价格',
+						trigger: ['blur', 'change']
+					},
+					{
+						asyncValidator: (rule, value, callback) => {
+							if (this.form.sellingPrice && value > this.form.sellingPrice) {
+								callback(new Error('团餐价格不能大于商品售价'));
+							} else {
+								callback();
+							}
+						},
+						trigger: ['blur', 'change']
+					},
+				],
+				sellingPrice: [
+					{
+						required: true,
+						type: 'number',
+						message: '请输入商品售价',
+						trigger: ['blur', 'change']
+					},
+					{
+						asyncValidator: (rule, value, callback) => {
+							if (this.form.price && value < this.form.price) {
+								callback(new Error('商品售价不能小于团餐价格'));
+							} else {
+								callback();
+							}
+						},
+						trigger: ['blur', 'change']
+					},
+				],
+				productImage: [
+					{
+						required: true,
+						message: '请上传商品封面图',
+						trigger: ['blur', 'change']
+					}
+				],
+				detailImages: [
+					{
+						required: true,
+						message: '请上传商品详情图片',
+						trigger: ['blur', 'change']
+					}
+				],
+				dailyStock:[
+					{
+						required: true,
+						type: 'number',
+						message: '请填写库存',
+						trigger: ['blur', 'change']
+					}
+				]
+			}
+		};
+	},
+	async onLoad(query) {
+			
+		if (query.id) {
+			this.query = query;
+			await this.getData();
+		}
+		// 清除
+		uni.removeStorageSync('gm_goods');
+		// 获取餐品分类
+		this.getClassData();
+		
+	},
+	onShow() {
+		this.getCategoryList()
+	},
+	onReady() {
+		this.$refs.uForm.setRules(this.rules);
+	},
+	methods: {
+		onCoverSuccess(res, index, lists) {
+			this.fileList.cover = lists;
+			this.processImageList(lists, 'productImage');
+		},
+		onCoverRemove(index, lists) {
+			this.fileList.cover = lists;
+			this.processImageList(lists, 'productImage');
+		},
+		onDetailSuccess(res, index, lists) {
+			this.fileList.detail = lists;
+			this.processImageList(lists, 'detailImages');
+		},
+		onDetailRemove(index, lists) {
+			this.fileList.detail = lists;
+			this.processImageList(lists, 'detailImages');
+		},
+		processImageList(lists, formKey) {
+			const urls = lists.map(item => {
+				if (item.response && item.response.message) {
+					return this.imageHttp + item.response.message;
+				}
+				if (item.url) {
+					return item.url;
+				}
+				return '';
+			}).filter(Boolean);
+			this.form[formKey] = urls.join(',');
+			console.log(this.form)
+		},
+		//获取特色分类
+		getCategoryList(){
+			this.$http.get('/groupmeal/gmSpecialCategory/listAll').then(res => {
+				console.log(res)
+				if (res.data.code === 200 && res.data.result) {
+					this.categoryList = res.data.result.map(item => {
+						return {
+							text: item.categoryName,
+							value: item.id 
+						}
+					})
+				}
+			})
+		},
+		// 编辑数据
+		getData() {
+			this.$http.get('/groupmeal/gmCustomerPackage/queryById', { params: { id: this.query.id } }).then(res => {
+				if (res.data.code === 200) {
+					let data = res.data.result;
+					// 查找商品分类名称
+					const categoryItem = this.actionSheetList.find(item => item.value == data.category);
+					// 查找特色分类名称
+					const specialCategoryItem = this.categoryList.find(item => item.value == data.specialCategoryId);
+					console.log(specialCategoryItem)
+					this.form = {
+						...data,
+						categoryName: categoryItem ? categoryItem.text : '',
+						specialCategoryId: data.specialCategoryId+'',
+						specialCategoryName: specialCategoryItem ? specialCategoryItem.text : ''
+					};
+					this.fileList = {
+						cover: data.productImage.split(',').map(item => {
+							return {
+								url: item
+							}
+						}),
+						detail: data.detailImages.split(',').map(item => {
+							return {
+								url: item
+							}
+						}),
+					};
+				}
+			});
+		},
+		// 餐品分类
+		getClassData() {
+			this.$http.get('/groupmeal/gmMerchantPackage/getGroupMealCategory', {}).then(res => {
+				if (res.data.code === 200) {
+					this.actionSheetList = res.data.result.map(item => {
+						return {
+							text: item.name,
+							value: item.code
+						}
+					})
+				}
+			});
+		},
+		// 商品分类选择回调
+		actionSheetCallback(e) {
+			this.form.category = e.value;
+			this.form.categoryName = e.text;
+		},
+		// 特色分类选择回调
+		specialCategoryCallback(e) {
+			console.log(e)
+			this.form.specialCategoryId = e.value;
+			this.form.specialCategoryName = e.text;
+		},
+		// 价格数值处理
+		onInput(value, key) {
+			// 过滤所有非数字和非小数点的字符
+			let filtered = value.replace(/[^\d.]/g, '');
+			
+			// 处理多个小数点(只保留第一个)
+			filtered = filtered.replace(/^\./g, ''); // 移除开头的小数点(避免 ".123" 情况)
+			filtered = filtered.replace(/\.{2,}/g, '.'); // 多个小数点只保留第一个
+			
+			// 分割整数和小数部分,限制小数最多两位
+			const parts = filtered.split('.');
+			if (parts.length > 1) {
+				// 小数部分只保留前两位
+				filtered = parts[0] + '.' + (parts[1].slice(0, 2) || '');
+			}
+			
+			// 处理特殊情况(如 "000" 转为 "0",但价格允许 "0.00")
+			// 若需要严格限制整数部分首位不为0(非必须,根据业务决定):
+			// if (parts.length === 1 && filtered.length > 1 && filtered[0] === '0') {
+			//   filtered = filtered.replace(/^0+/, '0');
+			// }
+			
+			// 更新绑定值
+			this.$nextTick(() => {
+				this.form[key] = !filtered ? filtered : filtered - 0;
+			});
+		},
+		// 图片处理
+		onUpload(event, type) {
+			this.fileList[type] = event;
+			this.form[type == 'cover' ? 'productImage' : 'detailImages'] = this.fileList[type].map(f => f.url).join();
+		},
+		// 下一步
+		goNextStep() {
+			// 表单校验
+			this.$refs.uForm.validate(valid => {
+				if (valid) {
+					const { categoryName, specialCategoryName, ...form } = this.form;
+					uni.setStorageSync('gm_goods', form);
+					uni.navigateTo({
+						url: '/pages/groupMeal/goods/form-day'
+					});
+				} else {
+					console.log('验证失败');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+$uni-text-color: #303133;
+$uni-color-primary: #2979ff;
+
+.page-container {
+	min-height: 100vh;
+	background-color: #f5f5f5;
+	.section-title {
+		font-weight: 500;
+		font-size: 30rpx;
+		color: #333333;
+		margin-bottom: 32rpx;
+	}
+}
+
+.form-card {
+	margin: 24rpx;
+	padding: 16rpx 16rpx;
+	background-color: #fff;
+	border-radius: 16rpx;
+
+	::v-deep .u-form-item {
+		padding: 16rpx 0;
+		line-height: 1;
+		.u-form-item--left__content {
+			flex: none;
+			.u-form-item--left__content__label {
+				font-weight: 600;
+			}
+			.u-form-item--left__content--required {
+				color: #fa3534; 
+				left: auto;
+				right: 0;
+			}
+		}
+	}
+
+	.form-input {
+		pointer-events: none;
+	}
+	::v-deep .uni-input-input:disabled {
+		pointer-events: none;
+	}
+
+	.input-suffix {
+		color: #232832;
+		font-size: 26rpx;
+		margin-left: 10rpx;
+	}
+
+	.upload-content {
+		width: 100%;
+
+		.upload-tip {
+			font-weight: 400;
+			font-size: 20rpx;
+			color: #999999;
+			margin-bottom: 20rpx;
+		}
+::v-deep .u-add-tips {
+			margin-top: 10rpx;
+		}
+	}
+}
+.tips{
+	
+	width: 100%;
+	font-weight: 400;
+	font-size: 20rpx;
+	color: #999999;
+	margin-top: 66rpx;
+	text-align: center;
+}
+
+// 底部按钮
+.footer-bar {
+	::v-deep .u-btn {
+		width: 100%;
+		height: 76rpx;
+		font-weight: 400;
+		font-size: 28rpx;
+		color: #449AFF;
+		border-radius: 54rpx;
+		border: 1rpx solid #449AFF;
+		background: #FFFFFF;
+	}
+}
+
+// 分类选择弹窗样式
+.category-popup {
+	padding: 30rpx;
+	
+	.popup-title {
+		text-align: center;
+		font-size: 34rpx;
+		font-weight: bold;
+		color: $uni-text-color;
+		margin-bottom: 30rpx;
+	}
+	
+	.category-list {
+		max-height: 600rpx; // 限制高度,可滚动
+	}
+	
+	.category-item {
+		padding: 20rpx 0;
+		font-size: 30rpx;
+		color: $uni-text-color;
+		border-bottom: 1rpx solid #eee;
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		
+		&:last-child {
+			border-bottom: none;
+		}
+		
+		&.selected {
+			color: $uni-color-primary;
+			font-weight: bold;
+		}
+	}
+	
+	.popup-footer {
+		padding-top: 30rpx;
+	}
+}
+</style>

+ 439 - 0
pages/groupMeal/goods/form-step2.vue

@@ -0,0 +1,439 @@
+<template>
+	<view class="page-container">
+		<u-navbar title="新建团餐" :autoBack="true" :placeholder="true"></u-navbar>
+
+		<u-form :model="form" :rules="rules" ref="formRef" :error-type="['toast', 'border-bottom']">
+			<u-collapse ref="collapseRef" :value="activeNames" @change="onCollapseChange">
+				<u-collapse-item :class="{
+						active: activeNames == category.id
+					}" v-for="(category, catIndex) in form.dishCategories" :key="category.id" :name="category.id" @click="onClick">
+					<view slot="title" class="collapse-title">
+						<text>{{ category.name }}</text>
+						<text class="num" v-if="isEditedShow(category)">({{ category.items.length }})</text>
+						<u-switch v-if="false" size="20" activeColor="#3c9cff" :value="category.isActive"
+							@change="onSwitchChange($event, category.id)"></u-switch>
+					</view>
+
+					<view class="collapse-content">
+						<view class="dish-item" v-for="(dish, dishIndex) in category.items" :key="dish.id">
+							<view class="dish-header">
+								<view class="dish-title">
+									<u-image width="32rpx" height="32rpx" src="/static/icon/icon_edit.png"></u-image>
+									<text>{{ category.name }}{{ dishIndex + 1 }}</text>
+								</view>
+
+							</view>
+							<view class="dish-form">
+								<u-form-item label="名称" :prop="`dishCategories.${catIndex}.items.${dishIndex}.name`"
+									:required="isEdited(dish)">
+									<u-input v-model="dish.name" height="41" placeholder="请输入" inputAlign="right"></u-input>
+								</u-form-item>
+								<u-form-item label="价格" :prop="`dishCategories.${catIndex}.items.${dishIndex}.price`"
+									:required="isEdited(dish)">
+									<u-input v-model.number="dish.price" type="digit" height="41" placeholder="输入金额" :clearable="false"
+										inputAlign="right"></u-input>
+									<text slot="right" style="color: #333333;">元</text>
+								</u-form-item>
+								<u-form-item label="描述" :prop="`dishCategories.${catIndex}.items.${dishIndex}.description`"
+									:required="isEdited(dish)">
+									<u-input v-model="dish.description" height="41" placeholder="请描述菜品的使用素材" inputAlign="right"></u-input>
+								</u-form-item>
+								
+							</view>
+							<view class="dish-actions">
+								<u-button v-if="category.items.length > 1" type="error" size="mini" hover-class="none" :custom-style="{
+										color: '#FF6067',
+										border: '1rpx solid #FF6067',
+										background: 'rgba(255,96,103,0.1)',
+									}" @click="removeDish(category.id, dish.id)">
+									<view class="text">
+										<u-image width="32rpx" height="32rpx" src="/static/icon/icon_del.png"></u-image>
+										<text>删除</text>
+									</view>
+								</u-button>
+								<u-button v-if="dishIndex === category.items.length - 1" type="primary" size="mini" hover-class="none"
+									:custom-style="{
+										color: '#2D88F4',
+										border: '1rpx solid #2D88F4',
+										background: 'rgba(45,136,244,0.1)',
+									}" @click="addDish(category.id)">
+									<view class="text">
+										<u-image width="32rpx" height="32rpx" src="/static/icon/icon_plus.png"></u-image>
+										<text>添加</text>
+									</view>
+								</u-button>
+								
+							</view>
+						</view>
+					</view>
+				</u-collapse-item>
+			</u-collapse>
+		</u-form>
+
+		<view class="footer-bar">
+			<u-button :custom-style="{
+					width: '205rpx',
+					fontWeight: '400',
+					fontSize: '28rpx',
+					color: '#449AFF',
+					border: '1rpx solid #449AFF',
+					background: '#fff',
+					marginRight: '24rpx'
+				}" @click="goBack">上一步</u-button>
+			<u-button :custom-style="{
+					fontWeight: '400',
+					fontSize: '28rpx',
+					color: '#FFFFFF',
+					background: 'linear-gradient(90deg, #77B6FF 0%, #449AFF 100%)',
+				}" @click="submit">提交审核</u-button>
+		</view>
+	</view>
+</template>
+
+<script>
+	let dishIdCounter = 100; // 简单的唯一ID生成器
+	export default {
+		data() {
+			return {
+				// 数据
+				form: {
+					dishCategories: [{
+							id: 1,
+							name: '荤菜',
+							isActive: false,
+							items: [{
+								id: dishIdCounter++,
+								name: '',
+								price: '',
+								description: ''
+							}]
+						},
+						{
+							id: 2,
+							name: '素菜',
+							isActive: false,
+							items: [{
+								id: dishIdCounter++,
+								name: '',
+								price: '',
+								description: ''
+							}]
+						},
+						{
+							id: 3,
+							name: '主食',
+							isActive: false,
+							items: [{
+								id: dishIdCounter++,
+								name: '',
+								price: '',
+								description: ''
+							}]
+						},
+						{
+							id: 4,
+							name: '汤',
+							isActive: false,
+							items: [{
+								id: dishIdCounter++,
+								name: '',
+								price: '',
+								description: ''
+							}]
+						},
+						{
+							id: 5,
+							name: '其他',
+							isActive: false,
+							items: [{
+								id: dishIdCounter++,
+								name: '',
+								price: '',
+								description: ''
+							}]
+						}
+					],
+				},
+				rules: {},
+				activeNames: [1], // 控制折叠面板展开的name数组
+			};
+		},
+		watch: {
+			'form.dishCategories': {
+				handler() {
+					this.$nextTick(() => {
+						this.updateValidationRules();
+					});
+				},
+				deep: true
+			}
+		},
+		onLoad() {
+			const goods = uni.getStorageSync('gm_goods');
+			if (goods.gmMerchantPackageDetails && goods.gmMerchantPackageDetails.length > 0) {
+				this.form.dishCategories.map(a => {
+					let items = goods.gmMerchantPackageDetails.filter(b => b.category == a.id);
+					if (items.length > 0) a.items = items;
+				});
+			}
+		},
+		mounted() {
+			this.$nextTick(() => {
+				this.$refs.formRef.setRules(this.rules);
+			});
+		},
+		methods: {
+			// 判断一个菜品是否被编辑过
+			isEdited(dish) {
+				return !!(dish.name || dish.price || dish.description);
+			},
+			isEditedShow(e) {
+				const goods = uni.getStorageSync('gm_goods');
+				return goods.gmMerchantPackageDetails && goods.gmMerchantPackageDetails.length > 0 && goods
+					.gmMerchantPackageDetails.some(b => b.category == e.id);;
+			},
+			updateValidationRules() {
+				if (!this.$refs.formRef) return;
+
+				const newRules = {};
+				this.form.dishCategories.forEach((category, catIndex) => {
+					category.items.forEach((dish, dishIndex) => {
+						if (this.isEdited(dish)) {
+							// 如果菜品的任一字段被填写,则所有字段都变为必填
+							const nameProp = `dishCategories.${catIndex}.items.${dishIndex}.name`;
+							const priceProp = `dishCategories.${catIndex}.items.${dishIndex}.price`;
+							const descProp = `dishCategories.${catIndex}.items.${dishIndex}.description`;
+
+							newRules[nameProp] = [{
+								required: true,
+								message: '请输入名称',
+								trigger: ['blur', 'change']
+							}];
+							newRules[priceProp] = [{
+								required: true,
+								type: 'number',
+								message: '请输入价格',
+								trigger: ['blur', 'change']
+							}];
+							newRules[descProp] = [{
+								required: true,
+								message: '请输入描述',
+								trigger: ['blur', 'change']
+							}];
+						}
+					});
+				});
+
+				this.rules = newRules;
+				this.$refs.formRef.setRules(this.rules);
+			},
+			// Switch开关状态变化
+			onSwitchChange(isActive, categoryId) {
+				const index = this.activeNames.indexOf(categoryId);
+				if (isActive) {
+					if (index === -1) {
+						this.activeNames.push(categoryId);
+					}
+				} else {
+					if (index > -1) {
+						this.activeNames.splice(index, 1);
+					}
+				}
+			},
+			// 折叠面板状态变化
+			onCollapseChange(names) {
+				console.log(names)
+				this.activeNames = names;
+			},
+			// 添加菜品
+			addDish(categoryId) {
+				const category = this.form.dishCategories.find(c => c.id === categoryId);
+				if (category) {
+					category.items.push({
+						id: dishIdCounter++,
+						name: '',
+						price: '',
+						description: ''
+					});
+				}
+			},
+			// 删除菜品
+			removeDish(categoryId, dishId) {
+				const category = this.form.dishCategories.find(c => c.id === categoryId);
+				if (category && category.items.length > 1) {
+					const index = category.items.findIndex(d => d.id === dishId);
+					if (index > -1) {
+						category.items.splice(index, 1);
+					}
+				}
+			},
+			// 上一步
+			goBack() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			// 提交审核
+			submit() {
+				this.$refs.formRef.validate().then(res => {
+					const goods = uni.getStorageSync('gm_goods');
+					const activeCategories = this.form.dishCategories.filter(c => c.items.some(this.isEdited));
+					const details = [];
+					// 遍历一级分类
+					activeCategories.forEach(category => {
+						// 检查是否有items属性且为数组
+						if (category.items && Array.isArray(category.items)) {
+							// 遍历二级菜品
+							category.items.forEach(item => {
+								// 创建新对象,只保留需要的属性
+								if (this.isEdited(item)) {
+									details.push({
+										category: category.id,
+										name: item.name,
+										price: item.price,
+										description: item.description
+									});
+								}
+							});
+						}
+					});
+					let params = {
+						...goods,
+						gmMerchantPackageDetails: [...details]
+					};
+					console.log('表单校验成功, 提交的数据:', activeCategories, params);
+					this.$http.post('/groupmeal/gmMerchantPackage/submit', params).then(res => {
+						let type = res.data.code === 200 ? 'success' : 'error';
+						this.$tip[type](res.data.message);
+						if (res.data.code === 200) {
+							// 清除
+							uni.removeStorageSync('gm_goods');
+							// 返回列表
+							uni.navigateBack({
+								delta: 3
+							});
+						}
+					});
+				}).catch(errors => {
+					console.log('校验失败', errors);
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.page-container {
+		min-height: 100vh;
+		background-color: #f5f5f5;
+		padding-bottom: 160rpx;
+
+		::v-deep .u-collapse-item {
+			margin-bottom: 16rpx;
+
+			.u-collapse-head {
+				padding: 20rpx 32rpx;
+				background-color: #fff;
+			}
+
+			&.active {
+				.u-collapse-body {
+					height: auto !important;
+				}
+			}
+		}
+
+		.collapse-title {
+			font-weight: 500;
+			font-size: 32rpx;
+			color: #333333;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+
+			.num {
+				padding: 0 10rpx;
+			}
+		}
+
+		.collapse-content {
+			.dish-item {
+				margin-top: 16rpx;
+				padding: 20rpx 32rpx;
+				background: #FFFFFF;
+				box-shadow: 0 2rpx 8rpx 0 rgba(214, 225, 237, 0.1);
+				border: 1rpx solid #F0F3F5;
+				border-radius: 16rpx;
+
+				.dish-header {
+					margin-bottom: 20rpx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+
+					.dish-title {
+						font-size: 28rpx;
+						color: #333333;
+						display: flex;
+						align-items: center;
+					}
+
+				}
+
+				.dish-form {
+					padding: 6rpx 16rpx;
+					border-radius: 8rpx;
+					background: #F7F8FC;
+				}
+
+				::v-deep .u-form-item {
+					padding: 16rpx 0;
+					line-height: 1;
+
+					.u-form-item--left__content {
+						flex: none;
+					}
+				}
+			}
+		}
+	}
+
+	.dish-actions {
+		width: 100%;
+		text-align: end;
+		margin-top: 16rpx;
+		// display: flex;
+		// align-items: center;
+		// justify-content: end;
+		// gap: 16rpx;
+		
+
+		::v-deep .u-btn {
+			font-weight: 400;
+			font-size: 22rpx;
+			margin-left: 16rpx;
+		}
+
+		.text {
+			display: flex;
+			align-items: center;
+
+			text {
+				line-height: 1;
+				margin-left: 4rpx;
+			}
+		}
+	}
+
+	.footer-bar {
+		::v-deep .u-btn {
+			width: 100%;
+			height: 76rpx;
+			border-radius: 54rpx;
+
+			&::after {
+				display: none;
+			}
+		}
+	}
+</style>

+ 1 - 1
pages/groupMeal/goods/list.vue

@@ -328,7 +328,7 @@ export default {
 		addGoods() {
 			this.isManaging = false;
 			uni.navigateTo({
-				url: '/pages/groupMeal/goods/form'
+				url: '/pages/groupMeal/goods/form-step1'
 			});
 		},
 		// 编辑商品

+ 8 - 6
pages/groupMeal/orders/order-alist.vue

@@ -5,7 +5,7 @@
 			<view class="navbar-title">团餐订单</view>
 			<view slot="right" class="navbar-right">
 				<text class="nav-btn" @click="goToDetail">订单明细</text>
-				<text class="nav-btn manage-btn" v-if="currentStatusTab == 0" @click="toggleManageMode">{{ isManaging ? '退出管理' : '批量管理' }}</text>
+				<!-- <text class="nav-btn manage-btn" v-if="currentStatusTab == 0" @click="toggleManageMode">{{ isManaging ? '退出管理' : '批量管理' }}</text> -->
 			</view>
 		</u-navbar>
 
@@ -27,12 +27,14 @@
 		<u-tabs :list="statusTabs" 
 			:current="currentStatusTab" 
 			inactive-color="#666666"
+			:bar-width="72"
 			:bar-style="{
 				bottom: '20rpx',
+				marginLeft: '-12rpx',
 				background: 'linear-gradient(90deg, #82BCFF 0%, rgba(130,188,255,0) 100%)'
 			}"
 			:active-item-style="{
-				fontWeight: '500',
+				fontWeight: '600',
 				fontSize: '32rpx',
 				color: '#000000',
 			}"		
@@ -40,7 +42,7 @@
 		></u-tabs>
 
 		<!-- 订单列表 -->
-		<view class="order-scroll-view" :style="{ paddingBottom: isManaging ? '120rpx' : '0' }">
+		<view class="order-scroll-view" >
 			<ListScrollView ref="listScroll" 
 				:api="api"
 				:init="{
@@ -73,8 +75,8 @@
 										<view style="font-size: 24rpx;">{{ item.createTime }}</view>
 									</view>
 								</view>
-								<view class="order-actions" v-if="isManaging">
-									<u-checkbox :name="item.hostOrderId" shape="circle" v-model="item.checked"></u-checkbox>
+								<view class="order-actions" >
+									<u-checkbox :name="item.orderId" shape="circle" v-model="item.checked"></u-checkbox>
 								</view>
 							</view>
 							
@@ -100,7 +102,7 @@
 		</view>
 
 		<!-- 批量操作底部栏 -->
-		<view class="footer-bar" v-if="isManaging">
+		<view class="footer-bar" >
 			<u-checkbox-group v-model="isAllSelected" placement="row">
 				<u-checkbox name="all" shape="circle" v-model="isAllSelected" @change="onSelectAll">全选</u-checkbox>
 			</u-checkbox-group>

+ 9 - 2
pages/order/record.vue

@@ -204,8 +204,15 @@
 			},
 			// 同意退款
 			agree(item){
+				if (!item || !item.reOrderId) {
+					uni.showToast({
+						icon: 'none',
+						title: '退款单号为空'
+					})
+					return
+				}
 				let data = {
-					reOrderId:this.reOrderId
+					reOrderId:item.reOrderId
 				}
 				this.$http.post("/app/order/locaOrder/agreeRefund",data).then(res => {
 					console.log(res)
@@ -506,4 +513,4 @@
 			}
 		}
 	}
-</style>
+</style>

BIN
static/groupMeal/upload.png