Przeglądaj źródła

feat(group-package):重构团购套餐管理功能

- 修改获取套餐列表接口为 getThaliList-优化套餐分组的展开/收起逻辑,支持一键控制所有分组- 更新菜品字段映射,如 dishId 改为 detailId、quantity 改为 qty
- 增强表单验证规则,确保分组和菜品信息完整性
- 调整标签页显示逻辑,根据审核状态动态过滤展示内容
-修复新建团购页面样式及交互细节问题
congxuesong 1 miesiąc temu
rodzic
commit
3d5f3e2db6

+ 2 - 2
src/api/modules/groupPackageManagement.ts

@@ -6,8 +6,8 @@ import http from "@/api";
  * @name 商铺用户模块
  */
 // 获取商铺用户列表
-export const getStoreUserList = (params: StoreUser.ReqUserParams) => {
-  return http.get<ResPage<StoreUser.ResStoreUserList>>(PORT_NONE + `/store/user/getStoreUserList`, params);
+export const getThaliList = params => {
+  return http.get<ResPage<StoreUser.ResStoreUserList>>(PORT_NONE + `/PcGroupBuy/getThaliList`, params);
 };
 
 // 新增商家端用户

+ 49 - 11
src/views/groupPackageManagement/index.vue

@@ -5,8 +5,8 @@
       <template #tableHeader="scope">
         <div class="table-header-btn">
           <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying"> 新建团购 </el-button>
-          <el-tabs v-model="activeName" class="tabs" @tab-click="handleClick">
-            <el-tab-pane v-for="tab in tabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
+          <el-tabs v-if="showTabs" v-model="activeName" class="tabs" @tab-click="handleClick">
+            <el-tab-pane v-for="tab in filteredTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />
           </el-tabs>
         </div>
       </template>
@@ -60,7 +60,7 @@
 </template>
 
 <script setup lang="tsx" name="groupPackageManagement">
-import { onActivated, onMounted, reactive, ref } from "vue";
+import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter } from "vue-router";
 import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
@@ -68,6 +68,7 @@ import ProTable from "@/components/ProTable/index.vue";
 import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
 import { Plus } from "@element-plus/icons-vue";
 import { audit, getStaffConfigList } from "@/api/modules/staffConfig";
+import { getThaliList } from "@/api/modules/groupPackageManagement";
 
 const router = useRouter();
 const dialogFormVisible = ref(false);
@@ -92,11 +93,14 @@ const rules = reactive<FormRules<RuleForm>>({
   ]
 });
 const statusEnum = [
-  { value: "0", label: "待审核" },
-  { value: "1", label: "审核通过" },
-  { value: "2", label: "审核拒绝" }
+  { value: "-1", label: "待审核" },
+  { value: "-2", label: "审核通过" },
+  { value: "0", label: "审核驳回" }
 ];
 
+// ProTable 实例(需要在使用它的地方之前定义)
+const proTable = ref<ProTableInstance>();
+
 // 表格配置项
 const columns = reactive<ColumnProps<any>[]>([
   {
@@ -170,7 +174,7 @@ const columns = reactive<ColumnProps<any>[]>([
 ]);
 
 // 在 script setup 中添加
-const tabOptions = [
+const allTabOptions = [
   { label: "全部", name: "0" },
   { label: "草稿", name: "1" },
   { label: "进行中", name: "2" },
@@ -179,14 +183,48 @@ const tabOptions = [
   { label: "已售罄", name: "5" },
   { label: "已结束", name: "6" }
 ];
+
+// 获取当前审核状态
+const currentAuditStatus = computed(() => {
+  if (!proTable.value?.searchParam) return "";
+  return proTable.value.searchParam.tradeStatus || "";
+});
+
+// 控制 el-tabs 是否显示:当审核状态为空或审核通过时显示
+const showTabs = computed(() => {
+  const status = currentAuditStatus.value;
+  return !status || status === "-2";
+});
+
+// 根据审核状态过滤 tabOptions:如果审核状态为空,只显示草稿;审核通过时,显示除草稿外的所有标签页
+const filteredTabOptions = computed(() => {
+  const status = currentAuditStatus.value;
+  if (!status) {
+    // 审核状态为空时,只显示草稿
+    return allTabOptions;
+  } else if (status === "-2") {
+    // 审核通过时,显示除草稿外的所有标签页
+    return allTabOptions.filter(tab => tab.name !== "1");
+  }
+  return [];
+});
+
+// 监听审核状态变化
+watch(
+  currentAuditStatus,
+  newStatus => {
+    if (!newStatus || newStatus === "-2") {
+      // 审核状态为空时,确保 activeName 为草稿
+      activeName.value = "0";
+    }
+  },
+  { immediate: true }
+);
 // 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
 const initParam = reactive({
   activeName: activeName
 });
 
-// ProTable 实例
-const proTable = ref<ProTableInstance>();
-
 // 页面加载时触发查询
 onMounted(() => {
   proTable.value?.getTableList();
@@ -210,7 +248,7 @@ const dataCallback = (data: any) => {
 // 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
-  return getStaffConfigList(newParams);
+  return getThaliList(newParams);
 };
 
 // 跳转详情页

+ 294 - 153
src/views/groupPackageManagement/newGroup.vue

@@ -80,9 +80,18 @@
               <div class="package-content-wrapper">
                 <div class="package-content-header">
                   <el-button type="primary" @click="addGroup" class="add-group-btn"> 添加分组 </el-button>
+                  <el-button
+                    type="primary"
+                    link
+                    @click="toggleAllGroupsCollapse"
+                    :icon="allGroupsCollapsed ? ArrowDown : ArrowUp"
+                    v-show="lifeGroupBuyThalis.length > 1"
+                  >
+                    {{ allGroupsCollapsed ? "展开全部" : "收起全部" }}
+                  </el-button>
                 </div>
                 <div class="package-content-container">
-                  <div v-for="(group, groupIndex) in storeInfoModel.lifeGroupBuyThalis" :key="groupIndex" class="package-group">
+                  <div v-for="(group, groupIndex) in lifeGroupBuyThalis" :key="groupIndex" class="package-group">
                     <div class="package-group-header">
                       <div class="header-right">
                         <el-button
@@ -90,25 +99,17 @@
                           link
                           @click="removeGroup(groupIndex)"
                           :icon="Delete"
-                          v-show="storeInfoModel.lifeGroupBuyThalis.length > 1"
+                          v-show="lifeGroupBuyThalis.length > 1"
                         >
                           删除
                         </el-button>
-                        <el-button
-                          type="primary"
-                          link
-                          @click="toggleGroupCollapse(groupIndex)"
-                          :icon="group.collapsed ? ArrowDown : ArrowUp"
-                        >
-                          {{ group.collapsed ? "展开" : "收起" }}
-                        </el-button>
                       </div>
                     </div>
                     <transition name="slide-fade">
-                      <div v-show="!group.collapsed" class="package-group-content">
+                      <div v-show="!allGroupsCollapsed" class="package-group-content">
                         <div class="category-row">
                           <span class="label">类别</span>
-                          <el-input v-model="group.category" placeholder="请输入" clearable />
+                          <el-input v-model="group.groupName" placeholder="请输入" clearable />
                         </div>
                         <div v-for="(dish, dishIndex) in group.dishes" :key="dishIndex" class="dish-row">
                           <span class="label">菜品</span>
@@ -119,7 +120,7 @@
                             <span v-else class="dish-placeholder">请选择</span>
                           </div>
                           <span class="label">数量</span>
-                          <el-input v-model="dish.quantity" placeholder="请输入" clearable class="quantity-input" />
+                          <el-input v-model="dish.qty" placeholder="请输入" clearable class="quantity-input" />
                           <el-button
                             :icon="Delete"
                             link
@@ -306,7 +307,7 @@
     <div class="button-container">
       <el-button @click="goBack"> 返回 </el-button>
       <el-button @click="handleSubmit('cg')"> 存草稿 </el-button>
-      <el-button type="primary" @click="handleSubmit()"> 确定 </el-button>
+      <el-button type="primary" @click="handleSubmit"> 确定 </el-button>
     </div>
     <!-- 图片预览对话框 -->
     <el-dialog v-model="imagePopupVisible" title="预览" width="30%">
@@ -322,7 +323,7 @@
           class="dish-search-input"
           @input="filterDishList"
         />
-        <div class="dish-list-container">
+        <el-scrollbar height="400px" class="dish-list-container">
           <template v-if="filteredDishList.length > 0">
             <div
               v-for="dish in filteredDishList"
@@ -338,7 +339,7 @@
               </div>
               <div class="dish-info">
                 <div class="dish-name">
-                  {{ dish.name }}
+                  {{ dish.dishName }}
                 </div>
                 <div class="dish-price">¥{{ dish.dishPrice }}/{{ dish.dishesUnit }}</div>
               </div>
@@ -348,7 +349,7 @@
             <div class="empty-text">暂无可选择菜品</div>
             <div class="empty-hint">请去全部-门店装修-菜品管理中添加</div>
           </div>
-        </div>
+        </el-scrollbar>
       </div>
       <template #footer>
         <div class="dialog-footer">
@@ -508,50 +509,61 @@ const rules = reactive({
       trigger: "blur"
     }
   ],
-  // lifeGroupBuyThalis: [
-  //   {
-  //     required: true,
-  //     validator: (rule: any, value: any, callback: any) => {
-  //       if (!value || value.length === 0) {
-  //         callback(new Error("请至少添加一个套餐分组"));
-  //         return;
-  //       }
-  //       for (let i = 0; i < value.length; i++) {
-  //         const group = value[i];
-  //         if (!group.category || group.category.trim() === "") {
-  //           callback(new Error(`第${i + 1}个分组的类别不能为空`));
-  //           return;
-  //         }
-  //         if (!group.dishes || group.dishes.length === 0) {
-  //           callback(new Error(`第${i + 1}个分组至少需要添加一个菜品`));
-  //           return;
-  //         }
-  //         for (let j = 0; j < group.dishes.length; j++) {
-  //           const dish = group.dishes[j];
-  //           if (!dish.dishId) {
-  //             callback(new Error(`第${i + 1}个分组的第${j + 1}个菜品未选择`));
-  //             return;
-  //           }
-  //           if (!dish.quantity || dish.quantity.toString().trim() === "") {
-  //             callback(new Error(`第${i + 1}个分组的第${j + 1}个菜品数量不能为空`));
-  //             return;
-  //           }
-  //           const quantityNum = Number(dish.quantity);
-  //           if (isNaN(quantityNum) || quantityNum <= 0) {
-  //             callback(new Error(`第${i + 1}个分组的第${j + 1}个菜品数量必须为正整数`));
-  //             return;
-  //           }
-  //           if (!Number.isInteger(quantityNum)) {
-  //             callback(new Error(`第${i + 1}个分组的第${j + 1}个菜品数量必须为正整数`));
-  //             return;
-  //           }
-  //         }
-  //       }
-  //       callback();
-  //     },
-  //     trigger: "blur"
-  //   }
-  // ],
+  lifeGroupBuyThalis: [
+    {
+      required: true,
+      validator: (rule: any, value: any, callback: any) => {
+        try {
+          // 验证独立的 lifeGroupBuyThalis 变量
+          const thalis = lifeGroupBuyThalis.value;
+          if (!thalis || thalis.length === 0) {
+            callback(new Error("请至少添加一个套餐分组"));
+            return;
+          }
+          // 如果设置了跳过最后一个分组验证的标记,则只验证除最后一个分组外的所有分组
+          const groupsToValidate = skipLastGroupValidation && thalis.length > 1 ? thalis.slice(0, thalis.length - 1) : thalis;
+
+          for (let i = 0; i < groupsToValidate.length; i++) {
+            const group = groupsToValidate[i];
+            const groupIndex = i;
+            if (!group.groupName || group.groupName.trim() === "") {
+              callback(new Error(`第${groupIndex + 1}个分组的类别不能为空`));
+              return;
+            }
+            if (!group.dishes || group.dishes.length === 0) {
+              callback(new Error(`第${groupIndex + 1}个分组至少需要添加一个菜品`));
+              return;
+            }
+            for (let j = 0; j < group.dishes.length; j++) {
+              const dish = group.dishes[j];
+              if (!dish.detailId) {
+                callback(new Error(`第${groupIndex + 1}个分组的第${j + 1}个菜品未选择`));
+                return;
+              }
+              if (!dish.qty || dish.qty.toString().trim() === "") {
+                callback(new Error(`第${groupIndex + 1}个分组的第${j + 1}个菜品数量不能为空`));
+                return;
+              }
+              const quantityNum = Number(dish.qty);
+              if (isNaN(quantityNum) || quantityNum <= 0) {
+                callback(new Error(`第${groupIndex + 1}个分组的第${j + 1}个菜品数量必须为正整数`));
+                return;
+              }
+              if (!Number.isInteger(quantityNum)) {
+                callback(new Error(`第${groupIndex + 1}个分组的第${j + 1}个菜品数量必须为正整数`));
+                return;
+              }
+            }
+          }
+          callback();
+        } catch (error) {
+          // 如果验证过程中出错,直接通过验证,避免报错
+          callback();
+        }
+      },
+      trigger: "blur"
+    }
+  ],
   originalPrice: [
     { required: true, message: "请输入原价" },
     {
@@ -759,23 +771,8 @@ const storeInfoModel = ref<any>({
   quotaValueStr: 0,
   // 自定义限购数量(当quotaType为1时必填)
   quotaValue: 0,
-  // 套餐内容(数组,每个元素是一个分组)
-  lifeGroupBuyThalis: [
-    {
-      category: "", // 类别
-      collapsed: false, // 是否收起
-      dishes: [
-        // 菜品列表
-        {
-          dishId: "", // 菜品ID
-          dishName: "", // 菜品名称
-          dishPrice: "", // 菜品价格
-          dishImage: "", // 菜品图片
-          quantity: "" // 数量
-        }
-      ]
-    }
-  ],
+  // 套餐内容(虚拟字段,用于表单验证,实际数据在 lifeGroupBuyThalis 变量中)
+  lifeGroupBuyThalis: null,
   // 原价
   originalPrice: "",
   // 优惠价
@@ -851,34 +848,43 @@ const weekdayList = ref([
 ]);
 
 // 节日选项列表
-const holidayList = ref([
-  { value: 1, label: "元旦" },
-  { value: 2, label: "春节" },
-  { value: 3, label: "情人节" },
-  { value: 4, label: "元宵节" },
-  { value: 5, label: "清明节" },
-  { value: 6, label: "劳动节" },
-  { value: 7, label: "儿童节" },
-  { value: 8, label: "端午节" },
-  { value: 9, label: "七夕" },
-  { value: 10, label: "中秋节" },
-  { value: 11, label: "国庆节" },
-  { value: 12, label: "冬至" },
-  { value: 13, label: "平安夜" },
-  { value: 14, label: "圣诞节" }
-]);
+const holidayList: any = ref([]);
 
 // 菜品选项列表(这里需要根据实际API获取,暂时使用示例数据)
 const dishOptions = ref([]);
 
+// 套餐内容(数组,每个元素是一个分组)
+const lifeGroupBuyThalis = ref([
+  {
+    groupName: "", // 类别
+    dishes: [
+      // 菜品列表
+      {
+        detailId: "", // 菜品ID
+        dishName: "", // 菜品名称
+        dishPrice: "", // 菜品价格
+        dishImage: "", // 菜品图片
+        qty: "", // 数量
+        dishesUnit: ""
+      }
+    ]
+  }
+]);
+
 // 菜品选择对话框相关
 const dishDialogVisible = ref(false);
 const dishSearchKeyword = ref("");
-const filteredDishList = ref([]);
+const filteredDishList: any = ref([]);
 const selectedDishId = ref<number | null>(null);
 const currentDishGroupIndex = ref<number>(-1);
 const currentDishIndex = ref<number>(-1);
 
+// 套餐内容整体展开收起状态
+const allGroupsCollapsed = ref(false);
+
+// 标记是否跳过最后一个分组的验证(用于添加新分组时)
+let skipLastGroupValidation = false;
+
 // ==================== 监听器 ====================
 
 /**
@@ -965,7 +971,7 @@ onMounted(async () => {
   };
   let res = await getHolidayList(params);
   if (res && res.code == 200) {
-    holidayList.value = res.data;
+    holidayList.value = res.data.records;
   }
   if (type.value != "add") {
     let res: any = await getStoreDetail({ id: id.value } as any);
@@ -982,37 +988,36 @@ onMounted(async () => {
       storeInfoModel.value.unavailableHolidays = [];
     }
     // 确保套餐内容字段存在
-    if (!storeInfoModel.value.lifeGroupBuyThalis || storeInfoModel.value.lifeGroupBuyThalis.length === 0) {
-      storeInfoModel.value.lifeGroupBuyThalis = [
+    if (res.data.lifeGroupBuyThalis && res.data.lifeGroupBuyThalis.length > 0) {
+      lifeGroupBuyThalis.value = res.data.lifeGroupBuyThalis;
+      // 确保每个分组都有必要的字段
+      lifeGroupBuyThalis.value.forEach((group: any) => {
+        if (!group.dishes || group.dishes.length === 0) {
+          group.dishes = [
+            {
+              detailId: "",
+              qty: ""
+            }
+          ];
+        }
+      });
+    } else {
+      // 如果没有数据,使用默认值
+      lifeGroupBuyThalis.value = [
         {
-          category: "",
-          collapsed: false,
+          groupName: "",
           dishes: [
             {
-              dishId: "",
+              detailId: "",
               dishName: "",
               dishPrice: "",
               dishImage: "",
-              quantity: ""
+              qty: "",
+              dishesUnit: ""
             }
           ]
         }
       ];
-    } else {
-      // 确保每个分组都有必要的字段
-      storeInfoModel.value.lifeGroupBuyThalis.forEach((group: any) => {
-        if (group.collapsed === undefined) {
-          group.collapsed = false;
-        }
-        if (!group.dishes || group.dishes.length === 0) {
-          group.dishes = [
-            {
-              dishId: "",
-              quantity: ""
-            }
-          ];
-        }
-      });
     }
     // 确保自定义不可用日期字段存在
     if (storeInfoModel.value.unavailableDates === 2) {
@@ -1152,22 +1157,35 @@ const toggleHoliday = value => {
  * 添加套餐分组
  */
 const addGroup = () => {
-  if (!storeInfoModel.value.lifeGroupBuyThalis) {
-    storeInfoModel.value.lifeGroupBuyThalis = [];
+  if (!lifeGroupBuyThalis.value) {
+    lifeGroupBuyThalis.value = [];
   }
-  storeInfoModel.value.lifeGroupBuyThalis.push({
-    category: "",
-    collapsed: false,
+
+  // 记录旧分组的数量,用于后续只验证旧分组
+  const oldGroupsCount = lifeGroupBuyThalis.value.length;
+
+  // 添加新分组
+  lifeGroupBuyThalis.value.push({
+    groupName: "",
     dishes: [
       {
-        dishId: "",
+        detailId: "",
         dishName: "",
         dishPrice: "",
         dishImage: "",
-        quantity: ""
+        qty: "",
+        dishesUnit: ""
       }
     ]
   });
+
+  // 清除表单验证状态,避免新分组显示验证错误
+  // 只清除验证状态,不重新触发验证
+  // 让用户操作时(如点击添加菜品)再触发验证
+  nextTick(() => {
+    ruleFormRef.value?.clearValidate("lifeGroupBuyThalis");
+    skipLastGroupValidation = false;
+  });
 };
 
 /**
@@ -1175,7 +1193,7 @@ const addGroup = () => {
  * @param groupIndex 分组索引
  */
 const removeGroup = (groupIndex: number) => {
-  if (storeInfoModel.value.lifeGroupBuyThalis.length <= 1) {
+  if (lifeGroupBuyThalis.value.length <= 1) {
     ElMessage.warning("至少需要保留一个分组");
     return;
   }
@@ -1185,18 +1203,17 @@ const removeGroup = (groupIndex: number) => {
     type: "warning"
   })
     .then(() => {
-      storeInfoModel.value.lifeGroupBuyThalis.splice(groupIndex, 1);
+      lifeGroupBuyThalis.value.splice(groupIndex, 1);
       ElMessage.success("删除成功");
     })
     .catch(() => {});
 };
 
 /**
- * 切换分组收起/展开状态
- * @param groupIndex 分组索引
+ * 切换所有分组的收起/展开状态
  */
-const toggleGroupCollapse = (groupIndex: number) => {
-  storeInfoModel.value.lifeGroupBuyThalis[groupIndex].collapsed = !storeInfoModel.value.lifeGroupBuyThalis[groupIndex].collapsed;
+const toggleAllGroupsCollapse = () => {
+  allGroupsCollapsed.value = !allGroupsCollapsed.value;
 };
 
 /**
@@ -1204,15 +1221,76 @@ const toggleGroupCollapse = (groupIndex: number) => {
  * @param groupIndex 分组索引
  */
 const addDish = (groupIndex: number) => {
-  if (!storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes) {
-    storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes = [];
+  const group = lifeGroupBuyThalis.value[groupIndex];
+
+  // 验证当前分组是否填写完整
+  // 验证类别
+  if (!group.groupName || group.groupName.trim() === "") {
+    ElMessage.warning("请先完成现有菜品的填写");
+    // 触发表单验证,显示验证错误
+    nextTick(() => {
+      ruleFormRef.value?.validateField("lifeGroupBuyThalis");
+    });
+    return;
+  }
+
+  // 验证是否有菜品
+  if (!group.dishes || group.dishes.length === 0) {
+    ElMessage.warning("请先完成现有菜品的填写");
+    // 触发表单验证,显示验证错误
+    nextTick(() => {
+      ruleFormRef.value?.validateField("lifeGroupBuyThalis");
+    });
+    return;
+  }
+
+  // 验证当前分组中每个菜品是否填写完整
+  for (let j = 0; j < group.dishes.length; j++) {
+    const dish = group.dishes[j];
+
+    // 验证菜品是否已选择
+    if (!dish.detailId) {
+      ElMessage.warning("请先完成现有菜品的填写");
+      // 触发表单验证,显示验证错误
+      nextTick(() => {
+        ruleFormRef.value?.validateField("lifeGroupBuyThalis");
+      });
+      return;
+    }
+
+    // 验证数量是否已填写
+    if (!dish.qty || dish.qty.toString().trim() === "") {
+      ElMessage.warning("请先完成现有菜品的填写");
+      // 触发表单验证,显示验证错误
+      nextTick(() => {
+        ruleFormRef.value?.validateField("lifeGroupBuyThalis");
+      });
+      return;
+    }
+
+    // 验证数量格式
+    const quantityNum = Number(dish.qty);
+    if (isNaN(quantityNum) || quantityNum <= 0 || !Number.isInteger(quantityNum)) {
+      ElMessage.warning("请先完成现有菜品的填写");
+      // 触发表单验证,显示验证错误
+      nextTick(() => {
+        ruleFormRef.value?.validateField("lifeGroupBuyThalis");
+      });
+      return;
+    }
   }
-  storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes.push({
-    dishId: "",
+
+  // 所有验证通过,添加新菜品
+  if (!group.dishes) {
+    group.dishes = [];
+  }
+  group.dishes.push({
+    detailId: "",
     dishName: "",
     dishPrice: "",
     dishImage: "",
-    quantity: ""
+    qty: "",
+    dishesUnit: ""
   });
 };
 
@@ -1222,7 +1300,7 @@ const addDish = (groupIndex: number) => {
  * @param dishIndex 菜品索引
  */
 const removeDish = (groupIndex: number, dishIndex: number) => {
-  const dishes = storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes;
+  const dishes = lifeGroupBuyThalis.value[groupIndex].dishes;
   if (dishes.length <= 1) {
     ElMessage.warning("至少需要保留一个菜品");
     return;
@@ -1250,8 +1328,8 @@ const openDishDialog = async (groupIndex: number, dishIndex: number) => {
   }
   filterDishList();
   // 如果已有选中的菜品,设置选中状态
-  const currentDish = storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes[dishIndex];
-  selectedDishId.value = currentDish?.dishId || null;
+  const currentDish = lifeGroupBuyThalis.value[groupIndex].dishes[dishIndex];
+  selectedDishId.value = currentDish?.detailId || null;
   dishDialogVisible.value = true;
 };
 
@@ -1271,13 +1349,14 @@ const confirmDishSelection = () => {
     ElMessage.warning("请选择一个菜品");
     return;
   }
-  const selectedDish = dishOptions.value.find(dish => dish.id === selectedDishId.value);
+  const selectedDish: any = dishOptions.value.find(dish => dish.id === selectedDishId.value);
   if (selectedDish && currentDishGroupIndex.value >= 0 && currentDishIndex.value >= 0) {
-    const dish = storeInfoModel.value.lifeGroupBuyThalis[currentDishGroupIndex.value].dishes[currentDishIndex.value];
-    dish.dishId = selectedDish.id;
-    dish.dishName = selectedDish.name;
-    dish.dishPrice = selectedDish.price;
-    dish.dishImage = selectedDish.image;
+    const dish = lifeGroupBuyThalis.value[currentDishGroupIndex.value].dishes[currentDishIndex.value];
+    dish.detailId = selectedDish.id;
+    dish.dishName = selectedDish.dishName;
+    dish.dishPrice = selectedDish.dishPrice;
+    dish.dishImage = selectedDish.imgUrl;
+    dish.dishesUnit = selectedDish.dishesUnit;
     dishDialogVisible.value = false;
   }
 };
@@ -1290,7 +1369,7 @@ const filterDishList = () => {
     filteredDishList.value = [...dishOptions.value];
   } else {
     const keyword = dishSearchKeyword.value.trim().toLowerCase();
-    filteredDishList.value = dishOptions.value.filter(dish => dish.name.toLowerCase().includes(keyword));
+    filteredDishList.value = dishOptions.value.filter(dish => dish.dishName.toLowerCase().includes(keyword));
   }
 };
 
@@ -1301,7 +1380,7 @@ const ruleFormRef = ref<FormInstance>(); // 表单引用
  * 提交数据(新增/编辑)
  * 验证表单,通过后调用相应的API接口
  */
-const handleSubmit = type => {
+const handleSubmit = (type?) => {
   let params: any = { ...storeInfoModel.value };
   if (params.effectiveDateType == 0) {
     params.couponCompDate = `用户购买${params.expirationDate}天内有效`;
@@ -1316,15 +1395,73 @@ const handleSubmit = type => {
     params.disableDateValue = dates.value.map(subArray => subArray.join(",")).join(";");
   }
   params.invoiceType = params.invoiceInformation.join(",");
-  console.log(params);
+  const output = lifeGroupBuyThalis.value.flatMap(group =>
+    group.dishes.map(dish => ({
+      groupName: group.groupName,
+      detailId: dish.detailId,
+      qty: dish.qty,
+      dishPrice: dish.dishPrice
+    }))
+  );
+  const paramsObj = {
+    lifeGroupBuyMain: params,
+    lifeGroupBuyThalis: output
+  };
+  console.log(paramsObj);
   if (type) {
     return;
   }
+  // 验证套餐内容
+  const validateThalis = () => {
+    const thalis = lifeGroupBuyThalis.value;
+    if (!thalis || thalis.length === 0) {
+      ElMessage.error("请至少添加一个套餐分组");
+      return false;
+    }
+    for (let i = 0; i < thalis.length; i++) {
+      const group = thalis[i];
+      if (!group.groupName || group.groupName.trim() === "") {
+        ElMessage.error(`第${i + 1}个分组的类别不能为空`);
+        return false;
+      }
+      if (!group.dishes || group.dishes.length === 0) {
+        ElMessage.error(`第${i + 1}个分组至少需要添加一个菜品`);
+        return false;
+      }
+      for (let j = 0; j < group.dishes.length; j++) {
+        const dish = group.dishes[j];
+        if (!dish.detailId) {
+          ElMessage.error(`第${i + 1}个分组的第${j + 1}个菜品未选择`);
+          return false;
+        }
+        if (!dish.qty || dish.qty.toString().trim() === "") {
+          ElMessage.error(`第${i + 1}个分组的第${j + 1}个菜品数量不能为空`);
+          return false;
+        }
+        const quantityNum = Number(dish.qty);
+        if (isNaN(quantityNum) || quantityNum <= 0) {
+          ElMessage.error(`第${i + 1}个分组的第${j + 1}个菜品数量必须为正整数`);
+          return false;
+        }
+        if (!Number.isInteger(quantityNum)) {
+          ElMessage.error(`第${i + 1}个分组的第${j + 1}个菜品数量必须为正整数`);
+          return false;
+        }
+      }
+    }
+    return true;
+  };
+
   // 验证表单
   ruleFormRef.value!.validate(async valid => {
     if (!valid) return;
+    // 验证套餐内容
+    if (!validateThalis()) {
+      return;
+    }
     // 组装提交参数
     let params: any = { ...storeInfoModel.value };
+    params.lifeGroupBuyThalis = lifeGroupBuyThalis.value;
   });
 };
 
@@ -1590,7 +1727,8 @@ const handleImageParam = (list: any[], result: any[]) => {
 }
 .package-content-header {
   display: flex;
-  justify-content: flex-end;
+  align-items: center;
+  justify-content: space-between;
   width: 100%;
 }
 .add-group-btn {
@@ -1691,11 +1829,14 @@ const handleImageParam = (list: any[], result: any[]) => {
   width: 100%;
 }
 .dish-list-container {
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  max-height: 400px;
-  overflow-y: auto;
+  :deep(.el-scrollbar__wrap) {
+    overflow-x: hidden;
+  }
+  :deep(.el-scrollbar__view) {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+  }
 }
 .dish-empty-state {
   display: flex;