|
|
@@ -76,105 +76,143 @@
|
|
|
<el-input v-model="storeInfoModel.quotaValue" maxlength="15" placeholder="请填写自定义限购数量" clearable />
|
|
|
</el-form-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>
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- link
|
|
|
- @click="toggleAllGroupsCollapse"
|
|
|
- :icon="allGroupsCollapsed ? ArrowDown : ArrowUp"
|
|
|
- v-show="lifeGroupBuyThalis.length > 1"
|
|
|
- >
|
|
|
- {{ allGroupsCollapsed ? "展开全部" : "收起全部" }}
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- <transition-group name="group-fade" tag="div" class="package-content-container">
|
|
|
- <div v-for="item in visibleGroups" :key="item.originalIndex" class="package-group">
|
|
|
- <div class="package-group-header">
|
|
|
- <span class="group-index">{{ item.group.groupName }}</span>
|
|
|
- <div class="header-right">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- link
|
|
|
- @click="toggleGroupCollapse(item.originalIndex)"
|
|
|
- :icon="isGroupCollapsed(item.originalIndex) ? ArrowDown : ArrowUp"
|
|
|
- v-show="item.group.dishes.length > 1"
|
|
|
- >
|
|
|
- {{ isGroupCollapsed(item.originalIndex) ? "展开" : "收起" }}
|
|
|
- </el-button>
|
|
|
- <el-button
|
|
|
- type="danger"
|
|
|
- link
|
|
|
- @click="removeGroup(item.originalIndex)"
|
|
|
- :icon="Delete"
|
|
|
- v-show="lifeGroupBuyThalis.length > 1"
|
|
|
- >
|
|
|
- 删除
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="package-group-content">
|
|
|
- <div class="category-row">
|
|
|
- <span class="label">类别</span>
|
|
|
- <el-input v-model="item.group.groupName" placeholder="请输入" clearable />
|
|
|
- </div>
|
|
|
- <!-- 第一行菜品,始终显示 -->
|
|
|
- <div v-if="item.group.dishes && item.group.dishes.length > 0" class="dish-row">
|
|
|
- <span class="label">菜品</span>
|
|
|
- <div class="dish-select-block" @click="openDishDialog(item.originalIndex, 0)">
|
|
|
- <span v-if="item.group.dishes[0].dishName" class="dish-selected-name"
|
|
|
- >{{ item.group.dishes[0].dishName }},¥{{ item.group.dishes[0].dishPrice }}/{{
|
|
|
- item.group.dishes[0].dishesUnit
|
|
|
- }}</span
|
|
|
+ <el-form-item label="套餐内容" class="package-content-item">
|
|
|
+ <el-form
|
|
|
+ ref="packageFormRef"
|
|
|
+ :model="packageFormModel"
|
|
|
+ :rules="packageFormRules"
|
|
|
+ label-width="0"
|
|
|
+ class="package-content-form"
|
|
|
+ >
|
|
|
+ <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>
|
|
|
+ <transition-group name="group-fade" tag="div" class="package-content-container">
|
|
|
+ <div v-for="item in visibleGroups" :key="item.originalIndex" class="package-group">
|
|
|
+ <div class="package-group-header">
|
|
|
+ <span class="group-index">{{ item.group.groupName }}</span>
|
|
|
+ <div class="header-right">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ link
|
|
|
+ @click="toggleGroupCollapse(item.originalIndex)"
|
|
|
+ :icon="isGroupCollapsed(item.originalIndex) ? ArrowDown : ArrowUp"
|
|
|
+ v-show="item.group.dishes.length > 1"
|
|
|
>
|
|
|
- <span v-else class="dish-placeholder">请选择</span>
|
|
|
+ {{ isGroupCollapsed(item.originalIndex) ? "展开" : "收起" }}
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ link
|
|
|
+ @click="removeGroup(item.originalIndex)"
|
|
|
+ :icon="Delete"
|
|
|
+ v-show="lifeGroupBuyThalis.length > 1"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
- <span class="label">数量</span>
|
|
|
- <el-input v-model="item.group.dishes[0].qty" placeholder="请输入" clearable class="quantity-input" />
|
|
|
- <el-button
|
|
|
- :icon="Delete"
|
|
|
- link
|
|
|
- type="danger"
|
|
|
- @click="removeDish(item.originalIndex, 0)"
|
|
|
- class="delete-dish-btn"
|
|
|
- v-show="item.group.dishes.length > 1"
|
|
|
- />
|
|
|
</div>
|
|
|
- <!-- 第二行及以后的菜品,收起时隐藏 -->
|
|
|
- <transition name="slide-fade">
|
|
|
- <div v-if="!isGroupCollapsed(item.originalIndex)" class="extra-dishes-container">
|
|
|
- <div
|
|
|
- v-for="(dish, dishIndex) in item.group.dishes"
|
|
|
- :key="dishIndex"
|
|
|
- class="dish-row"
|
|
|
- v-show="dishIndex > 0"
|
|
|
+ <div class="package-group-content">
|
|
|
+ <el-form-item
|
|
|
+ :prop="`groups.${item.originalIndex}.groupName`"
|
|
|
+ :rules="packageFormRules[`groups.${item.originalIndex}.groupName`]"
|
|
|
+ class="category-form-item"
|
|
|
+ >
|
|
|
+ <div class="category-row">
|
|
|
+ <span class="label">类别</span>
|
|
|
+ <el-input v-model="item.group.groupName" placeholder="请输入" clearable />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 第一行菜品,始终显示 -->
|
|
|
+ <div v-if="item.group.dishes && item.group.dishes.length > 0" class="dish-row">
|
|
|
+ <el-form-item
|
|
|
+ :prop="`groups.${item.originalIndex}.dishes.0.detailId`"
|
|
|
+ :rules="packageFormRules[`groups.${item.originalIndex}.dishes.0.detailId`]"
|
|
|
+ class="dish-form-item"
|
|
|
>
|
|
|
<span class="label">菜品</span>
|
|
|
- <div class="dish-select-block" @click="openDishDialog(item.originalIndex, dishIndex)">
|
|
|
- <span v-if="dish.dishName" class="dish-selected-name"
|
|
|
- >{{ dish.dishName }},¥{{ dish.dishPrice }}/{{ dish.dishesUnit }}</span
|
|
|
+ <div class="dish-select-block" @click="openDishDialog(item.originalIndex, 0)">
|
|
|
+ <span v-if="item.group.dishes[0].dishName" class="dish-selected-name"
|
|
|
+ >{{ item.group.dishes[0].dishName }},¥{{ item.group.dishes[0].dishPrice }}/{{
|
|
|
+ item.group.dishes[0].dishesUnit
|
|
|
+ }}</span
|
|
|
>
|
|
|
<span v-else class="dish-placeholder">请选择</span>
|
|
|
</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ :prop="`groups.${item.originalIndex}.dishes.0.qty`"
|
|
|
+ :rules="packageFormRules[`groups.${item.originalIndex}.dishes.0.qty`]"
|
|
|
+ class="dish-form-item"
|
|
|
+ >
|
|
|
<span class="label">数量</span>
|
|
|
- <el-input v-model="dish.qty" placeholder="请输入" clearable class="quantity-input" />
|
|
|
+ <el-input v-model="item.group.dishes[0].qty" placeholder="请输入" clearable class="quantity-input" />
|
|
|
<el-button
|
|
|
:icon="Delete"
|
|
|
link
|
|
|
type="danger"
|
|
|
- @click="removeDish(item.originalIndex, dishIndex)"
|
|
|
+ @click="removeDish(item.originalIndex, 0)"
|
|
|
class="delete-dish-btn"
|
|
|
+ v-show="item.group.dishes.length > 1"
|
|
|
/>
|
|
|
- </div>
|
|
|
- <el-button type="primary" @click="addDish(item.originalIndex)" class="add-dish-btn"> 添加 </el-button>
|
|
|
+ </el-form-item>
|
|
|
</div>
|
|
|
- </transition>
|
|
|
+ <!-- 第二行及以后的菜品,收起时隐藏 -->
|
|
|
+ <transition name="slide-fade">
|
|
|
+ <div v-if="!isGroupCollapsed(item.originalIndex)" class="extra-dishes-container">
|
|
|
+ <div
|
|
|
+ v-for="(dish, dishIndex) in item.group.dishes"
|
|
|
+ :key="dishIndex"
|
|
|
+ class="dish-row"
|
|
|
+ v-show="dishIndex > 0"
|
|
|
+ >
|
|
|
+ <el-form-item
|
|
|
+ :prop="`groups.${item.originalIndex}.dishes.${dishIndex}.detailId`"
|
|
|
+ :rules="packageFormRules[`groups.${item.originalIndex}.dishes.${dishIndex}.detailId`]"
|
|
|
+ class="dish-form-item"
|
|
|
+ >
|
|
|
+ <span class="label">菜品</span>
|
|
|
+ <div class="dish-select-block" @click="openDishDialog(item.originalIndex, dishIndex)">
|
|
|
+ <span v-if="dish.dishName" class="dish-selected-name"
|
|
|
+ >{{ dish.dishName }},¥{{ dish.dishPrice }}/{{ dish.dishesUnit }}</span
|
|
|
+ >
|
|
|
+ <span v-else class="dish-placeholder">请选择</span>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item
|
|
|
+ :prop="`groups.${item.originalIndex}.dishes.${dishIndex}.qty`"
|
|
|
+ :rules="packageFormRules[`groups.${item.originalIndex}.dishes.${dishIndex}.qty`]"
|
|
|
+ class="dish-form-item"
|
|
|
+ >
|
|
|
+ <span class="label">数量</span>
|
|
|
+ <el-input v-model="dish.qty" placeholder="请输入" clearable class="quantity-input" />
|
|
|
+ <el-button
|
|
|
+ :icon="Delete"
|
|
|
+ link
|
|
|
+ type="danger"
|
|
|
+ @click="removeDish(item.originalIndex, dishIndex)"
|
|
|
+ class="delete-dish-btn"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <el-button type="primary" @click="addDish(item.originalIndex)" class="add-dish-btn"> 添加 </el-button>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </transition-group>
|
|
|
- </div>
|
|
|
+ </transition-group>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
</el-form-item>
|
|
|
</div>
|
|
|
<!-- 价格信息模块 -->
|
|
|
@@ -554,47 +592,12 @@ const rules = reactive({
|
|
|
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) {
|
|
|
// 如果验证过程中出错,直接通过验证,避免报错
|
|
|
@@ -1221,6 +1224,77 @@ const addGroup = () => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 验证所有现有分组是否填写完整
|
|
|
+ for (let i = 0; i < lifeGroupBuyThalis.value.length; i++) {
|
|
|
+ const group = lifeGroupBuyThalis.value[i];
|
|
|
+
|
|
|
+ // 验证类别
|
|
|
+ if (!group.groupName || group.groupName.trim() === "") {
|
|
|
+ ElMessage.warning("请先完成现有分组的填写");
|
|
|
+ // 触发表单验证,显示验证错误
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.validate(() => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证是否有菜品
|
|
|
+ if (!group.dishes || group.dishes.length === 0) {
|
|
|
+ ElMessage.warning("请先完成现有分组的填写");
|
|
|
+ // 触发表单验证,显示验证错误
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.validate(() => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证当前分组中每个菜品是否填写完整
|
|
|
+ for (let j = 0; j < group.dishes.length; j++) {
|
|
|
+ const dish = group.dishes[j];
|
|
|
+
|
|
|
+ // 验证菜品是否已选择
|
|
|
+ if (!dish.detailId) {
|
|
|
+ ElMessage.warning("请先完成现有分组的填写");
|
|
|
+ // 触发表单验证,显示验证错误
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.validate(() => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证数量是否已填写
|
|
|
+ if (!dish.qty || dish.qty.toString().trim() === "") {
|
|
|
+ ElMessage.warning("请先完成现有分组的填写");
|
|
|
+ // 触发表单验证,显示验证错误
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.validate(() => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证数量格式
|
|
|
+ const quantityNum = Number(dish.qty);
|
|
|
+ if (isNaN(quantityNum) || quantityNum <= 0 || !Number.isInteger(quantityNum)) {
|
|
|
+ ElMessage.warning("请先完成现有分组的填写");
|
|
|
+ // 触发表单验证,显示验证错误
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.validate(() => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 记录旧分组的数量,用于后续只验证旧分组
|
|
|
const oldGroupsCount = lifeGroupBuyThalis.value.length;
|
|
|
|
|
|
@@ -1246,7 +1320,9 @@ const addGroup = () => {
|
|
|
// 只清除验证状态,不重新触发验证
|
|
|
// 让用户操作时(如点击添加菜品)再触发验证
|
|
|
nextTick(() => {
|
|
|
- ruleFormRef.value?.clearValidate("lifeGroupBuyThalis");
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ packageFormRef.value.clearValidate();
|
|
|
+ }
|
|
|
skipLastGroupValidation = false;
|
|
|
});
|
|
|
};
|
|
|
@@ -1481,6 +1557,13 @@ const confirmDishSelection = () => {
|
|
|
dish.dishImage = selectedDish.imgUrl;
|
|
|
dish.dishesUnit = selectedDish.dishesUnit;
|
|
|
dishDialogVisible.value = false;
|
|
|
+ // 重新验证对应的字段,如果验证通过,错误提示会消失
|
|
|
+ nextTick(() => {
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ const prop = `groups.${currentDishGroupIndex.value}.dishes.${currentDishIndex.value}.detailId`;
|
|
|
+ packageFormRef.value.validateField(prop, () => {});
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -1498,6 +1581,72 @@ const filterDishList = () => {
|
|
|
|
|
|
// ==================== 表单引用 ====================
|
|
|
const ruleFormRef = ref<FormInstance>(); // 表单引用
|
|
|
+const packageFormRef = ref<FormInstance>(); // 套餐内容表单引用
|
|
|
+
|
|
|
+// 套餐内容表单模型(用于验证)
|
|
|
+const packageFormModel = computed(() => {
|
|
|
+ return {
|
|
|
+ groups: lifeGroupBuyThalis.value
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+// 套餐内容验证规则
|
|
|
+const packageFormRules = computed(() => {
|
|
|
+ const rules: any = {};
|
|
|
+ lifeGroupBuyThalis.value.forEach((group, groupIndex) => {
|
|
|
+ // 类别验证规则 - 使用与 prop 相同的路径格式
|
|
|
+ rules[`groups.${groupIndex}.groupName`] = [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: `第${groupIndex + 1}个分组的类别不能为空`,
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 每个菜品的验证规则
|
|
|
+ if (group.dishes) {
|
|
|
+ group.dishes.forEach((dish, dishIndex) => {
|
|
|
+ // 菜品选择验证
|
|
|
+ rules[`groups.${groupIndex}.dishes.${dishIndex}.detailId`] = [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: `第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品未选择`,
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 菜品数量验证
|
|
|
+ rules[`groups.${groupIndex}.dishes.${dishIndex}.qty`] = [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: `第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量不能为空`,
|
|
|
+ trigger: "blur"
|
|
|
+ },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value || value.toString().trim() === "") {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0) {
|
|
|
+ callback(new Error(`第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量必须为正整数`));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!Number.isInteger(num)) {
|
|
|
+ callback(new Error(`第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量必须为正整数`));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return rules;
|
|
|
+});
|
|
|
|
|
|
/**
|
|
|
* 提交数据(新增/编辑)
|
|
|
@@ -1534,53 +1683,24 @@ const handleSubmit = (type?) => {
|
|
|
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;
|
|
|
+ // 验证套餐内容表单
|
|
|
+ if (packageFormRef.value) {
|
|
|
+ let packageValid = false;
|
|
|
+ await new Promise<void>(resolve => {
|
|
|
+ packageFormRef.value!.validate(valid => {
|
|
|
+ packageValid = valid;
|
|
|
+ if (!valid) {
|
|
|
+ ElMessage.error("请完善套餐内容信息");
|
|
|
+ }
|
|
|
+ resolve();
|
|
|
+ });
|
|
|
+ });
|
|
|
+ if (!packageValid) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
// 组装提交参数
|
|
|
let params: any = { ...storeInfoModel.value };
|
|
|
@@ -1842,6 +1962,69 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
.package-content-item {
|
|
|
width: 100%;
|
|
|
}
|
|
|
+.package-content-form {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表单项基础样式 */
|
|
|
+.package-content-form :deep(.el-form-item) {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+.package-content-form :deep(.el-form-item__label) {
|
|
|
+ display: none;
|
|
|
+}
|
|
|
+
|
|
|
+/* 类别表单项样式 */
|
|
|
+.category-form-item {
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+.category-form-item :deep(.el-form-item__content) {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: flex-start;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.category-form-item :deep(.el-form-item__error) {
|
|
|
+ position: static;
|
|
|
+ width: 100%;
|
|
|
+ padding-top: 4px;
|
|
|
+ padding-left: 62px;
|
|
|
+ margin-top: 0;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.5;
|
|
|
+ color: #f56c6c;
|
|
|
+}
|
|
|
+.category-row .el-input {
|
|
|
+ flex: 1;
|
|
|
+ max-width: 400px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 菜品表单项样式 */
|
|
|
+.dish-form-item {
|
|
|
+ position: relative;
|
|
|
+ flex: 1;
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+.dish-form-item :deep(.el-form-item__content) {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+.dish-form-item :deep(.el-form-item__error) {
|
|
|
+ position: absolute;
|
|
|
+ top: 100%;
|
|
|
+ left: 0;
|
|
|
+ z-index: 1;
|
|
|
+ padding-top: 4px;
|
|
|
+ padding-right: 4px;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1.5;
|
|
|
+ color: #f56c6c;
|
|
|
+ white-space: nowrap;
|
|
|
+ background-color: #ffffff;
|
|
|
+}
|
|
|
.package-content-wrapper {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
@@ -1878,8 +2061,18 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
background-color: #f5f7fa;
|
|
|
border-bottom: 1px solid #e4e7ed;
|
|
|
}
|
|
|
+.group-index {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
.header-right {
|
|
|
display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
gap: 12px;
|
|
|
align-items: center;
|
|
|
}
|
|
|
@@ -1887,12 +2080,22 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 16px;
|
|
|
+ min-height: 60px;
|
|
|
padding: 16px;
|
|
|
}
|
|
|
.extra-dishes-container {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 16px;
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+.extra-dishes-container .dish-row {
|
|
|
+ padding-bottom: 22px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+.extra-dishes-container .dish-row:last-child {
|
|
|
+ padding-bottom: 0;
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
|
.package-group-collapsed {
|
|
|
padding: 16px;
|
|
|
@@ -1916,11 +2119,24 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
display: flex;
|
|
|
gap: 12px;
|
|
|
align-items: center;
|
|
|
+ width: 100%;
|
|
|
}
|
|
|
.dish-row {
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
gap: 12px;
|
|
|
- align-items: center;
|
|
|
+ align-items: flex-start;
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+.dish-row:last-child {
|
|
|
+ padding-bottom: 0;
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 当表单项有错误时,增加底部间距 */
|
|
|
+.dish-row .dish-form-item.is-error {
|
|
|
+ margin-bottom: 0;
|
|
|
}
|
|
|
.label {
|
|
|
min-width: 50px;
|
|
|
@@ -1929,6 +2145,7 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
.dish-select-block {
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
flex: 1;
|
|
|
align-items: center;
|
|
|
@@ -1947,6 +2164,28 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
.dish-select-block:focus-within {
|
|
|
border-color: #409eff;
|
|
|
}
|
|
|
+
|
|
|
+/* 当菜品选择有验证错误时,显示错误边框 */
|
|
|
+.dish-form-item.is-error .dish-select-block {
|
|
|
+ border-color: #f56c6c;
|
|
|
+}
|
|
|
+.dish-form-item.is-error .dish-select-block:hover {
|
|
|
+ border-color: #f56c6c;
|
|
|
+}
|
|
|
+.dish-form-item.is-error .dish-select-block:focus-within {
|
|
|
+ border-color: #f56c6c;
|
|
|
+}
|
|
|
+
|
|
|
+/* 优化表单项的错误状态 */
|
|
|
+.package-content-form :deep(.el-form-item.is-error .el-input__wrapper) {
|
|
|
+ box-shadow: 0 0 0 1px #f56c6c inset;
|
|
|
+}
|
|
|
+.package-content-form :deep(.el-form-item.is-error .el-input__wrapper:hover) {
|
|
|
+ box-shadow: 0 0 0 1px #f56c6c inset;
|
|
|
+}
|
|
|
+.package-content-form :deep(.el-form-item.is-error .el-input__wrapper.is-focus) {
|
|
|
+ box-shadow: 0 0 0 1px #f56c6c inset;
|
|
|
+}
|
|
|
.dish-selected-name {
|
|
|
font-size: 14px;
|
|
|
color: #606266;
|
|
|
@@ -1956,16 +2195,64 @@ const handleImageParam = (list: any[], result: any[]) => {
|
|
|
color: #c0c4cc;
|
|
|
}
|
|
|
.quantity-input {
|
|
|
+ flex-shrink: 0;
|
|
|
width: 150px;
|
|
|
}
|
|
|
.delete-dish-btn {
|
|
|
flex-shrink: 0;
|
|
|
+ margin-left: 8px;
|
|
|
}
|
|
|
.add-dish-btn {
|
|
|
width: fit-content;
|
|
|
margin-top: 8px;
|
|
|
}
|
|
|
|
|
|
+/* 优化菜品行的布局,确保在有验证错误时也能正确显示 */
|
|
|
+.dish-row .dish-form-item {
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+.dish-row .dish-form-item:first-child {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 200px;
|
|
|
+ max-width: 350px;
|
|
|
+}
|
|
|
+.dish-row .dish-form-item:last-child {
|
|
|
+ display: flex;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保数量输入框和删除按钮在同一行 */
|
|
|
+.dish-form-item:last-child :deep(.el-form-item__content) {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式布局优化 */
|
|
|
+@media (width <= 768px) {
|
|
|
+ .dish-row {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: stretch;
|
|
|
+ }
|
|
|
+ .dish-row .dish-form-item {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+ .dish-row .dish-form-item:first-child {
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+ .category-row {
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: stretch;
|
|
|
+ }
|
|
|
+ .category-row .el-input {
|
|
|
+ max-width: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/* 菜品选择对话框样式 */
|
|
|
.dish-dialog-content {
|
|
|
display: flex;
|