export type ValidationKey = 'email' | 'phone' | 'required' | 'range'
interface RuleProp {
type: ValidationKey;
message?: string;
min?: { message: string, length:number};
max?: { message: string, length:number};
}
interface ResObj {
isValid: boolean;
message: string
}
type Validation = Record<ValidationKey, (param: string, rule: Array<RuleProp>) => ResObj>
// Record是ts中高级类型,可以理解为,定义了一系列的对象,对象的属性值为validtaionKey的值,而属性值则是第二个参数决定的,它可以是对象,联合类型,枚举,函数等等,在这个案例中,他是一个函数,
// 函数有一个参数值是字符串类型,该函数会返回一个布尔值
// 邮箱的正则
const emailReg = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
const phoneReg = /^(?:\+?86)?1(?:3\d{3}|5[^4\D]\d{2}|8\d{3}|7(?:[0-35-9]\d{2}|4(?:0\d|1[0-2]|9\d))|9[0-35-9]\d{2}|6[2567]\d{2}|4(?:(?:10|4[01])\d{3}|[68]\d{4}|[579]\d{2}))\d{6}$/
// 这个地方他导出的就是一个对象,属性为可以,属性值为方法体
export const Validations:Validation = {
email (str, rule) {
const result = { isValid: true, message: '' }
for (let i = 0; i < rule.length; i++) {
if (rule[i].type === 'email') {
result.isValid = emailReg.test(str)
result.message = rule[i].message as string
break
}
}
return result
},
phone (str, rule) {
const result = { isValid: true, message: '' }
for (let i = 0; i < rule.length; i++) {
if (rule[i].type === 'required') {
result.isValid = phoneReg.test(str)
result.message = rule[i].message as string
break
}
}
return result
},
required (str, rule) {
const result = { isValid: true, message: '' }
for (let i = 0; i < rule.length; i++) {
if (rule[i].type === 'required') {
result.isValid = !!str.trim()
result.message = rule[i].message as string
break
}
}
return result
},
range (str: string, rule) {
const newstr = str.trim()
const result = { isValid: true, message: '' }
for (let i = 0; i < rule.length; i++) {
if (rule[i].type === 'range') {
if (rule[i]?.min) {
const nummin = rule[i]?.min?.length as number
if (newstr.length < nummin) {
result.isValid = false
result.message = rule[i]?.min?.message as string
break
}
}
if (rule[i]?.max) {
const nummax = rule[i]?.max?.length as number
if (newstr.length > nummax) {
result.isValid = false
result.message = rule[i]?.max?.message as string
break
}
}
}
}
return result
}
}
老师,上面是我将校验规则单独抽离的ts文件,增加了range校验规则
在ValidateInput.vue中,如下定义
<template>
<div class="validata-input-container pb-3">
<input
v-bind="$attrs"
:class="{'is-invalid': inputRef.error}"
class="form-control"
id="exampleInputEmail1"
:value="inputRef.val"
@input="updateValue"
@blur="validateInput">
<span v-if="inputRef.error" class="invalid-feedback">{{inputRef.message}}</span>
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, renderSlot } from 'vue'
import { ValidationKey, Validations } from '../hooks/validation'
import { emitter } from './validateForm.vue'
interface RuleProp {
// ValidationKey是一种类型,可以导入导出使用,那么这里的type就限定了3中,只能是邮箱,手机和必填
type: ValidationKey;
message?: string;
min?: { message: string, length:number};
max?: { message: string, length:number};
}
// export type RulesProp = RuleProp[],这句话的意思是type是类型别名,这句话的意思就是,创建一个类型,这个类型是个数组类型,每个数组成员是RuleProp类型的数据
// export 只是一个关键字,在ts中,类型可以导出和导入,代码实现也可以导出和到入
export type RulesProp = RuleProp[]
export default defineComponent({
props: {
rules: Array as PropType<RulesProp>,
modelValue: String
},
inheritAttrs: false,
setup (props, context) {
// 需要判断用户有没有传入默认的初始值,如果传入了需要显示在页面上
const inputRef = reactive({
val: props.modelValue || '',
error: false,
message: ''
})
const updateValue = (e: KeyboardEvent) => {
// 修改组件的值,实现双向数据绑定
const targetValue = (e.target as HTMLInputElement).value
inputRef.val = targetValue
context.emit('update:modelValue', targetValue)
}
const validateInput = () => {
// 首先判读是否有rules规则
if (props.rules) {
// 判断表单所有的是否都通过校验
for (const rule of props.rules) {
const resultObj = Validations[rule.type](inputRef.val, props.rules)
if (!resultObj.isValid) {
inputRef.error = true
inputRef.message = resultObj.message
return false
}
}
inputRef.error = false
}
}
onMounted(() => {
emitter.emit('form-item-created', inputRef.val)
})
return {
inputRef,
updateValue,
validateInput
}
}
})
</script>
我传入的规则校验是
// 密码的校验规则
const passwordRules: RulesProp = [
{ type: 'required', message: '密码不能为空' },
{ type: 'range', min: { message: '你的密码至少包括六位,不能含有空格', length: 6 }, max: { message: '你的密码至多包括10位,不能含有空格', length: 10 } }
]
但是这样会有一个问题,当密码的校验规则既有min也有max的时候,按照我的写法,他永远只会提示,当前密码长度不能小于6个,当超过10个的时候也是提示这个,因为我在动态判断inputRef.message的时候,用的是三元表达式,这样肯定不对的,我需要一个标识,来判断当前到底是min没有通过,还是max没有通过
我的想法是,range方法返回一个对象类似于{ type :‘min’, flag: true},然后在失焦方法触发的时候,去判断这个类型是啥,然后动态设置inputRef.message的信息,但是会遇到问题,希望老师提供一个解决思路
第二个问题,定义rules类型的时候,因为message变成了一个可选参数,inputRef.message = rule.message ? rule.message : ‘’,这里如果直接赋值会报错,rules上不存在message属性,所有这里我使用了三元表达式,不知道这样写合不合乎规范