Ver Fonte

feat(utils): 添加多种表单验证函数并优化现有验证逻辑

- 新增正整数、正数、密码、确认密码、日期范围等验证函数
- 添加条件必填、数组长度、日期列表等高级验证功能
- 重构现有组件中的自定义验证逻辑,统一使用新验证函数
- 优化日期验证逻辑,支持更灵活的配置选项
- 添加验证函数的弃用标记,引导使用新的配置方式
- 统一验证错误提示信息,提高用户体验一致性
- 增强验证函数的类型安全性和参数校验
- 完善验证函数的 JSDoc 文档注释
- 修复日期验证中前导零检查的逻辑问题
- 提升验证函数对空值和边界情况的处理能力
congxuesong há 1 mês atrás
pai
commit
2dfb198f88

+ 420 - 1
src/utils/eleValidate.ts

@@ -13,7 +13,9 @@ export function checkPhoneNumber(rule: any, value: any, callback: any) {
   }
 }
 
-// 自定义校验函数:检查是否全是空格或为空
+/**
+ * 自定义校验函数:检查是否全是空格或为空
+ */
 export function validateNoEmptySpaces(rule: any, value: any, callback: any) {
   // 如果值为空或者去空格后为空字符串,则报错
   if (value === null || value === undefined || value.trim() === "") {
@@ -22,3 +24,420 @@ export function validateNoEmptySpaces(rule: any, value: any, callback: any) {
     callback(); // 校验通过
   }
 }
+
+/**
+ * 验证正整数
+ * @param errorMessage - 错误提示信息,默认为"必须为正整数"
+ * @param options - 配置选项
+ * @param options.required - 是否必填,默认为true
+ * @param options.checkLeadingZero - 是否检查前导零,默认为true(用于字符串输入)
+ * @returns 验证函数
+ */
+export function validatePositiveInteger(
+  errorMessage: string = "必须为正整数",
+  options: { required?: boolean; checkLeadingZero?: boolean } = {}
+) {
+  const { required = true, checkLeadingZero = true } = options;
+
+  return (rule: any, value: any, callback: any) => {
+    // 检查是否为空
+    if (value === null || value === undefined || value === "") {
+      if (required) {
+        callback(new Error(errorMessage));
+        return;
+      } else {
+        callback();
+        return;
+      }
+    }
+
+    // 对于数字类型(如 el-input-number),直接验证数字
+    if (typeof value === "number") {
+      if (value <= 0 || !Number.isInteger(value)) {
+        callback(new Error(errorMessage));
+        return;
+      }
+      callback();
+      return;
+    }
+
+    // 转换为字符串进行检查
+    const strValue = value.toString().trim();
+    if (strValue === "") {
+      if (required) {
+        callback(new Error(errorMessage));
+        return;
+      } else {
+        callback();
+        return;
+      }
+    }
+
+    // 检查是否有前导零(除了单独的"0")
+    if (checkLeadingZero && strValue.length > 1 && strValue.startsWith("0")) {
+      callback(new Error(errorMessage));
+      return;
+    }
+
+    // 检查是否为数字
+    const num = Number(strValue);
+    if (isNaN(num)) {
+      callback(new Error(errorMessage));
+      return;
+    }
+
+    // 检查是否为正数
+    if (num <= 0) {
+      callback(new Error(errorMessage));
+      return;
+    }
+
+    // 检查是否为整数
+    if (!Number.isInteger(num)) {
+      callback(new Error(errorMessage));
+      return;
+    }
+
+    callback();
+  };
+}
+
+/**
+ * 验证正数(可以是小数)
+ * @param errorMessage - 错误提示信息,默认为"必须为正数"
+ * @returns 验证函数
+ */
+export function validatePositiveNumber(errorMessage: string = "必须为正数") {
+  return (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(errorMessage));
+      return;
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证密码(6-16位,必须包含字母和数字)
+ */
+export function validatePassword(rule: any, value: string, callback: any) {
+  if (!value) {
+    callback(new Error("请输入密码"));
+    return;
+  }
+  if (value.length < 6 || value.length > 16) {
+    callback(new Error("密码长度为6-16位,密码必须包含字母和数字"));
+    return;
+  }
+  if (!/^(?=.*[a-zA-Z])(?=.*\d).+$/.test(value)) {
+    callback(new Error("密码长度为6-16位,密码必须包含字母和数字"));
+    return;
+  }
+  callback();
+}
+
+/**
+ * 验证确认密码(两次输入的密码必须一致)
+ * @param originalPassword - 原始密码值或获取原始密码的函数
+ * @param errorMessage - 错误提示信息,默认为"两次输入的密码不一致"
+ * @returns 验证函数
+ */
+export function validateConfirmPassword(
+  originalPassword: string | (() => string),
+  errorMessage: string = "两次输入的密码不一致"
+) {
+  return (rule: any, value: string, callback: any) => {
+    if (!value) {
+      callback(new Error("请确认密码"));
+      return;
+    }
+    const original = typeof originalPassword === "function" ? originalPassword() : originalPassword;
+    if (value !== original) {
+      callback(new Error(errorMessage));
+      return;
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证日期不能早于今天
+ * @param errorMessage - 错误提示信息,默认为"日期不能早于当前时间"
+ * @returns 验证函数
+ */
+export function validateDateNotBeforeToday(errorMessage: string = "日期不能早于当前时间") {
+  return (rule: any, value: any, callback: any) => {
+    if (!value) {
+      callback();
+      return;
+    }
+    const selectedDate = new Date(value);
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+    if (selectedDate < today) {
+      callback(new Error(errorMessage));
+      return;
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证日期范围(开始时间必须早于结束时间)
+ * @param getStartDate - 获取开始日期的函数
+ * @param getEndDate - 获取结束日期的函数(可选,如果不提供则使用当前值作为结束日期)
+ * @param startErrorMessage - 开始日期错误提示,默认为"开始时间不能早于当前时间"
+ * @param endErrorMessage - 结束日期错误提示,默认为"结束时间不能早于当前时间"
+ * @param rangeErrorMessage - 日期范围错误提示,默认为"开始时间必须早于结束时间"
+ * @param checkToday - 是否检查不能早于今天,默认为true
+ * @returns 验证函数
+ */
+export function validateDateRange(
+  getStartDate: () => any,
+  getEndDate?: () => any,
+  startErrorMessage: string = "开始时间不能早于当前时间",
+  endErrorMessage: string = "结束时间不能早于当前时间",
+  rangeErrorMessage: string = "开始时间必须早于结束时间",
+  checkToday: boolean = true
+) {
+  return (rule: any, value: any, callback: any, isStartDate: boolean = true) => {
+    if (!value) {
+      callback();
+      return;
+    }
+    const selectedDate = new Date(value);
+
+    // 验证不能早于今天
+    if (checkToday) {
+      const today = new Date();
+      today.setHours(0, 0, 0, 0);
+      if (selectedDate < today) {
+        callback(new Error(isStartDate ? startErrorMessage : endErrorMessage));
+        return;
+      }
+    }
+
+    // 验证日期范围
+    if (isStartDate && getEndDate) {
+      const endDate = getEndDate();
+      if (endDate) {
+        const end = new Date(endDate);
+        if (selectedDate >= end) {
+          callback(new Error(rangeErrorMessage));
+          return;
+        }
+      }
+    } else if (!isStartDate && getStartDate) {
+      const startDate = getStartDate();
+      if (startDate) {
+        const start = new Date(startDate);
+        if (selectedDate <= start) {
+          callback(new Error(rangeErrorMessage));
+          return;
+        }
+      }
+    }
+
+    callback();
+  };
+}
+
+/**
+ * 验证正整数(简化版,用于库存、数量等字段,允许空值)
+ * @param errorMessage - 错误提示信息,默认为"必须为正整数"
+ * @returns 验证函数
+ * @deprecated 请使用 validatePositiveInteger(errorMessage, { required: false })
+ */
+export function validatePositiveIntegerOptional(errorMessage: string = "必须为正整数") {
+  return validatePositiveInteger(errorMessage, { required: false, checkLeadingZero: true });
+}
+
+/**
+ * 验证日期范围(开始时间必须早于结束时间,且不能早于今天)
+ * @param getStartDate - 获取开始日期的函数
+ * @param getEndDate - 获取结束日期的函数(可选,如果不提供则使用当前值作为结束日期)
+ * @param startErrorMessage - 开始日期错误提示,默认为"开始时间不能早于当前时间"
+ * @param endErrorMessage - 结束日期错误提示,默认为"结束时间不能早于当前时间"
+ * @param rangeErrorMessage - 日期范围错误提示,默认为"开始时间必须早于结束时间"
+ * @returns 验证函数
+ * @deprecated 请使用 validateDateRange(..., true) 或 validateDateRange(..., checkToday: true)
+ */
+export function validateDateRangeWithToday(
+  getStartDate: () => any,
+  getEndDate?: () => any,
+  startErrorMessage: string = "开始时间不能早于当前时间",
+  endErrorMessage: string = "结束时间不能早于当前时间",
+  rangeErrorMessage: string = "开始时间必须早于结束时间"
+) {
+  return validateDateRange(getStartDate, getEndDate, startErrorMessage, endErrorMessage, rangeErrorMessage, true);
+}
+
+/**
+ * 验证日期范围数组(每个日期范围的开始时间必须早于结束时间)
+ * @param errorMessage - 错误提示信息,默认为"开始时间必须早于结束时间"
+ * @param checkToday - 是否检查不能早于今天,默认为false
+ * @param todayErrorMessage - 今天错误提示,默认为"时间不能早于当前时间"
+ * @returns 验证函数
+ */
+export function validateDateRangeArray(
+  errorMessage: string = "开始时间必须早于结束时间",
+  checkToday: boolean = false,
+  todayErrorMessage: string = "时间不能早于当前时间"
+) {
+  return (rule: any, value: any, callback: any) => {
+    if (!value || value.length === 0) {
+      callback();
+      return;
+    }
+    if (!Array.isArray(value) || value.length !== 2) {
+      callback(new Error("请选择完整的日期范围"));
+      return;
+    }
+
+    const startDate = new Date(value[0]);
+    const endDate = new Date(value[1]);
+
+    // 验证不能早于今天
+    if (checkToday) {
+      const today = new Date();
+      today.setHours(0, 0, 0, 0);
+      if (startDate < today) {
+        callback(new Error("开始" + todayErrorMessage));
+        return;
+      }
+      if (endDate < today) {
+        callback(new Error("结束" + todayErrorMessage));
+        return;
+      }
+    }
+
+    // 验证开始时间必须早于结束时间
+    if (startDate >= endDate) {
+      callback(new Error(errorMessage));
+      return;
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证日期范围数组(带今天检查)
+ * @param checkToday - 是否检查不能早于今天,默认为true
+ * @param rangeErrorMessage - 日期范围错误提示,默认为"开始时间必须早于结束时间"
+ * @param todayErrorMessage - 今天错误提示,默认为"时间不能早于当前时间"
+ * @returns 验证函数
+ * @deprecated 请使用 validateDateRangeArray(rangeErrorMessage, checkToday, todayErrorMessage)
+ */
+export function validateDateRangeArrayWithToday(
+  checkToday: boolean = true,
+  rangeErrorMessage: string = "开始时间必须早于结束时间",
+  todayErrorMessage: string = "时间不能早于当前时间"
+) {
+  return validateDateRangeArray(rangeErrorMessage, checkToday, todayErrorMessage);
+}
+
+/**
+ * 条件必填验证(根据某个条件判断是否必填)
+ * @param condition - 判断条件的函数,返回true表示必填
+ * @param errorMessage - 错误提示信息
+ * @returns 验证函数
+ */
+export function validateConditionalRequired(condition: () => boolean, errorMessage: string) {
+  return (rule: any, value: any, callback: any) => {
+    if (condition()) {
+      if (!value || (Array.isArray(value) && value.length === 0) || (typeof value === "string" && value.trim() === "")) {
+        callback(new Error(errorMessage));
+        return;
+      }
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证数组最小长度
+ * @param minLength - 最小长度
+ * @param errorMessage - 错误提示信息
+ * @returns 验证函数
+ */
+export function validateArrayMinLength(minLength: number, errorMessage: string) {
+  return (rule: any, value: any, callback: any) => {
+    if (!value || !Array.isArray(value) || value.length < minLength) {
+      callback(new Error(errorMessage));
+      return;
+    }
+    callback();
+  };
+}
+
+/**
+ * 验证日期数组列表(每个日期项的开始时间必须早于结束时间)
+ * @param getDateList - 获取日期列表的函数
+ * @param itemErrorMessage - 单个日期项错误提示,默认为"日期项未完整填写"
+ * @param rangeErrorMessage - 日期范围错误提示,默认为"开始时间必须早于结束时间"
+ * @param checkToday - 是否检查不能早于今天,默认为false
+ * @returns 验证函数
+ */
+export function validateDateListArray(
+  getDateList: () => any[],
+  itemErrorMessage: string = "日期项未完整填写",
+  rangeErrorMessage: string = "开始时间必须早于结束时间",
+  checkToday: boolean = false
+) {
+  return (rule: any, value: any, callback: any) => {
+    const dateList = getDateList();
+    if (!dateList || dateList.length === 0) {
+      callback();
+      return;
+    }
+
+    const today = new Date();
+    today.setHours(0, 0, 0, 0);
+
+    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}个${itemErrorMessage}`));
+        return;
+      }
+
+      const startDate = new Date(dateItem[0]);
+      const endDate = new Date(dateItem[1]);
+
+      // 验证不能早于今天
+      if (checkToday) {
+        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}个日期项的${rangeErrorMessage}`));
+        return;
+      }
+    }
+
+    callback();
+  };
+}
+
+/**
+ * 验证正整数(用于 el-input-number,value 已经是数字类型)
+ * @param errorMessage - 错误提示信息,默认为"必须为正整数"
+ * @returns 验证函数
+ * @deprecated 请使用 validatePositiveInteger(errorMessage, { required: false, checkLeadingZero: false })
+ */
+export function validatePositiveIntegerForNumber(errorMessage: string = "必须为正整数") {
+  return validatePositiveInteger(errorMessage, { required: false, checkLeadingZero: false });
+}

+ 23 - 84
src/views/couponManagement/newCoupon.vue

@@ -106,6 +106,7 @@ import { ElMessage } from "element-plus";
 import { useRoute, useRouter } from "vue-router";
 import type { FormInstance } from "element-plus";
 import { getCouponDetail } from "@/api/modules/couponManagement";
+import { validatePositiveNumber, validatePositiveInteger, validateDateRange } from "@/utils/eleValidate";
 // ==================== 响应式数据定义 ====================
 
 // 路由相关
@@ -121,108 +122,49 @@ const rules = reactive({
   faceValue: [
     { required: true, message: "请输入面值" },
     {
-      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("面值必须为正数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveNumber("面值必须为正数"),
       trigger: "blur"
     }
   ],
   startCollectionTime: [
     { required: true, message: "请选择开始领取时间" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value) {
-          callback();
-          return;
-        }
-        const selectedDate = new Date(value);
-        const today = new Date();
-        today.setHours(0, 0, 0, 0);
-        if (selectedDate < today) {
-          callback(new Error("开始领取时间不能早于当前时间"));
-          return;
-        }
-        if (couponModel.value.endCollectionTime) {
-          const endDate = new Date(couponModel.value.endCollectionTime);
-          if (selectedDate >= endDate) {
-            callback(new Error("开始领取时间必须早于结束领取时间"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateDateRange(
+        () => couponModel.value.startCollectionTime,
+        () => couponModel.value.endCollectionTime,
+        "开始领取时间不能早于当前时间",
+        "结束领取时间不能早于当前时间",
+        "开始领取时间必须早于结束领取时间",
+        true
+      ),
       trigger: "change"
     }
   ],
   endCollectionTime: [
     { required: true, message: "请选择结束领取时间" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value) {
-          callback();
-          return;
-        }
-        const selectedDate = new Date(value);
-        const today = new Date();
-        today.setHours(0, 0, 0, 0);
-        if (selectedDate < today) {
-          callback(new Error("结束领取时间不能早于当前时间"));
-          return;
-        }
-        if (couponModel.value.startCollectionTime) {
-          const startDate = new Date(couponModel.value.startCollectionTime);
-          if (selectedDate <= startDate) {
-            callback(new Error("结束领取时间必须晚于开始领取时间"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateDateRange(
+        () => couponModel.value.startCollectionTime,
+        () => couponModel.value.endCollectionTime,
+        "开始领取时间不能早于当前时间",
+        "结束领取时间不能早于当前时间",
+        "开始领取时间必须早于结束领取时间",
+        true
+      ),
       trigger: "change"
     }
   ],
   validityPeriod: [
     { required: true, message: "请输入有效期" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value || value.toString().trim() === "") {
-          callback();
-          return;
-        }
-        const num = Number(value);
-        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
-          callback(new Error("有效期必须为正整数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("有效期必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
   inventory: [
     { required: true, message: "请输入库存" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value || value.toString().trim() === "") {
-          callback();
-          return;
-        }
-        const num = Number(value);
-        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
-          callback(new Error("库存必须为正整数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("库存必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
@@ -234,13 +176,10 @@ const rules = reactive({
             callback(new Error("请输入最低消费金额"));
             return;
           }
-          const num = Number(value);
-          if (isNaN(num) || num <= 0) {
-            callback(new Error("最低消费金额必须为正数"));
-            return;
-          }
+          validatePositiveNumber("最低消费金额必须为正数")(rule, value, callback);
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "blur"
     }

+ 38 - 127
src/views/groupPackageManagement/newGroup.vue

@@ -467,6 +467,12 @@ import { saveDraft, getHolidayList, getMenuByStoreId, getThaliById, saveThali }
 import { useRouter, useRoute } from "vue-router";
 import type { UploadProps, FormInstance, UploadInstance, UploadFile } from "element-plus";
 import { localGet } from "@/utils";
+import {
+  validatePositiveInteger,
+  validateDateRangeArray,
+  validateConditionalRequired,
+  validateDateListArray
+} from "@/utils/eleValidate";
 
 // ==================== 响应式数据定义 ====================
 
@@ -499,6 +505,9 @@ const generateImgSort = (() => {
   };
 })();
 
+// ==================== 验证辅助函数 ====================
+// 验证函数已从 @/utils/eleValidate 导入
+
 // ==================== 表单验证规则 ====================
 const rules = reactive({
   imageValueStr: [{ required: true, message: "请上传图片" }],
@@ -565,22 +574,7 @@ const rules = reactive({
   inventoryNum: [
     { required: true, message: "请填写库存数量" },
     {
-      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("库存数量必须为正整数"));
-          return;
-        }
-        if (!Number.isInteger(num)) {
-          callback(new Error("库存数量必须为正整数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("库存数量必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
@@ -594,17 +588,10 @@ const rules = reactive({
             callback(new Error("请输入自定义限购数量"));
             return;
           }
-          const num = Number(value);
-          if (isNaN(num) || num <= 0) {
-            callback(new Error("自定义限购数量必须为正整数"));
-            return;
-          }
-          if (!Number.isInteger(num)) {
-            callback(new Error("自定义限购数量必须为正整数"));
-            return;
-          }
+          validatePositiveInteger("自定义限购数量必须为正整数")(rule, value, callback);
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "blur"
     }
@@ -670,17 +657,19 @@ const rules = reactive({
     {
       required: true,
       validator: (rule: any, value: any, callback: any) => {
-        if (storeInfoModel.value.expirationDate === 0) {
+        if (storeInfoModel.value.effectiveDateType === 0) {
           if (value === null || value === undefined || value === "") {
             callback(new Error("请输入用户购买天数"));
             return;
           }
-          if (value <= 0) {
-            callback(new Error("天数必须大于0"));
-            return;
-          }
+          validatePositiveInteger("用户购买天数必须为正整数", { required: false, checkLeadingZero: false })(
+            rule,
+            value,
+            callback
+          );
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "blur"
     }
@@ -694,26 +683,10 @@ const rules = reactive({
             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;
-          }
+          validateDateRangeArray("开始时间必须早于结束时间", true, "时间不能早于当前时间")(rule, value, callback);
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "change"
     }
@@ -722,30 +695,14 @@ const rules = reactive({
   unavailableWeekdays: [
     {
       required: true,
-      validator: (rule: any, value: any, callback: any) => {
-        if (storeInfoModel.value.disableDateType === 1) {
-          if (!value || value.length === 0) {
-            callback(new Error("至少需要选择一个星期"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(() => storeInfoModel.value.disableDateType === 1, "至少需要选择一个星期"),
       trigger: "change"
     }
   ],
   unavailableHolidays: [
     {
       required: true,
-      validator: (rule: any, value: any, callback: any) => {
-        if (storeInfoModel.value.disableDateType === 1) {
-          if (!value || value.length === 0) {
-            callback(new Error("至少需要选择一个节日"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(() => storeInfoModel.value.disableDateType === 1, "至少需要选择一个节日"),
       trigger: "change"
     }
   ],
@@ -758,33 +715,15 @@ const rules = reactive({
             callback(new Error("至少需要添加一个自定义不可用日期"));
             return;
           }
-          const today = new Date();
-          today.setHours(0, 0, 0, 0);
-          // 验证每个日期项是否已填写
-          for (let i = 0; i < storeInfoModel.value.disableDateList.length; i++) {
-            if (!storeInfoModel.value.disableDateList[i] || storeInfoModel.value.disableDateList[i].length !== 2) {
-              callback(new Error(`第${i + 1}个日期项未完整填写`));
-              return;
-            }
-            // 验证开始时间和结束时间不能早于当前时间
-            const startDate = new Date(storeInfoModel.value.disableDateList[i][0]);
-            const endDate = new Date(storeInfoModel.value.disableDateList[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;
-            }
-          }
+          validateDateListArray(
+            () => storeInfoModel.value.disableDateList,
+            "日期项未完整填写",
+            "开始时间必须早于结束时间",
+            true
+          )(rule, value, callback);
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "change"
     }
@@ -794,22 +733,7 @@ const rules = reactive({
   applicableNum: [
     { required: true, message: "请输入适用人数" },
     {
-      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("适用人数必须为正整数"));
-          return;
-        }
-        if (!Number.isInteger(num)) {
-          callback(new Error("适用人数必须为正整数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("适用人数必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
@@ -1906,22 +1830,9 @@ const packageFormRules = computed(() => {
             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();
-            },
+            validator: validatePositiveInteger(`第${groupIndex + 1}个分组的第${dishIndex + 1}个菜品数量必须为正整数`, {
+              required: false
+            }),
             trigger: "blur"
           }
         ];

+ 3 - 27
src/views/login/index.vue

@@ -591,6 +591,7 @@ import { useKeepAliveStore } from "@/stores/modules/keepAlive";
 import { initDynamicRouter } from "@/routers/modules/dynamicRouter";
 import type { ElForm } from "element-plus";
 import md5 from "md5";
+import { validatePassword, validateConfirmPassword } from "@/utils/eleValidate";
 import {
   forgetPassword,
   getImgCode,
@@ -675,22 +676,7 @@ const codeRules = reactive({
   ],
   code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
 });
-// 密码验证函数
-const validatePassword = (rule: any, value: string, callback: any) => {
-  if (!value) {
-    callback(new Error("请输入密码"));
-    return;
-  }
-  if (value.length < 6 || value.length > 16) {
-    callback(new Error("密码长度为6-16位,密码必须包含字母和数字"));
-    return;
-  }
-  if (!/^(?=.*[a-zA-Z])(?=.*\d).+$/.test(value)) {
-    callback(new Error("密码长度为6-16位,密码必须包含字母和数字"));
-    return;
-  }
-  callback();
-};
+// 密码验证函数已从 @/utils/eleValidate 导入
 
 const forgetRules = ref({
   phone: [
@@ -718,17 +704,7 @@ const registerRules = reactive({
   confirmPassword: [
     { required: true, message: "请确认密码", trigger: "blur" },
     {
-      validator: (rule: any, value: string, callback: any) => {
-        if (!value) {
-          callback(new Error("请确认密码"));
-          return;
-        }
-        if (value !== registerForm.password) {
-          callback(new Error("两次输入的密码不一致"));
-        } else {
-          callback();
-        }
-      },
+      validator: validateConfirmPassword(() => registerForm.password),
       trigger: "blur"
     }
   ]

+ 50 - 171
src/views/voucherManagement/newVoucher.vue

@@ -236,6 +236,15 @@ import { ElMessage } from "element-plus";
 import { useRoute, useRouter } from "vue-router";
 import type { FormInstance } from "element-plus";
 import { getVoucherDetail } from "@/api/modules/voucherManagement";
+import {
+  validatePositiveNumber,
+  validatePositiveInteger,
+  validateDateRange,
+  validateDateRangeArray,
+  validateConditionalRequired,
+  validateArrayMinLength,
+  validateDateListArray
+} from "@/utils/eleValidate";
 
 // ==================== 响应式数据定义 ====================
 
@@ -253,109 +262,49 @@ const rules = reactive({
   discountPrice: [
     { required: true, message: "请输入抵扣价格" },
     {
-      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("抵扣价格必须为正数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveNumber("抵扣价格必须为正数"),
       trigger: "blur"
     }
   ],
   sellingPrice: [
     { required: true, message: "请输入售卖价格" },
     {
-      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("售卖价格必须为正数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveNumber("售卖价格必须为正数"),
       trigger: "blur"
     }
   ],
   startSellingTime: [
     { required: true, message: "请选择开始售卖时间" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value) {
-          callback();
-          return;
-        }
-        const selectedDate = new Date(value);
-        const today = new Date();
-        today.setHours(0, 0, 0, 0);
-        if (selectedDate < today) {
-          callback(new Error("开始售卖时间不能早于当前时间"));
-          return;
-        }
-        if (voucherModel.value.endSellingTime) {
-          const endDate = new Date(voucherModel.value.endSellingTime);
-          if (selectedDate >= endDate) {
-            callback(new Error("开始售卖时间必须早于结束售卖时间"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateDateRange(
+        () => voucherModel.value.startSellingTime,
+        () => voucherModel.value.endSellingTime,
+        "开始售卖时间不能早于当前时间",
+        "结束售卖时间不能早于当前时间",
+        "开始售卖时间必须早于结束售卖时间",
+        true
+      ),
       trigger: "change"
     }
   ],
   endSellingTime: [
     { required: true, message: "请选择结束售卖时间" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value) {
-          callback();
-          return;
-        }
-        const selectedDate = new Date(value);
-        const today = new Date();
-        today.setHours(0, 0, 0, 0);
-        if (selectedDate < today) {
-          callback(new Error("结束售卖时间不能早于当前时间"));
-          return;
-        }
-        if (voucherModel.value.startSellingTime) {
-          const startDate = new Date(voucherModel.value.startSellingTime);
-          if (selectedDate <= startDate) {
-            callback(new Error("结束售卖时间必须晚于开始售卖时间"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateDateRange(
+        () => voucherModel.value.startSellingTime,
+        () => voucherModel.value.endSellingTime,
+        "开始售卖时间不能早于当前时间",
+        "结束售卖时间不能早于当前时间",
+        "开始售卖时间必须早于结束售卖时间",
+        true
+      ),
       trigger: "change"
     }
   ],
   usageTime: [
     { required: true, message: "请选择使用时间" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value || value.length !== 2) {
-          callback();
-          return;
-        }
-        const startDate = new Date(value[0]);
-        const endDate = new Date(value[1]);
-        if (startDate >= endDate) {
-          callback(new Error("使用开始时间必须早于结束时间"));
-          return;
-        }
-        callback();
-      },
+      validator: validateDateRangeArray("使用开始时间必须早于结束时间"),
       trigger: "change"
     }
   ],
@@ -382,20 +331,16 @@ const rules = reactive({
   validityPeriod: [
     {
       required: true,
+      validator: validateConditionalRequired(() => voucherModel.value.validityPeriodType === 1, "请选择指定时间段"),
+      trigger: "change"
+    },
+    {
       validator: (rule: any, value: any, callback: any) => {
-        if (voucherModel.value.validityPeriodType === 1) {
-          if (!value || value.length !== 2) {
-            callback(new Error("请选择指定时间段"));
-            return;
-          }
-          const startDate = new Date(value[0]);
-          const endDate = new Date(value[1]);
-          if (startDate >= endDate) {
-            callback(new Error("开始时间必须早于结束时间"));
-            return;
-          }
+        if (voucherModel.value.validityPeriodType === 1 && value && value.length === 2) {
+          validateDateRangeArray("开始时间必须早于结束时间")(rule, value, callback);
+        } else {
+          callback();
         }
-        callback();
       },
       trigger: "change"
     }
@@ -403,118 +348,52 @@ const rules = reactive({
   unavailableDateType: [{ required: true, message: "请选择不可用日期类型" }],
   unavailableWeekdays: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (voucherModel.value.unavailableDateType === 1) {
-          if (!value || value.length === 0) {
-            callback(new Error("至少需要选择一个星期"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(() => voucherModel.value.unavailableDateType === 1, "至少需要选择一个星期"),
       trigger: "change"
     }
   ],
   unavailableHolidays: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (voucherModel.value.unavailableDateType === 1) {
-          if (!value || value.length === 0) {
-            callback(new Error("至少需要选择一个节日"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(() => voucherModel.value.unavailableDateType === 1, "至少需要选择一个节日"),
       trigger: "change"
     }
   ],
   customUnavailableDates: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (voucherModel.value.unavailableDateType === 2) {
-          if (!dates.value || dates.value.length === 0) {
-            callback(new Error("至少需要添加一个自定义不可用日期"));
-            return;
-          }
-          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 >= endDate) {
-              callback(new Error(`第${i + 1}个日期项的开始时间必须早于结束时间`));
-              return;
-            }
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(
+        () => voucherModel.value.unavailableDateType === 2,
+        "至少需要添加一个自定义不可用日期"
+      ),
+      trigger: "change"
+    },
+    {
+      validator: validateDateListArray(() => dates.value, "日期项未完整填写", "开始时间必须早于结束时间", false),
       trigger: "change"
     }
   ],
   inventory: [
     { required: true, message: "请输入库存" },
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (!value || value.toString().trim() === "") {
-          callback();
-          return;
-        }
-        const num = Number(value);
-        if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
-          callback(new Error("库存必须为正整数"));
-          return;
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("库存必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
   dailyAvailableQuantity: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (value && value.toString().trim() !== "") {
-          const num = Number(value);
-          if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
-            callback(new Error("单日可用数量必须为正整数"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("单日可用数量必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
   purchaseLimitQuantity: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (value && value.toString().trim() !== "") {
-          const num = Number(value);
-          if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
-            callback(new Error("限购数量必须为正整数"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validatePositiveInteger("限购数量必须为正整数", { required: false }),
       trigger: "blur"
     }
   ],
   applicableScopeType: [{ required: true, message: "请选择适用范围类型" }],
   applicableScope: [
     {
-      validator: (rule: any, value: any, callback: any) => {
-        if (voucherModel.value.applicableScopeType === 1) {
-          if (!value || value.trim() === "") {
-            callback(new Error("请输入适用范围"));
-            return;
-          }
-        }
-        callback();
-      },
+      validator: validateConditionalRequired(() => voucherModel.value.applicableScopeType === 1, "请输入适用范围"),
       trigger: "blur"
     }
   ]