Elementplus 2.6.1表单链式校验,享受精简丝滑的开发体验

需求

之前的表单代码看了下,写的比较冗长,于是去万能的Github找点轮子,发现了这个:

GitHub - aweiu/element-ui-verify: 如果你受够了饿了么ElementUI原生的校验方式,那就来试试它吧!一款更懂你的校验插件

但是用起来发现不支持最新的vue,因为里面基于mixin混入。改了一会没耐性了,这么简单个功能,自己卷个轮子算了,索性自己改,但是要有以下几点支持:

1)支持label提示,例如必输的信息,如果前面label为用户的话,输入框提示为“用户昵称不为空”,如果是最小长度检测的话,需要提示“用户昵称最少4字符”。而用户昵称的校验器我只需要设置“用户昵称”这个变量,提示信息自动拼接

2)支持验证器组合,例如同时非空,且为数值格式。

3)支持自定义提示信息,不要自己构造方法,最好都参数化解决

思路

既然都Typescript了,我们就采用对象的方式来解决问题。问题1的方式,只需要在构造器里添加label信息即可。

问题2的方式,写过后端的都知道,有个很舒服的方式叫做 链式调用,OK,把多行代码压缩为一行个的方式就它了。

问题3,给个可选参数就好,这个是es5的好工具,结合typescript的默认值操作很方便的解决

问题4,有些代码直接照搬了github,其它未测试的部分理论上应该没问题,有BUG再回来改😉 

效果

以修改密码的业务为例,这样个界面

改造前

原来代码:

/** 修改密码窗体模块 */
<template>
  <cc-window
    ref="windowDom"
    v-bind="$attrs"
    title="修改当前用户密码"
    width="400px"
    :append-to-body="true"
    @submit="onSubmit"
  >
    <el-form
      ref="editFormDom"
      :model="form"
      auto-complete="off"
      :label-width="100"
      class="h-rmargin40"
      :rules="formRules"
    >
      <el-form-item prop="userPsw" label="输入密码">
        <el-input type="password" v-model="form.userPsw" maxlength="20" show-password />
      </el-form-item>
      <el-form-item class="x-fillitem" prop="reUserPsw" label="确认密码">
        <el-input type="password" v-model="form.reUserPsw" maxlength="20" show-password />
      </el-form-item>
    </el-form>
  </cc-window>
</template>

校验部分

const formRules = reactive<{
  userPsw: any[]
  reUserPsw: any[]
}>({
  userPsw: [
    { required: true, message: '请输入密码', trigger: ['change', 'blur'] },
    {
      pattern: /^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]+\S{5,20}$/,
      message: '需同时含有字母和数字,长度在6-20之间',
      trigger: ['change', 'blur']
    }
  ],
  reUserPsw: [
    { required: true, message: '请重新输入密码', trigger: ['change', 'blur'] },
    {
      validator: (rule: any, value: any, callback: object) => {
        return value === form.userPsw
      },
      message: '两次输入的密码不相等',
      trigger: ['change', 'blur']
    }
  ]
})

改造后

/** 修改密码窗体模块 */
<template>
  <cc-window
    ref="windowDom"
    v-bind="$attrs"
    title="修改当前用户密码"
    width="400px"
    :append-to-body="true"
    @submit="onSubmit"
  >
    <el-form ref="editFormDom" :model="form" auto-complete="off" :label-width="100" class="h-rmargin40">
      <el-form-item
        prop="userPsw"
        label="输入密码"
        :rules="
          utils
            .validate('密码')
            .required()
            .pattern(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]+\S{5,20}$/, '需同时含有字母和数字,长度在6-20之间')
        "
      >
        <el-input type="password" v-model="form.userPsw" maxlength="20" show-password />
      </el-form-item>
      <el-form-item
        class="x-fillitem"
        prop="reUserPsw"
        label="确认密码"
        :rules="utils.validate('密码').compare(form.userPsw)"
      >
        <el-input type="password" v-model="form.reUserPsw" maxlength="20" show-password />
      </el-form-item>
    </el-form>
  </cc-window>
</template>

可见简短了很多,方便使用还避免了出错

代码实现

代码实现也不复杂,写一个模块即可解决:

/**
 * 链式校验模块,支持常用校验方式
 *
 * 使用列:
 * import {validate} from '@/commons/utils/Validator'
 *
 * //建立一个非空4字符以上的昵称校验,未输入提示“请输入昵称”,过短提示“昵称长度最小为 4 字符”
 * validate('昵称').required().minLength(4)
 *
 * 注意除了非空校验外,其它校验都是为空可以通过的,除非显示的指定了非空校验required
 *
 * @author Jim 24/05/09
 */

export interface IValidator extends Array<any> {
  required: (error?: string) => IValidator // 必须输入
  gt: (value: number, error?: string) => IValidator // 必须大于
  gte: (value: number, error?: string) => IValidator // 必须大于等于
  lt: (value: number, error?: string) => IValidator // 必须小于于
  lte: (value: number, error?: string) => IValidator // 必须小于等于
  minLength: (size: number, error?: string) => IValidator // 不能小于X字符
  maxLength: (size: number, error?: string) => IValidator // 不能超过X字符(用于textArea校验)
  precision: (digitSize: number, error?: string) => IValidator // 校验数字最大小数位(精度限制)
  number: () => IValidator // 校验是否为数字
  int: () => IValidator // 校验是否为整数
  phone: () => IValidator // 校验是否为手机号(随着号段的增加,未来可能需要更新)
  email: () => IValidator // 校验是否为电子邮件地址
  verifyCode: () => IValidator // 校验6位数字验证码
  pattern: (regex: RegExp, error?: string) => IValidator // 校验正则格式
  compare: (otherVal: string, error?: string) => Validator // 校验两次输入一致
}

export class Validator extends Array implements IValidator {
  private labelText?: string = undefined

  constructor(label?: string) {
    super(0)
    this.labelText = label
    return this
  }

  required(error?: string): Validator {
    this.push({
      required: true,
      message: error ?? (this.labelText ? `请输入${this.labelText}` : '输入不能为空'),
      trigger: ['change', 'blur']
    })
    return this
  }

  gt(value: number, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return !val || val > value
      },
      message: error ?? `${this.labelText ?? '输入数值'}应大于 ${value}`,
      trigger: ['change', 'blur']
    })
    return this
  }

  gte(value: number, error?: string): Validator {
    this.push({
      type: 'number',
      min: value,
      transform: (v: string) => Number(v),
      message: error ?? `${this.labelText ?? '输入数值'}不能小于 ${value}`,
      trigger: ['change', 'blur']
    })
    return this
  }

  lt(value: number, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return !val || val < value
      },
      message: error ?? `${this.labelText ?? '输入数值'}应小于 ${value}`,
      trigger: ['change', 'blur']
    })
    return this
  }

  lte(value: number, error?: string): Validator {
    this.push({
      type: 'number',
      max: value,
      transform: (v: string) => Number(v),
      message: error ?? `${this.labelText ?? '输入数值'}不能大于 ${value}`,
      trigger: ['change', 'blur']
    })
    return this
  }

  minLength(size: number, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return (val?.length || 0) >= size
      },
      message: error ?? `${this.labelText ?? '内容'}长度最小为 ${size} 字符`,
      trigger: ['change', 'blur']
    })
    return this
  }

  maxLength(size: number, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return (val?.length || 0) <= size
      },
      message: error ?? `${this.labelText ?? '内容'}长度最多为 ${size} 字符`,
      trigger: ['change', 'blur']
    })
    return this
  }

  precision(digitSize: number, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        const decimal = val.toString().split('.')[1]
        return !!decimal && decimal.length < digitSize
      },
      message: error ?? `${this.labelText ?? ''}小数部分不能超过 ${digitSize} 位`,
      trigger: ['change', 'blur']
    })
    return this
  }

  number(): Validator {
    return this.pattern(/^([-+])?\d+(\.\d+)?$/, `${this.labelText ?? ''}请输入数字`)
  }

  int(): Validator {
    this.push({
      type: 'integer',
      transform: (v: string) => Number(v),
      message: '请输入整数',
      trigger: ['change', 'blur']
    })
    return this
  }

  phone(): Validator {
    return this.pattern(/^1\d{10}$/, '请输入正确手机号码')
  }

  email(): Validator {
    this.push({
      type: 'email',
      message: '邮箱格式不正确',
      trigger: ['change', 'blur']
    })
    return this
  }

  verifyCode(): Validator {
    return this.pattern(/^([-+])?\d+(\.\d+)?$/, '请输入验证码')
  }

  compare(otherVal: string, error?: string): Validator {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return !val || val === otherVal
      },
      message: error ?? `${this.labelText ?? ''}两次输入不一致`,
      trigger: ['change', 'blur']
    })
    return this
  }

  pattern(regex: RegExp, error?: string) {
    this.push({
      validator: (rule: any, val: any, callback: (error?: Error) => void) => {
        return !val || regex.test(val)
      },
      message: error ?? '请输入正确格式',
      trigger: ['change', 'blur']
    })
    return this
  }
}

export const validate: (label?: string) => IValidator = (label) => {
  return new Validator(label)
}

不到200行搞掂

小遗憾

与element-ui-verify相比,唯一有点小遗憾的这种方式还是基于配置,无法自动获取到label的信息了,不然定义完label后,还得重复写一次,精简的不够极致。而且也无法响应校验的配置变化,算一点小遗憾了,以后有空再继续改造

--------------------- [ 2024/06/05 新增 ] ---------------------

今天改造个vue2项目,也需要校验,偷个懒,让GPT翻译成了javascript,能用

/* eslint-disable no-unused-vars */
/**
 * 链式校验模块,支持常用校验方式
 *
 * 使用列:
 * import Validator from '@/commons/utils/validator'
 *
 * //建立一个非空4字符以上的昵称校验,未输入提示“请输入昵称”,过短提示“昵称长度最小为 4 字符”
 * new Validator('昵称').required().minLength(4)
 *
 * 注意除了非空校验外,其它校验都是为空可以通过的,除非显示的指定了非空校验required
 *
 * @author Jim 24/06/05
 */

export default class Validator extends Array {
    constructor(label) {
      super();
      this.labelText = label;
      return this;
    }
  
    required(error) {
      this.push({
        required: true,
        message: error ?? (this.labelText ? `请输入${this.labelText}` : '输入不能为空'),
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    gt(value, error) {
      this.push({
        validator: (rule, val, callback) => {
          return !val || val > value;
        },
        message: error ?? `${this.labelText ?? '输入数值'}应大于 ${value}`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    gte(value, error) {
      this.push({
        type: 'number',
        min: value,
        transform: (v) => Number(v),
        message: error ?? `${this.labelText ?? '输入数值'}不能小于 ${value}`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    lt(value, error) {
      this.push({
        validator: (rule, val, callback) => {
          return !val || val < value;
        },
        message: error ?? `${this.labelText ?? '输入数值'}应小于 ${value}`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    lte(value, error) {
      this.push({
        type: 'number',
        max: value,
        transform: (v) => Number(v),
        message: error ?? `${this.labelText ?? '输入数值'}不能大于 ${value}`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    minLength(size, error) {
      this.push({
        validator: (rule, val, callback) => {
          return (val?.length || 0) >= size;
        },
        message: error ?? `${this.labelText ?? '内容'}长度最小为 ${size} 字符`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    maxLength(size, error) {
      this.push({
        validator: (rule, val, callback) => {
          return (val?.length || 0) <= size;
        },
        message: error ?? `${this.labelText ?? '内容'}长度最多为 ${size} 字符`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    precision(digitSize, error) {
      this.push({
        validator: (rule, val, callback) => {
          const decimal = val.toString().split('.')[1];
          return !!decimal && decimal.length < digitSize;
        },
        message: error ?? `${this.labelText ?? ''}小数部分不能超过 ${digitSize} 位`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    number() {
      return this.pattern(/^([-+])?\d+(\.\d+)?$/, `${this.labelText ?? ''}请输入数字`);
    }
  
    int() {
      this.push({
        type: 'integer',
        transform: (v) => Number(v),
        message: '请输入整数',
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    phone() {
      return this.pattern(/^1\d{10}$/, '请输入正确手机号码');
    }
  
    email() {
      this.push({
        type: 'email',
        message: '邮箱格式不正确',
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    verifyCode() {
      return this.pattern(/^([-+])?\d+(\.\d+)?$/, '请输入验证码');
    }
  
    compare(otherVal, error) {
      this.push({
        validator: (rule, val, callback) => {
          return !val || val === otherVal;
        },
        message: error ?? `${this.labelText ?? ''}两次输入不一致`,
        trigger: ['change', 'blur']
      });
      return this;
    }
  
    pattern(regex, error) {
      this.push({
        validator: (rule, val, callback) => {
          return !val || regex.test(val);
        },
        message: error ?? '请输入正确格式',
        trigger: ['change', 'blur']
      });
      return this;
    }
  }
  
  const validate = (label) => {
    return new Validator(label);
  }
  
  export { validate };
  

关键代码: 


        <el-form-item

          label="批次名称"

          prop="batchName"

          :rules="new Validator('批次名称').required()"

        >

          <el-input

            v-model="form.batchName"

            placeholder="方便管理的名称,领用人/领用日期等"

            style="width: 300px;"

            maxlength="32"

          />

        </el-form-item>

....

import Validator from '@/utils/validator'

export default {

    data() {

        return {

          Validator

        }

    },

....

对于 Element Plus 表单校验,你可以通过使用内置的校验规则和自定义校验函数来实现。下面是一个简单的示例: 首先,确保你已经正确引入 Element Plus 的表单组件和样式。然后,在表单中使用 `el-form` 和 `el-form-item` 组件包裹你的表单项。 ```html <template> <el-form :model="form" ref="form" :rules="rules" label-width="120px"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username"></el-input> </el-form-item> <el-form-item label="密码" prop="password"> <el-input type="password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm">提交</el-button> </el-form-item> </el-form> </template> ``` 接下来,在你的 Vue 组件中定义表单的数据模型 (`form`) 和校验规则 (`rules`): ```javascript export default { data() { return { form: { username: '', password: '' }, rules: { username: [ { required: true, message: '请输入用户名', trigger: 'blur' } ], password: [ { required: true, message: '请输入密码', trigger: 'blur' }, { min: 6, max: 20, message: '密码长度在 6 到 20 个字符', trigger: 'blur' } ] } }; }, methods: { submitForm() { this.$refs.form.validate((valid) => { if (valid) { // 表单校验通过,可以进行提交操作 console.log('表单校验通过'); } else { // 表单校验不通过,显示错误信息 console.log('表单校验不通过'); } }); } } }; ``` 在上述代码中,我们定义了 `form` 对象来存储表单数据,并使用 `rules` 对象定义了校验规则。`el-form-item` 组件的 `prop` 属性对应了 `rules` 对象中的属性名。 最后,我们在 `submitForm` 方法中调用了 `this.$refs.form.validate` 来触发表单校验。如果校验通过,可以在回调函数中执行提交操作;如果校验不通过,可以在回调函数中处理错误信息。 这是一个简单的 Element Plus 表单校验的示例,你可以根据实际需求进行扩展和定制。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值