瀏覽代碼

feat(voucher): 更新代金券管理功能并优化表单校验

- 修改代金券删除接口路径为 `/couponPlatform/deleteCoupon`
- 限制团购名称输入长度为30字符
- 限制类别名称输入长度为10字符
- 添加自定义不可用日期最多10个的限制
- 为预约规则、使用规则和其他规则添加字数统计显示
- 强化库存数量校验,限制最大值为9999
- 完善原价字段校验逻辑,支持非必填及格式验证
- 增强自定义不可用日期校验,防止重复和无效日期
- 优化适用人数校验,增加范围控制(1-99999)
- 控制代金券名称长度不超过20字符
- 设置代金券有效期上限为10000天
- 限制代金券库存最大值为100
- 调整单次可用数量标签文案
- 缩短补充说明最大字数至300字符
- 在列表页中处理空值显示"--"
congxuesong 1 月之前
父節點
當前提交
589d89d2cd

+ 1 - 1
src/api/modules/voucherManagement.ts

@@ -6,7 +6,7 @@ export const getThaliList = params => {
   return http.get<ResPage<StoreUser.ResStoreUserList>>(PORT_NONE + `/couponPlatform/getCouponList`, params);
 };
 export const delThaliById = (params: { id: string }) => {
-  return http.get(PORT_NONE + `/PcGroupBuy/deleteThali`, params);
+  return http.get(PORT_NONE + `/couponPlatform/deleteCoupon`, params);
 };
 export const updateStatus = params => {
   return http.get(PORT_NONE + `/couponPlatform/updateCoupon`, params);

+ 150 - 16
src/views/groupPackageManagement/newGroup.vue

@@ -44,7 +44,7 @@
             </el-form-item>
             <!-- 团购名称 -->
             <el-form-item label="团购名称" prop="groupName">
-              <el-input maxlength="50" v-model="storeInfoModel.groupName" placeholder="请填写团购名称" clearable />
+              <el-input maxlength="30" v-model="storeInfoModel.groupName" placeholder="请填写团购名称" clearable />
             </el-form-item>
             <!-- 开始售卖时间设置方式 -->
             <el-form-item label="开始售卖时间" prop="startTimeType">
@@ -145,7 +145,7 @@
                         >
                           <div class="category-row">
                             <span class="label">类别</span>
-                            <el-input v-model="item.group.groupName" placeholder="请输入" clearable />
+                            <el-input v-model="item.group.groupName" placeholder="请输入" maxlength="10" clearable />
                           </div>
                         </el-form-item>
                         <!-- 第一行菜品,始终显示 -->
@@ -322,7 +322,15 @@
             </template>
             <el-form-item label="" prop="customUnavailableDates" v-else-if="storeInfoModel.disableDateType == 2">
               <div class="date-picker-container">
-                <el-button :icon="Plus" class="add-date-btn" type="primary" @click="addDate"> 添加日期 </el-button>
+                <el-button
+                  :icon="Plus"
+                  class="add-date-btn"
+                  type="primary"
+                  :disabled="storeInfoModel.disableDateList.length >= 10"
+                  @click="addDate"
+                >
+                  添加日期
+                </el-button>
                 <div v-for="(item, index) in storeInfoModel.disableDateList" :key="index" class="date-item">
                   <el-date-picker
                     v-model="storeInfoModel.disableDateList[index]"
@@ -354,6 +362,7 @@
                 :rows="4"
                 type="textarea"
                 placeholder="请输入预约规则"
+                show-word-limit
               />
             </el-form-item>
             <!-- 使用规则  prop="useRules"-->
@@ -364,6 +373,7 @@
                 :rows="4"
                 type="textarea"
                 placeholder="请输入使用规则"
+                show-word-limit
               />
             </el-form-item>
             <!-- 适用人数 -->
@@ -378,6 +388,7 @@
                 :rows="4"
                 type="textarea"
                 placeholder="请输入其他规则"
+                show-word-limit
               />
             </el-form-item>
             <!-- 发票信息  prop="invoiceInformation"-->
@@ -575,7 +586,24 @@ const rules = reactive({
   inventoryNum: [
     { required: true, message: "请填写库存数量" },
     {
-      validator: validatePositiveInteger("库存数量必须为正整数", { required: false }),
+      validator: (rule: any, value: any, callback: any) => {
+        // 先验证是否为正整数
+        validatePositiveInteger("库存数量必须为正整数", { required: false })(rule, value, (error: any) => {
+          if (error) {
+            callback(error);
+            return;
+          }
+          // 验证最大值
+          if (value) {
+            const numValue = Number(value);
+            if (!isNaN(numValue) && numValue > 99999) {
+              callback(new Error("库存数量不得大于99999"));
+              return;
+            }
+          }
+          callback();
+        });
+      },
       trigger: "blur"
     }
   ],
@@ -618,9 +646,9 @@ const rules = reactive({
     }
   ],
   originalPrice: [
-    { required: true, message: "请输入原价" },
     {
       validator: (rule: any, value: any, callback: any) => {
+        // 非必填,如果为空则直接通过
         if (!value || value.toString().trim() === "") {
           callback();
           return;
@@ -633,6 +661,10 @@ const rules = reactive({
         callback();
       },
       trigger: "blur"
+    },
+    {
+      validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
+      trigger: "blur"
     }
   ],
   preferentialPrice: [
@@ -720,12 +752,62 @@ const rules = reactive({
             callback(new Error("至少需要添加一个自定义不可用日期"));
             return;
           }
-          validateDateListArray(
-            () => storeInfoModel.value.disableDateList,
-            "日期项未完整填写",
-            "开始时间必须早于结束时间",
-            true
-          )(rule, value, callback);
+
+          const dateList = storeInfoModel.value.disableDateList;
+          const today = new Date();
+          today.setHours(0, 0, 0, 0);
+
+          // 用于存储所有日期范围,用于检查重复
+          const dateRanges: Array<{ start: Date; end: Date; index: number }> = [];
+
+          for (let i = 0; i < dateList.length; i++) {
+            const dateItem = dateList[i];
+            if (!dateItem || !Array.isArray(dateItem) || dateItem.length !== 2) {
+              callback(new Error(`第${i + 1}个日期项未完整填写`));
+              return;
+            }
+
+            const startDate = new Date(dateItem[0]);
+            const endDate = new Date(dateItem[1]);
+            startDate.setHours(0, 0, 0, 0);
+            endDate.setHours(0, 0, 0, 0);
+
+            // 验证不能早于今天
+            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;
+            }
+
+            // 存储日期范围用于重复检查
+            dateRanges.push({ start: startDate, end: endDate, index: i });
+          }
+
+          // 检查日期范围是否重复
+          for (let i = 0; i < dateRanges.length; i++) {
+            for (let j = i + 1; j < dateRanges.length; j++) {
+              const range1 = dateRanges[i];
+              const range2 = dateRanges[j];
+
+              // 检查两个日期范围是否有重叠
+              // 重叠条件:range1的开始时间 <= range2的结束时间 && range1的结束时间 >= range2的开始时间
+              if (range1.start.getTime() <= range2.end.getTime() && range1.end.getTime() >= range2.start.getTime()) {
+                callback(new Error(`第${range1.index + 1}个日期项与第${range2.index + 1}个日期项存在重复,请重新选择`));
+                return;
+              }
+            }
+          }
+
+          callback();
         } else {
           callback();
         }
@@ -736,9 +818,34 @@ const rules = reactive({
   reservationRules: [{ required: true, message: "请输入预约规则" }],
   useRules: [{ required: true, message: "请输入使用规则" }],
   applicableNum: [
-    { required: true, message: "请输入适用人数" },
     {
-      validator: validatePositiveInteger("适用人数必须为正整数", { required: false }),
+      validator: (rule: any, value: any, callback: any) => {
+        // 非必填,如果为空则直接通过
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        // 先验证是否为正整数
+        validatePositiveInteger("适用人数必须为正整数", { required: false })(rule, value, (error: any) => {
+          if (error) {
+            callback(error);
+            return;
+          }
+          // 验证范围:1-99999
+          const numValue = Number(value);
+          if (!isNaN(numValue)) {
+            if (numValue < 1) {
+              callback(new Error("适用人数不能小于1"));
+              return;
+            }
+            if (numValue > 99999) {
+              callback(new Error("适用人数不能大于99999"));
+              return;
+            }
+          }
+          callback();
+        });
+      },
       trigger: "blur"
     }
   ],
@@ -1323,6 +1430,11 @@ const handlePictureCardPreview = (file: any) => {
  * 添加自定义不可用日期
  */
 const addDate = () => {
+  // 检查最大数量限制
+  if (storeInfoModel.value.disableDateList.length >= 10) {
+    ElMessage.warning("最多只能添加10个自定义不可用日期");
+    return;
+  }
   storeInfoModel.value.disableDateList.push(null);
 };
 
@@ -1797,6 +1909,11 @@ const packageFormRules = computed(() => {
             return;
           }
           const trimmedValue = value.toString().trim();
+          // 检查长度(不得大于10字)
+          if (trimmedValue.length > 10) {
+            callback(new Error("类别不得大于10字"));
+            return;
+          }
           // 检查是否与其他分组的类别重复
           for (let i = 0; i < lifeGroupBuyThalis.value.length; i++) {
             // 跳过当前分组
@@ -1836,9 +1953,26 @@ const packageFormRules = computed(() => {
             trigger: "blur"
           },
           {
-            validator: validatePositiveInteger(`第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量必须为正整数`, {
-              required: false
-            }),
+            validator: (rule: any, value: any, callback: any) => {
+              // 先验证是否为正整数
+              validatePositiveInteger(`第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量必须为正整数`, {
+                required: false
+              })(rule, value, (error: any) => {
+                if (error) {
+                  callback(error);
+                  return;
+                }
+                // 验证最大值
+                if (value) {
+                  const numValue = Number(value);
+                  if (!isNaN(numValue) && numValue > 99999) {
+                    callback(new Error("数量不得大于99999"));
+                    return;
+                  }
+                }
+                callback();
+              });
+            },
             trigger: "blur"
           }
         ];

+ 22 - 3
src/views/voucherManagement/index.vue

@@ -61,7 +61,7 @@
           link
           type="primary"
           @click="deleteRow(scope.row)"
-          v-if="canShowButton(scope.row.status, OPERATION_PERMISSIONS.删除)"
+          v-if="canShowButton(scope.row.status, OPERATION_PERMISSIONS.删除) && scope.row.dataType == 1"
         >
           删除
         </el-button>
@@ -151,6 +151,19 @@ const rules = reactive<FormRules<RuleForm>>({
       pattern: /^(0|[1-9][0-9]*)$/,
       message: "请输入整数,不允许输入小数,负数",
       trigger: "blur"
+    },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (value) {
+          const numValue = Number(value);
+          if (!isNaN(numValue) && numValue > 10000) {
+            callback(new Error("库存不得大于10000"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "blur"
     }
   ]
 });
@@ -188,11 +201,17 @@ const columns = reactive<ColumnProps<any>[]>([
   },
   {
     prop: "saleNum",
-    label: "已售"
+    label: "已售",
+    render: scope => {
+      return scope.row.saleNum === null || !scope.row.saleNum ? "--" : scope.row.saleNum;
+    }
   },
   {
     prop: "singleQty",
-    label: "剩余库存"
+    label: "剩余库存",
+    render: scope => {
+      return scope.row.singleQty === null ? "--" : scope.row.singleQty;
+    }
   },
   {
     prop: "endDate",

+ 48 - 8
src/views/voucherManagement/newVoucher.vue

@@ -14,7 +14,7 @@
             <h3 style="font-weight: bold">基础信息:</h3>
             <!-- 代金券名称 -->
             <el-form-item label="代金券名称" prop="name">
-              <el-input maxlength="50" v-model="voucherModel.name" placeholder="请输入" clearable />
+              <el-input maxlength="20" v-model="voucherModel.name" placeholder="请输入" clearable />
             </el-form-item>
             <!-- 抵扣价格 -->
             <el-form-item label="抵扣价格(¥)" prop="offprice">
@@ -71,7 +71,13 @@
             <el-form-item label="" prop="expirationDate" v-if="voucherModel.expirationType == 0">
               <div class="expiration-date-container">
                 <span class="expiration-label">用户购买</span>
-                <el-input-number v-model="voucherModel.expirationDate" placeholder="请输入" :min="0" class="expiration-input" />
+                <el-input-number
+                  v-model="voucherModel.expirationDate"
+                  placeholder="请输入"
+                  :min="0"
+                  :max="10000"
+                  class="expiration-input"
+                />
                 <span class="expiration-label">天内有效</span>
               </div>
             </el-form-item>
@@ -174,8 +180,8 @@
           <!-- 使用规则模块 -->
           <div class="model">
             <h3 style="font-weight: bold">使用规则:</h3>
-            <!-- 单可用数量 -->
-            <el-form-item label="单可用数量" prop="singleCanUse">
+            <!-- 单可用数量 -->
+            <el-form-item label="单可用数量" prop="singleCanUse">
               <el-input v-model="voucherModel.singleCanUse" maxlength="15" placeholder="请输入" clearable />
             </el-form-item>
             <!-- 限购数量 -->
@@ -206,7 +212,7 @@
             <h3 style="font-weight: bold">补充说明:</h3>
             <el-form-item label="补充说明" prop="supplement">
               <el-input
-                maxlength="500"
+                maxlength="300"
                 v-model="voucherModel.supplement"
                 :rows="4"
                 type="textarea"
@@ -384,10 +390,23 @@ const rules = reactive({
             callback(new Error("请输入用户购买天数"));
             return;
           }
+          // 先验证是否为正整数
           validatePositiveInteger("用户购买天数必须为正整数", { required: false, checkLeadingZero: false })(
             rule,
             value,
-            callback
+            (error: any) => {
+              if (error) {
+                callback(error);
+                return;
+              }
+              // 验证最大值
+              const numValue = Number(value);
+              if (!isNaN(numValue) && numValue > 10000) {
+                callback(new Error("有效期不得大于10000"));
+                return;
+              }
+              callback();
+            }
           );
         } else {
           callback();
@@ -449,9 +468,30 @@ const rules = reactive({
     }
   ],
   singleQty: [
-    { required: true, message: "请输入库存" },
     {
-      validator: validatePositiveInteger("库存必须为正整数", { required: false }),
+      validator: (rule: any, value: any, callback: any) => {
+        // 非必填,如果为空则直接通过
+        if (!value || value.toString().trim() === "") {
+          callback();
+          return;
+        }
+        // 先验证是否为正整数
+        validatePositiveInteger("库存必须为正整数", { required: false })(rule, value, (error: any) => {
+          if (error) {
+            callback(error);
+            return;
+          }
+          // 验证最大值
+          if (value) {
+            const numValue = Number(value);
+            if (!isNaN(numValue) && numValue > 10000) {
+              callback(new Error("库存不得大于10000"));
+              return;
+            }
+          }
+          callback();
+        });
+      },
       trigger: "blur"
     }
   ],