[完善]表单使用async-validator4.0.7实现校验功能

🐛[修复]下拉框点击自身不能隐藏和设置modelValue为空时不能置空下拉框
This commit is contained in:
xumi
2021-12-19 07:12:33 +08:00
parent 63d1e7538d
commit cd727c0ef5
9 changed files with 912 additions and 33 deletions

View File

@@ -0,0 +1,49 @@
import { ValidateMessages } from "async-validator";
// 中文翻译 --> 根据 async-validator 中 ValidateMessages 进行翻译
export default {
default: "%s验证失败",
required: "%s不能为空",
enum: "%s不在枚举%s里面",
whitespace: "%s不能为空",
date: {
format: "%s日期%s不是一个有效格式的日期%s",
parse: "%s无法解析为日期,%s是无效的",
invalid: "%s日期%s是无效的"
},
types: {
number: '%s不是一个有效的数字',
boolean: '%s不是一个有效的布尔类型',
method: '%s不是一个有效的方法',
regexp: '%s不是一个有效的正则表达式',
integer: '%s不是一个有效的整型数字',
float: '%s不是一个有效的浮点小数',
array: '%s不是一个有效的数组',
object: '%s不是一个有效的对象',
enum: '%s不是一个有效的枚举',
date: '%s不是一个有效的日期',
url: '%s不是一个有效的url',
hex: '%s不是一个有效的十六进制',
email: '%s不是一个有效的邮箱'
},
string: {
len: "%s必须是长度为%s个字符",
min: "%s最小长度为%s个字符",
max: "%s最长%s个字符",
range: "%s字符长度需要在%s和%s直接"
},
number: {
len: "%s长度必须为%s",
min: "%s必须小于%s",
max: "%s必须大于%s",
range: "%s需要在%s和%s之间"
},
array: {
len: "%s长度必须为%s",
min: "%s长度必须小于%s",
max: "%s长度必须大于%s",
range: "%s长度需要在%s和%s之间"
},
pattern: {
"mismatch": "%s值%s不能匹配%s"
}
} as ValidateMessages;

View File

@@ -0,0 +1,47 @@
@error_color : red;
.layui-required{
color: @error_color;
font-size: 12px;
line-height: 1;
}
.layui-form .layui-form-item{
.layui-input-block
,.layui-input-inline{
.layui-form-danger {
border-color: #ff5722 !important;
}
}
}
.layui-error-message {
color: @error_color;
font-size: 12px;
line-height: 1;
padding-top: 2px;
position: absolute;
top: 100%;
left: 0;
}
.layui-error-message-anim {
-ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-animation: layui-top-show-anim 0.3s ease 1;
animation: layui-top-show-anim 0.3s ease 1;
}
@keyframes layui-top-show-anim {
0% {
opacity: 0.3;
transform: rotateX(45deg);
}
100% {
opacity: 1;
transform: rotateX(0);
}
}

View File

@@ -1,21 +1,140 @@
<template>
<div class="layui-form-item">
<label class="layui-form-label">{{ label }}</label>
<div class="layui-form-item" ref="formItemRef">
<label class="layui-form-label">
<span v-if="props.prop &&isRequired" :class="['layui-required', 'layui-icon'].concat(layForm.requiredIcons??'')">
<slot name="required" :props="{...props, model: layForm.model}">{{layForm.requiredIcons? '' : '*'}}</slot>
</span>
<slot name="label" :props="{...props, model: layForm.model}">
{{ label }}
</slot>
</label>
<div :class="[mode ? 'layui-input-' + mode : '']">
<slot />
<div ref="slotParent">
<slot :props="{...props, model: layForm.model}"/>
</div>
<span v-if="errorStatus" :class="['layui-error-message', {'layui-error-message-anim': errorStatus}]">{{errorMsg}}</span>
</div>
</div>
</template>
<script setup name="LayFormItem" lang="ts">
import { defineProps, withDefaults } from 'vue'
import "./index.less";
import { defineProps, inject, withDefaults, ref, reactive, toRefs, onMounted, computed, watch} from 'vue'
import {layFormKey, LayFormContext, LayFormItemContext, FieldValidateError} from "../type/form"
import Schema, { Rule, RuleItem, Rules, ValidateCallback, ValidateError, ValidateMessages} from 'async-validator';
import cnValidateMessage from './cnValidateMessage';
const props = withDefaults(
defineProps<{
prop?: string
mode?: string
label?: string
errorMessage?: string
rules?: Rule
required?: boolean
}>(),
{
mode: 'block'
}
)
const layForm = inject(layFormKey, {} as LayFormContext)
const formItemRef = ref<HTMLDivElement>()
const slotParent = ref<HTMLDivElement>()
// 是否必填
const isRequired = computed(()=>{
return props.required || layForm.required;
})
// 拼接校验规则
const ruleItems = computed(()=>{
const prop = props.prop;
if (!prop) {
return {};
}
let rulesArrs : RuleItem[] = [];
if (isRequired.value) {
rulesArrs.push({required: true});
}
if (props.rules) {
rulesArrs = rulesArrs.concat((props.rules as RuleItem | RuleItem[]));
}
if (layForm.rules && layForm.rules[prop]) {
rulesArrs = rulesArrs.concat((layForm.rules[prop] as RuleItem | RuleItem[]));
}
return rulesArrs;
});
// 值 计算 和 监听
const filedValue = computed(()=> props.prop ? layForm.model[props.prop] : undefined);
watch(()=>filedValue.value, (val)=> validate());
// 错误状态和信息
const errorStatus = ref(false);
const errorMsg = ref();
// 校验数据有效性
const validate = (callback ?: ValidateCallback)=> {
if (props.prop && (ruleItems.value as RuleItem[]).length > 0) {
// 校验规则
const descriptor : Rules = {};
descriptor[layForm.useCN? (props.label||props.prop ): props.prop] = ruleItems.value;
const validator = new Schema(descriptor);
let model : {[key : string]:any} = {};
let validateMessage = null;
// 使用中文错误提示
if (layForm.useCN) {
validateMessage = Object.assign({}, cnValidateMessage, layForm.validateMessage);
model[props.label||props.prop] = filedValue.value;
} else {
layForm.validateMessage && (validateMessage = layForm.validateMessage);
model[props.prop] = filedValue.value;
}
// 自定义校验消息
layForm.requiredErrorMessage && (validateMessage = Object.assign(validateMessage, {required : layForm.requiredErrorMessage}));
validateMessage && validator.messages(validateMessage);
// 开始校验
validator.validate(model, (errors, fields) => {
errorStatus.value = errors !== null && errors.length > 0;
const slotParentDiv = slotParent.value as HTMLDivElement;
if (errorStatus.value) {
const _errors = (errors as FieldValidateError[]);
// 如果是中文,将错误信息转换成FieldValidateError类型
layForm.useCN && _errors.forEach(error => {
error.label = props.label;
error.field = props.prop;
})
errorMsg.value = props.errorMessage??_errors[0].message;
slotParentDiv.childElementCount > 0 && slotParentDiv.firstElementChild?.classList.add('layui-form-danger');
callback && callback(_errors, fields);
} else {
clearValidate();
}
});
}
}
// 清除校验
const clearValidate = ()=> {
errorStatus.value = false;
errorMsg.value = '';
const slotParentDiv = slotParent.value as HTMLDivElement;
slotParentDiv.childElementCount > 0 && slotParentDiv.firstElementChild?.classList.remove('layui-form-danger');
}
defineExpose({validate, clearValidate});
onMounted(()=>{
if (props.prop) {
layForm.addField(reactive({
...toRefs(props),
$el: formItemRef,
validate,
clearValidate
}) as LayFormItemContext);
}
})
</script>