Procházet zdrojové kódy

feat(group-package): 新增菜品选择和时间验证功能

- 添加根据门店ID获取菜单的API接口
- 实现菜品选择对话框,支持搜索和选中状态显示
- 增加开始售卖时间和结束售卖时间的日期禁用逻辑- 添加指定时间段和自定义不可用日期的验证规则- 完善套餐内容分组和菜品的增删改操作
-优化菜品列表展示,增加空状态提示
-修复套餐内容字段名称不一致的问题- 添加表单字段联动验证和日期范围控制
congxuesong před 1 měsícem
rodič
revize
f0c374a769

+ 4 - 0
src/api/modules/groupPackageManagement.ts

@@ -74,6 +74,10 @@ export const getUserByPhone = params => {
 export const getDetail = params => {
   return http.get(PORT_NONE + `/store/info/getDetail`, params);
 };
+export const getMenuByStoreId = params => {
+  return http.get(PORT_NONE + `/menu/getMenuByStoreId`, params);
+};
+
 //获取商铺明细信息
 export const getStoreDetail = (params: StoreUser.ReqUserParams) => {
   return http.get<StoreUser.ResStoreUserList>(PORT_NONE + `/store/info/getStoreDetail`, params);

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

@@ -46,6 +46,7 @@
                 format="YYYY/MM/DD"
                 value-format="YYYY-MM-DD"
                 placeholder="请选择开始售卖时间"
+                :disabled-date="disabledStartDate"
               />
             </el-form-item>
             <!-- 结束售卖时间 -->
@@ -55,6 +56,7 @@
                 format="YYYY/MM/DD"
                 value-format="YYYY-MM-DD"
                 placeholder="请选择结束售卖时间"
+                :disabled-date="disabledEndDate"
               />
             </el-form-item>
             <!-- 库存数量 -->
@@ -74,13 +76,13 @@
               <el-input v-model="storeInfoModel.quotaValue" maxlength="15" placeholder="请填写自定义限购数量" clearable />
             </el-form-item>
             <!-- 套餐内容 -->
-            <el-form-item label="套餐内容" prop="packageContent" class="package-content-item">
+            <el-form-item label="套餐内容" prop="lifeGroupBuyThalis" class="package-content-item">
               <div class="package-content-wrapper">
                 <div class="package-content-header">
                   <el-button type="primary" @click="addGroup" class="add-group-btn"> 添加分组 </el-button>
                 </div>
                 <div class="package-content-container">
-                  <div v-for="(group, groupIndex) in storeInfoModel.packageContent" :key="groupIndex" class="package-group">
+                  <div v-for="(group, groupIndex) in storeInfoModel.lifeGroupBuyThalis" :key="groupIndex" class="package-group">
                     <div class="package-group-header">
                       <div class="header-right">
                         <el-button
@@ -88,7 +90,7 @@
                           link
                           @click="removeGroup(groupIndex)"
                           :icon="Delete"
-                          v-show="storeInfoModel.packageContent.length > 1"
+                          v-show="storeInfoModel.lifeGroupBuyThalis.length > 1"
                         >
                           删除
                         </el-button>
@@ -112,7 +114,7 @@
                           <span class="label">菜品</span>
                           <div class="dish-select-block" @click="openDishDialog(groupIndex, dishIndex)">
                             <span v-if="dish.dishName" class="dish-selected-name"
-                              >{{ dish.dishName }},¥{{ dish.dishPrice }}/</span
+                              >{{ dish.dishName }},¥{{ dish.dishPrice }}/{{ dish.dishesUnit }}</span
                             >
                             <span v-else class="dish-placeholder">请选择</span>
                           </div>
@@ -174,6 +176,7 @@
                 range-separator="-"
                 start-placeholder="开始时间"
                 end-placeholder="结束时间"
+                :disabled-date="disabledEffectiveDate"
               />
             </el-form-item>
             <el-form-item label="不可用日期" prop="disableDateType">
@@ -235,6 +238,7 @@
                     start-placeholder="开始时间"
                     end-placeholder="结束时间"
                     class="date-picker"
+                    :disabled-date="disabledCustomUnavailableDate"
                   />
                   <el-button
                     :icon="Delete"
@@ -319,20 +323,30 @@
           @input="filterDishList"
         />
         <div class="dish-list-container">
-          <div v-for="dish in filteredDishList" :key="dish.id" class="dish-item" @click="selectDish(dish)">
-            <div class="dish-image">
-              <el-image v-if="dish.image" :src="dish.image" fit="cover" class="dish-img" />
-              <div v-else class="dish-image-placeholder">
-                <el-icon><Picture /></el-icon>
+          <template v-if="filteredDishList.length > 0">
+            <div
+              v-for="dish in filteredDishList"
+              :key="dish.id"
+              :class="['dish-item', { 'dish-item-selected': selectedDishId === dish.id }]"
+              @click="selectDish(dish)"
+            >
+              <div class="dish-image">
+                <el-image v-if="dish.imgUrl" :src="dish.imgUrl" fit="cover" class="dish-img" />
+                <div v-else class="dish-image-placeholder">
+                  <el-icon><Picture /></el-icon>
+                </div>
               </div>
-            </div>
-            <div class="dish-info">
-              <div class="dish-name">
-                {{ dish.name }}
+              <div class="dish-info">
+                <div class="dish-name">
+                  {{ dish.name }}
+                </div>
+                <div class="dish-price">¥{{ dish.dishPrice }}/{{ dish.dishesUnit }}</div>
               </div>
-              <div class="dish-price">¥{{ dish.price }}/份</div>
             </div>
-            <el-radio v-model="selectedDishId" :label="dish.id" @click.stop="selectDish(dish)" />
+          </template>
+          <div v-else class="dish-empty-state">
+            <div class="empty-text">暂无可选择菜品</div>
+            <div class="empty-hint">请去全部-门店装修-菜品管理中添加</div>
           </div>
         </div>
       </div>
@@ -352,14 +366,15 @@
  */
 import { ref, reactive, onMounted, watch, nextTick } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { Plus, Delete, ArrowDown, ArrowUp, Picture } from "@element-plus/icons-vue";
+import { Plus, Delete, ArrowDown, ArrowUp, Picture, Check } from "@element-plus/icons-vue";
 import {
   saveStoreInfo,
   editStoreInfo,
   getDetail,
   getStoreDetail,
   getHolidayList,
-  getUserByPhone
+  getUserByPhone,
+  getMenuByStoreId
 } from "@/api/modules/groupPackageManagement";
 import { useRouter, useRoute } from "vue-router";
 import type { UploadProps, FormInstance } from "element-plus";
@@ -388,8 +403,64 @@ const rules = reactive({
   imageList: [{ required: true, message: "请上传图片" }],
   groupName: [{ required: true, message: "请填写团购名称" }],
   startTimeType: [{ required: true, message: "请选择开始售卖时间" }],
-  saleTimeStr: [{ required: true, message: "请选择开始售卖时间" }],
-  endTime: [{ required: true, message: "请选择结束售卖时间" }],
+  saleTimeStr: [
+    { required: true, message: "请选择开始售卖时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (storeInfoModel.value.startTimeType === 1) {
+          if (!value) {
+            callback(new Error("请选择开始售卖时间"));
+            return;
+          }
+          const selectedDate = new Date(value);
+          const today = new Date();
+          today.setHours(0, 0, 0, 0);
+          if (selectedDate < today) {
+            callback(new Error("开始售卖时间不能早于当前时间"));
+            return;
+          }
+          // 如果结束时间已设置,验证开始时间必须早于结束时间
+          if (storeInfoModel.value.endTime) {
+            const endDate = new Date(storeInfoModel.value.endTime);
+            if (selectedDate >= endDate) {
+              callback(new Error("开始售卖时间必须早于结束售卖时间"));
+              return;
+            }
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  endTime: [
+    { required: true, message: "请选择结束售卖时间" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (!value) {
+          callback(new Error("请选择结束售卖时间"));
+          return;
+        }
+        const selectedDate = new Date(value);
+        const today = new Date();
+        today.setHours(0, 0, 0, 0);
+        if (selectedDate < today) {
+          callback(new Error("结束售卖时间不能早于当前时间"));
+          return;
+        }
+        // 如果开始时间已设置,验证结束时间必须晚于开始时间
+        if (storeInfoModel.value.startTimeType === 1 && storeInfoModel.value.saleTimeStr) {
+          const startDate = new Date(storeInfoModel.value.saleTimeStr);
+          if (selectedDate <= startDate) {
+            callback(new Error("结束售卖时间必须晚于开始售卖时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
   inventoryNum: [
     { required: true, message: "请填写库存数量" },
     {
@@ -437,7 +508,7 @@ const rules = reactive({
       trigger: "blur"
     }
   ],
-  // packageContent: [
+  // lifeGroupBuyThalis: [
   //   {
   //     required: true,
   //     validator: (rule: any, value: any, callback: any) => {
@@ -541,15 +612,33 @@ const rules = reactive({
     {
       required: true,
       validator: (rule: any, value: any, callback: any) => {
-        if (storeInfoModel.value.expirationDate === 1) {
+        if (storeInfoModel.value.effectiveDateType === 1) {
           if (!value || value.length !== 2) {
             callback(new Error("请选择指定时间段"));
             return;
           }
+          // 验证开始时间和结束时间不能早于当前时间
+          const today = new Date();
+          today.setHours(0, 0, 0, 0);
+          const startDate = new Date(value[0]);
+          const endDate = new Date(value[1]);
+          if (startDate < today) {
+            callback(new Error("开始时间不能早于当前时间"));
+            return;
+          }
+          if (endDate < today) {
+            callback(new Error("结束时间不能早于当前时间"));
+            return;
+          }
+          // 验证开始时间必须早于结束时间
+          if (startDate >= endDate) {
+            callback(new Error("开始时间必须早于结束时间"));
+            return;
+          }
         }
         callback();
       },
-      trigger: "blur"
+      trigger: "change"
     }
   ],
   disableDateType: [{ required: true, message: "请选择不可用日期" }],
@@ -587,22 +676,40 @@ const rules = reactive({
     {
       required: true,
       validator: (rule: any, value: any, callback: any) => {
-        if (storeInfoModel.value.unavailableDates === 2) {
+        if (storeInfoModel.value.disableDateType === 2) {
           if (!dates.value || dates.value.length === 0) {
             callback(new Error("至少需要添加一个自定义不可用日期"));
             return;
           }
+          const today = new Date();
+          today.setHours(0, 0, 0, 0);
           // 验证每个日期项是否已填写
           for (let i = 0; i < dates.value.length; i++) {
             if (!dates.value[i] || dates.value[i].length !== 2) {
               callback(new Error(`第${i + 1}个日期项未完整填写`));
               return;
             }
+            // 验证开始时间和结束时间不能早于当前时间
+            const startDate = new Date(dates.value[i][0]);
+            const endDate = new Date(dates.value[i][1]);
+            if (startDate < today) {
+              callback(new Error(`第${i + 1}个日期项的开始时间不能早于当前时间`));
+              return;
+            }
+            if (endDate < today) {
+              callback(new Error(`第${i + 1}个日期项的结束时间不能早于当前时间`));
+              return;
+            }
+            // 验证开始时间必须早于结束时间
+            if (startDate >= endDate) {
+              callback(new Error(`第${i + 1}个日期项的开始时间必须早于结束时间`));
+              return;
+            }
           }
         }
         callback();
       },
-      trigger: "blur"
+      trigger: "change"
     }
   ],
   reservationRules: [{ required: true, message: "请输入预约规则" }],
@@ -653,7 +760,7 @@ const storeInfoModel = ref<any>({
   // 自定义限购数量(当quotaType为1时必填)
   quotaValue: 0,
   // 套餐内容(数组,每个元素是一个分组)
-  packageContent: [
+  lifeGroupBuyThalis: [
     {
       category: "", // 类别
       collapsed: false, // 是否收起
@@ -700,29 +807,29 @@ const storeInfoModel = ref<any>({
 // ==================== 下拉选项数据 ====================
 
 // 发票类型列表
-const businessTypes = ref<any[]>([
+const businessTypes = ref([
   { value: 0, label: "提供电子发票" },
   { value: 1, label: "提供纸质发票" }
 ]);
 
 // 开始售卖时间设置方式列表
-const storeStatusList = ref<any[]>([
+const storeStatusList = ref([
   { value: 0, label: "审核通过后立即开始" },
   { value: 1, label: "设置售卖时间" }
 ]);
 
 // 每人限购设置列表
-const perList = ref<any[]>([
+const perList = ref([
   { value: 0, label: "不限量" },
   { value: 1, label: "自定义限购数量" }
 ]);
 // 每人限购设置列表
-const expirationDateList = ref<any[]>([
+const expirationDateList = ref([
   { value: 0, label: "指定天数" },
   { value: 1, label: "指定时间段内可用" }
 ]);
 // 每人限购设置列表
-const unavailableDatesList = ref<any[]>([
+const unavailableDatesList = ref([
   { value: 0, label: "全部日期可用" },
   { value: 1, label: "限制日期" },
   { value: 2, label: "自定义不可用日期" }
@@ -730,10 +837,10 @@ const unavailableDatesList = ref<any[]>([
 //图片集合
 const videoUrlList = ref<string[]>([]);
 // 自定义不可用日期列表
-const dates = ref<any[]>([]);
+const dates = ref([]);
 
 // 星期选项列表
-const weekdayList = ref<any[]>([
+const weekdayList = ref([
   { name: "周一", id: "0", oName: "星期一" },
   { name: "周二", id: "1", oName: "星期二" },
   { name: "周三", id: "2", oName: "星期三" },
@@ -744,7 +851,7 @@ const weekdayList = ref<any[]>([
 ]);
 
 // 节日选项列表
-const holidayList = ref<any[]>([
+const holidayList = ref([
   { value: 1, label: "元旦" },
   { value: 2, label: "春节" },
   { value: 3, label: "情人节" },
@@ -762,17 +869,12 @@ const holidayList = ref<any[]>([
 ]);
 
 // 菜品选项列表(这里需要根据实际API获取,暂时使用示例数据)
-const dishOptions = ref<any[]>([
-  { id: 1, name: "辣炒牛肉", price: 58, image: "" },
-  { id: 2, name: "辣炒牛肉", price: 58, image: "" },
-  { id: 3, name: "宫保鸡丁", price: 45, image: "" },
-  { id: 4, name: "麻婆豆腐", price: 32, image: "" }
-]);
+const dishOptions = ref([]);
 
 // 菜品选择对话框相关
 const dishDialogVisible = ref(false);
 const dishSearchKeyword = ref("");
-const filteredDishList = ref<any[]>([]);
+const filteredDishList = ref([]);
 const selectedDishId = ref<number | null>(null);
 const currentDishGroupIndex = ref<number>(-1);
 const currentDishIndex = ref<number>(-1);
@@ -796,6 +898,36 @@ watch(
   { immediate: true }
 );
 
+/**
+ * 监听开始售卖时间变化
+ * 当开始时间改变时,重新验证结束时间
+ */
+watch(
+  () => storeInfoModel.value.saleTimeStr,
+  () => {
+    if (storeInfoModel.value.endTime) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("endTime");
+      });
+    }
+  }
+);
+
+/**
+ * 监听结束售卖时间变化
+ * 当结束时间改变时,重新验证开始时间
+ */
+watch(
+  () => storeInfoModel.value.endTime,
+  () => {
+    if (storeInfoModel.value.startTimeType === 1 && storeInfoModel.value.saleTimeStr) {
+      nextTick(() => {
+        ruleFormRef.value?.validateField("saleTimeStr");
+      });
+    }
+  }
+);
+
 // ==================== 生命周期钩子 ====================
 
 /**
@@ -833,7 +965,7 @@ onMounted(async () => {
   };
   let res = await getHolidayList(params);
   if (res && res.code == 200) {
-    holidayList.value = res.data.records;
+    holidayList.value = res.data;
   }
   if (type.value != "add") {
     let res: any = await getStoreDetail({ id: id.value } as any);
@@ -850,8 +982,8 @@ onMounted(async () => {
       storeInfoModel.value.unavailableHolidays = [];
     }
     // 确保套餐内容字段存在
-    if (!storeInfoModel.value.packageContent || storeInfoModel.value.packageContent.length === 0) {
-      storeInfoModel.value.packageContent = [
+    if (!storeInfoModel.value.lifeGroupBuyThalis || storeInfoModel.value.lifeGroupBuyThalis.length === 0) {
+      storeInfoModel.value.lifeGroupBuyThalis = [
         {
           category: "",
           collapsed: false,
@@ -868,7 +1000,7 @@ onMounted(async () => {
       ];
     } else {
       // 确保每个分组都有必要的字段
-      storeInfoModel.value.packageContent.forEach((group: any) => {
+      storeInfoModel.value.lifeGroupBuyThalis.forEach((group: any) => {
         if (group.collapsed === undefined) {
           group.collapsed = false;
         }
@@ -1020,10 +1152,10 @@ const toggleHoliday = value => {
  * 添加套餐分组
  */
 const addGroup = () => {
-  if (!storeInfoModel.value.packageContent) {
-    storeInfoModel.value.packageContent = [];
+  if (!storeInfoModel.value.lifeGroupBuyThalis) {
+    storeInfoModel.value.lifeGroupBuyThalis = [];
   }
-  storeInfoModel.value.packageContent.push({
+  storeInfoModel.value.lifeGroupBuyThalis.push({
     category: "",
     collapsed: false,
     dishes: [
@@ -1043,7 +1175,7 @@ const addGroup = () => {
  * @param groupIndex 分组索引
  */
 const removeGroup = (groupIndex: number) => {
-  if (storeInfoModel.value.packageContent.length <= 1) {
+  if (storeInfoModel.value.lifeGroupBuyThalis.length <= 1) {
     ElMessage.warning("至少需要保留一个分组");
     return;
   }
@@ -1053,7 +1185,7 @@ const removeGroup = (groupIndex: number) => {
     type: "warning"
   })
     .then(() => {
-      storeInfoModel.value.packageContent.splice(groupIndex, 1);
+      storeInfoModel.value.lifeGroupBuyThalis.splice(groupIndex, 1);
       ElMessage.success("删除成功");
     })
     .catch(() => {});
@@ -1064,7 +1196,7 @@ const removeGroup = (groupIndex: number) => {
  * @param groupIndex 分组索引
  */
 const toggleGroupCollapse = (groupIndex: number) => {
-  storeInfoModel.value.packageContent[groupIndex].collapsed = !storeInfoModel.value.packageContent[groupIndex].collapsed;
+  storeInfoModel.value.lifeGroupBuyThalis[groupIndex].collapsed = !storeInfoModel.value.lifeGroupBuyThalis[groupIndex].collapsed;
 };
 
 /**
@@ -1072,10 +1204,10 @@ const toggleGroupCollapse = (groupIndex: number) => {
  * @param groupIndex 分组索引
  */
 const addDish = (groupIndex: number) => {
-  if (!storeInfoModel.value.packageContent[groupIndex].dishes) {
-    storeInfoModel.value.packageContent[groupIndex].dishes = [];
+  if (!storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes) {
+    storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes = [];
   }
-  storeInfoModel.value.packageContent[groupIndex].dishes.push({
+  storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes.push({
     dishId: "",
     dishName: "",
     dishPrice: "",
@@ -1090,7 +1222,7 @@ const addDish = (groupIndex: number) => {
  * @param dishIndex 菜品索引
  */
 const removeDish = (groupIndex: number, dishIndex: number) => {
-  const dishes = storeInfoModel.value.packageContent[groupIndex].dishes;
+  const dishes = storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes;
   if (dishes.length <= 1) {
     ElMessage.warning("至少需要保留一个菜品");
     return;
@@ -1103,13 +1235,22 @@ const removeDish = (groupIndex: number, dishIndex: number) => {
  * @param groupIndex 分组索引
  * @param dishIndex 菜品索引
  */
-const openDishDialog = (groupIndex: number, dishIndex: number) => {
+const openDishDialog = async (groupIndex: number, dishIndex: number) => {
   currentDishGroupIndex.value = groupIndex;
   currentDishIndex.value = dishIndex;
   dishSearchKeyword.value = "";
+  const params = {
+    storeId: 361,
+    phoneId: 18641153170,
+    dishType: 0
+  };
+  const res = await getMenuByStoreId(params);
+  if (res && res.code == 200) {
+    dishOptions.value = res.data;
+  }
   filterDishList();
   // 如果已有选中的菜品,设置选中状态
-  const currentDish = storeInfoModel.value.packageContent[groupIndex].dishes[dishIndex];
+  const currentDish = storeInfoModel.value.lifeGroupBuyThalis[groupIndex].dishes[dishIndex];
   selectedDishId.value = currentDish?.dishId || null;
   dishDialogVisible.value = true;
 };
@@ -1132,7 +1273,7 @@ const confirmDishSelection = () => {
   }
   const selectedDish = dishOptions.value.find(dish => dish.id === selectedDishId.value);
   if (selectedDish && currentDishGroupIndex.value >= 0 && currentDishIndex.value >= 0) {
-    const dish = storeInfoModel.value.packageContent[currentDishGroupIndex.value].dishes[currentDishIndex.value];
+    const dish = storeInfoModel.value.lifeGroupBuyThalis[currentDishGroupIndex.value].dishes[currentDishIndex.value];
     dish.dishId = selectedDish.id;
     dish.dishName = selectedDish.name;
     dish.dishPrice = selectedDish.price;
@@ -1190,6 +1331,64 @@ const handleSubmit = type => {
 // ==================== 工具函数 ====================
 
 /**
+ * 禁用开始售卖时间的日期
+ * 不能选择早于当前时间的日期
+ * @param time 日期对象
+ * @returns 是否禁用该日期
+ */
+const disabledStartDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return time.getTime() < today.getTime();
+};
+
+/**
+ * 禁用结束售卖时间的日期
+ * 不能选择早于当前时间的日期,也不能选择早于或等于开始售卖时间的日期
+ * @param time 日期对象
+ * @returns 是否禁用该日期
+ */
+const disabledEndDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  // 不能早于当前时间
+  if (time.getTime() < today.getTime()) {
+    return true;
+  }
+  // 如果开始售卖时间已设置,不能早于或等于开始时间
+  if (storeInfoModel.value.startTimeType === 1 && storeInfoModel.value.saleTimeStr) {
+    const startDate = new Date(storeInfoModel.value.saleTimeStr);
+    startDate.setHours(0, 0, 0, 0);
+    return time.getTime() <= startDate.getTime();
+  }
+  return false;
+};
+
+/**
+ * 禁用指定时间段内可用的日期
+ * 不能选择早于当前时间的日期
+ * @param time 日期对象
+ * @returns 是否禁用该日期
+ */
+const disabledEffectiveDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return time.getTime() < today.getTime();
+};
+
+/**
+ * 禁用自定义不可用日期的日期
+ * 不能选择早于当前时间的日期
+ * @param time 日期对象
+ * @returns 是否禁用该日期
+ */
+const disabledCustomUnavailableDate = (time: Date) => {
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return time.getTime() < today.getTime();
+};
+
+/**
  * 处理图片结果
  * 将上传组件的文件对象或URL字符串转换为URL数组
  * @param list 图片列表(可能是文件对象或URL字符串)
@@ -1498,7 +1697,25 @@ const handleImageParam = (list: any[], result: any[]) => {
   max-height: 400px;
   overflow-y: auto;
 }
+.dish-empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 60px 20px;
+  text-align: center;
+}
+.empty-text {
+  margin-bottom: 8px;
+  font-size: 16px;
+  color: #909399;
+}
+.empty-hint {
+  font-size: 14px;
+  color: #c0c4cc;
+}
 .dish-item {
+  position: relative;
   display: flex;
   gap: 12px;
   align-items: center;
@@ -1512,6 +1729,14 @@ const handleImageParam = (list: any[], result: any[]) => {
   background-color: #f5f7fa;
   border-color: #409eff;
 }
+.dish-item-selected {
+  background-color: #ecf5ff;
+  border-color: #409eff;
+  border-width: 2px;
+}
+.dish-item-selected:hover {
+  background-color: #d9ecff;
+}
 .dish-image {
   flex-shrink: 0;
   width: 60px;
@@ -1548,6 +1773,18 @@ const handleImageParam = (list: any[], result: any[]) => {
   font-size: 14px;
   color: #909399;
 }
+.dish-selected-icon {
+  display: flex;
+  flex-shrink: 0;
+  align-items: center;
+  justify-content: center;
+  width: 24px;
+  height: 24px;
+  font-size: 16px;
+  color: #ffffff;
+  background-color: #409eff;
+  border-radius: 50%;
+}
 .dialog-footer {
   display: flex;
   justify-content: center;