✨[完善]表单使用async-validator4.0.7实现校验功能
🐛[修复]下拉框点击自身不能隐藏和设置modelValue为空时不能置空下拉框
			
			
This commit is contained in:
		
							parent
							
								
									63d1e7538d
								
							
						
					
					
						commit
						cd727c0ef5
					
				@ -4,37 +4,62 @@
 | 
			
		||||
::: demo
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <lay-form @submit="submit" :model="model">
 | 
			
		||||
    <lay-form-item label="账户">
 | 
			
		||||
  <lay-form :model="model">
 | 
			
		||||
    <lay-form-item label="账户" prop="username">
 | 
			
		||||
      <lay-input v-model="model.username"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="密码">
 | 
			
		||||
      <lay-input v-model="model.password"></lay-input>
 | 
			
		||||
    <lay-form-item label="密码" prop="password">
 | 
			
		||||
      <lay-input v-model="model.password" type="password"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="爱好" prop="hobby">
 | 
			
		||||
      <lay-select v-model="model.hobby">
 | 
			
		||||
        <lay-select-option value="1" label="学习"></lay-select-option>
 | 
			
		||||
        <lay-select-option value="2" label="编码"></lay-select-option>
 | 
			
		||||
        <lay-select-option value="3" label="运动"></lay-select-option>
 | 
			
		||||
      </lay-select>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="特长" prop="specialty">
 | 
			
		||||
      <lay-radio v-model="model.specialty" name="specialty" label="1">写作</lay-radio>
 | 
			
		||||
      <lay-radio v-model="model.specialty" name="specialty" label="2">画画</lay-radio>
 | 
			
		||||
      <lay-radio v-model="model.specialty" name="specialty" label="3">编码</lay-radio>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="描述" prop="desc">
 | 
			
		||||
      <lay-textarea placeholder="请输入描述" v-model="model.desc"></lay-textarea>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item>
 | 
			
		||||
      <lay-button naive-type="submit">提交</lay-button>
 | 
			
		||||
      <lay-button @click="submitClick">提交</lay-button>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
  </lay-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ref, reactive } from 'vue'
 | 
			
		||||
import {layer} from '@layui/layer-vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setup() {
 | 
			
		||||
 | 
			
		||||
    const model = reactive({
 | 
			
		||||
        username: "admin",
 | 
			
		||||
        password: "admin"
 | 
			
		||||
        password: "123456",
 | 
			
		||||
        specialty: "1"
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const submit = function(val) {
 | 
			
		||||
      alert(JSON.stringify(val))
 | 
			
		||||
    }
 | 
			
		||||
    const submitClick = function(){
 | 
			
		||||
      layer.open({
 | 
			
		||||
        type: 1,
 | 
			
		||||
        title:"表单结果", 
 | 
			
		||||
        content: `<div style="padding: 10px">${JSON.stringify(model)}</div>`, 
 | 
			
		||||
        shade: false,
 | 
			
		||||
        isHtmlFragment: true,
 | 
			
		||||
        btn : [{ text: '确认', callback(index) {  layer.close(index) }}],
 | 
			
		||||
        area : '500px'
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      model,
 | 
			
		||||
      submit
 | 
			
		||||
      submitClick
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -42,38 +67,515 @@ export default {
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单事件
 | 
			
		||||
::: title 表单基本校验功能
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: demo
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <lay-form :model="validateModel" ref="layFormRef" required>
 | 
			
		||||
    <lay-form-item label="账户" prop="username">
 | 
			
		||||
      <lay-input v-model="validateModel.username"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="密码" prop="password">
 | 
			
		||||
      <lay-input v-model="validateModel.password" type="password">></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="爱好" prop="hobby">
 | 
			
		||||
      <lay-select v-model="validateModel.hobby">
 | 
			
		||||
        <lay-select-option value="1" label="学习"></lay-select-option>
 | 
			
		||||
        <lay-select-option value="2" label="编码"></lay-select-option>
 | 
			
		||||
        <lay-select-option value="3" label="运动"></lay-select-option>
 | 
			
		||||
      </lay-select>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="特长" prop="specialty">
 | 
			
		||||
      <lay-radio v-model="validateModel.specialty" name="specialty" label="1">写作</lay-radio>
 | 
			
		||||
      <lay-radio v-model="validateModel.specialty" name="specialty" label="2">画画</lay-radio>
 | 
			
		||||
      <lay-radio v-model="validateModel.specialty" name="specialty" label="3">编码</lay-radio>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="描述" prop="desc">
 | 
			
		||||
      <lay-textarea placeholder="请输入描述" v-model="validateModel.desc"></lay-textarea>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item>
 | 
			
		||||
      <lay-button @click="validate">提交</lay-button>
 | 
			
		||||
      <lay-button @click="clearValidate">清除校验</lay-button>
 | 
			
		||||
      <lay-button @click="reset">重置表单</lay-button>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
  </lay-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ref, reactive } from 'vue'
 | 
			
		||||
import {layer} from '@layui/layer-vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setup() {
 | 
			
		||||
 | 
			
		||||
    const validateModel = reactive({
 | 
			
		||||
        username: "",
 | 
			
		||||
        password: "",
 | 
			
		||||
        specialty: "1"
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const layFormRef = ref(null);
 | 
			
		||||
    // 校验
 | 
			
		||||
    const validate = function() {
 | 
			
		||||
      layFormRef.value.validate((isValidate, model, errors) => {
 | 
			
		||||
         layer.open({
 | 
			
		||||
          type: 1,
 | 
			
		||||
          title:"表单提交结果", 
 | 
			
		||||
          content: `<div style="padding: 10px"><p>是否通过 : ${isValidate}</p> <p>表单数据 : ${JSON.stringify(model)} </p> <p>错误信息 : ${JSON.stringify(errors)}</p></div>`, 
 | 
			
		||||
          shade: false,
 | 
			
		||||
          isHtmlFragment: true,
 | 
			
		||||
          btn : [{ text: '确认', callback(index) {  layer.close(index) }}],
 | 
			
		||||
          area : '500px'
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 清除校验
 | 
			
		||||
    const clearValidate = function() {
 | 
			
		||||
      layFormRef.value.clearValidate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 重置表单
 | 
			
		||||
    const reset = function() {
 | 
			
		||||
      layFormRef.value.reset();
 | 
			
		||||
      validateModel.specialty = "1"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      validateModel,
 | 
			
		||||
      layFormRef,
 | 
			
		||||
      validate,
 | 
			
		||||
      clearValidate,
 | 
			
		||||
      reset
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 校验规则 - 通过表单配置
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: demo
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <lay-form :model="ruleDemo1" ref="layFormRef1" :rules="rules" required initValidate>
 | 
			
		||||
    <lay-form-item label="邮箱" prop="email">
 | 
			
		||||
      <lay-input v-model="ruleDemo1.email"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="url路径" prop="url">
 | 
			
		||||
      <lay-input v-model="ruleDemo1.url"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="日期" prop="date">
 | 
			
		||||
      <lay-input v-model="ruleDemo1.date"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="用户名" prop="username">
 | 
			
		||||
      <lay-input v-model="ruleDemo1.username"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="年龄" prop="age">
 | 
			
		||||
      <lay-input v-model="ruleDemo1.age"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item>
 | 
			
		||||
      <lay-button @click="validate1">提交</lay-button>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
  </lay-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ref, reactive } from 'vue'
 | 
			
		||||
import {layer} from '@layui/layer-vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setup() {
 | 
			
		||||
 | 
			
		||||
    const ruleDemo1 = reactive({
 | 
			
		||||
        email: "",
 | 
			
		||||
        url: "",
 | 
			
		||||
        date: "",
 | 
			
		||||
        username: "",
 | 
			
		||||
        age: null,
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const rules = ref({
 | 
			
		||||
      email : {
 | 
			
		||||
        type : 'email'
 | 
			
		||||
      },
 | 
			
		||||
      url : {
 | 
			
		||||
        type : 'url'
 | 
			
		||||
      },
 | 
			
		||||
      date : {
 | 
			
		||||
        type : 'date'
 | 
			
		||||
      },
 | 
			
		||||
      username : {
 | 
			
		||||
        type :  'string',
 | 
			
		||||
        min : 8,
 | 
			
		||||
        max : 16
 | 
			
		||||
      },
 | 
			
		||||
      age : {
 | 
			
		||||
        validator(rule, value, callback, source, options){
 | 
			
		||||
          if (value < 18) {
 | 
			
		||||
            callback(new Error(`${rule.field}太过于年轻`));
 | 
			
		||||
          } else {
 | 
			
		||||
            return true;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const layFormRef1 = ref(null);
 | 
			
		||||
    // 校验
 | 
			
		||||
    const validate1 = function() {
 | 
			
		||||
      layFormRef1.value.validate((isValidate, model, errors) => {
 | 
			
		||||
         layer.open({
 | 
			
		||||
          type: 1,
 | 
			
		||||
          title:"表单提交结果", 
 | 
			
		||||
          content: `<div style="padding: 10px"><p>是否通过 : ${isValidate}</p> <p>表单数据 : ${JSON.stringify(model)} </p> <p>错误信息 : ${JSON.stringify(errors)}</p></div>`, 
 | 
			
		||||
          shade: false,
 | 
			
		||||
          isHtmlFragment: true,
 | 
			
		||||
          btn : [{ text: '确认', callback(index) {  layer.close(index) }}],
 | 
			
		||||
          area : '500px'
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      ruleDemo1,
 | 
			
		||||
      layFormRef1,
 | 
			
		||||
      validate1,
 | 
			
		||||
      rules
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 校验规则 - 通过表单子项配置
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: demo
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <lay-form :model="ruleDemo2" ref="layFormRef2" required initValidate>
 | 
			
		||||
    <lay-form-item label="邮箱" prop="email" :rules="{type : 'email'}">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.email"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="url路径" prop="url" :rules="{type : 'url'}">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.url"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="日期" prop="date" :rules="{type : 'date'}">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.date"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="用户名" prop="username" 
 | 
			
		||||
      :rules="{
 | 
			
		||||
        type :  'string',
 | 
			
		||||
        min : 8,
 | 
			
		||||
        max : 16
 | 
			
		||||
      }">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.username"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="年龄" prop="age" :rules="ageRules">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.age"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="自定义提示" prop="myEmail" 
 | 
			
		||||
    :rules="{
 | 
			
		||||
      type : 'email',
 | 
			
		||||
      message : '必须为邮箱'
 | 
			
		||||
    }">
 | 
			
		||||
      <lay-input v-model="ruleDemo2.myEmail"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item>
 | 
			
		||||
      <lay-button @click="validate2">提交</lay-button>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
  </lay-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ref, reactive } from 'vue'
 | 
			
		||||
import {layer} from '@layui/layer-vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setup() {
 | 
			
		||||
 | 
			
		||||
    const ruleDemo2 = reactive({
 | 
			
		||||
        email: "",
 | 
			
		||||
        url: "",
 | 
			
		||||
        date: "",
 | 
			
		||||
        username: "",
 | 
			
		||||
        age: null,
 | 
			
		||||
        myEmail: "",
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const ageRules = {
 | 
			
		||||
      validator(rule, value, callback, source, options){
 | 
			
		||||
        if (value < 18) {
 | 
			
		||||
          callback(new Error(`${rule.field}太过于年轻`));
 | 
			
		||||
          return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const layFormRef2 = ref(null);
 | 
			
		||||
    // 校验
 | 
			
		||||
    const validate2 = function() {
 | 
			
		||||
      layFormRef2.value.validate((isValidate, model, errors) => {
 | 
			
		||||
         layer.open({
 | 
			
		||||
          type: 1,
 | 
			
		||||
          title:"表单提交结果", 
 | 
			
		||||
          content: `<div style="padding: 10px"><p>是否通过 : ${isValidate}</p> <p>表单数据 : ${JSON.stringify(model)} </p> <p>错误信息 : ${JSON.stringify(errors)}</p></div>`, 
 | 
			
		||||
          shade: false,
 | 
			
		||||
          isHtmlFragment: true,
 | 
			
		||||
          btn : [{ text: '确认', callback(index) {  layer.close(index) }}],
 | 
			
		||||
          area : '500px'
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      ruleDemo2,
 | 
			
		||||
      layFormRef2,
 | 
			
		||||
      validate2,
 | 
			
		||||
      ageRules
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 原生submit方式校验 -- 校验通过将提交表单 -- 不推荐使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: demo
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <lay-form @submit="submit" :model="submitModel" requiredIcons="layui-icon-heart-fill" :use-CN="false" required>
 | 
			
		||||
    <lay-form-item label="账户" prop="username">
 | 
			
		||||
    <template #label>
 | 
			
		||||
      <i class="layui-icon layui-icon-username"></i>
 | 
			
		||||
      账户
 | 
			
		||||
    </template>
 | 
			
		||||
      <lay-input v-model="submitModel.username"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item label="密码" prop="password">
 | 
			
		||||
      <lay-input v-model="submitModel.password"></lay-input>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
    <lay-form-item>
 | 
			
		||||
      <lay-button native-type="submit">提交</lay-button>
 | 
			
		||||
    </lay-form-item>
 | 
			
		||||
  </lay-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script>
 | 
			
		||||
import { ref, reactive } from 'vue'
 | 
			
		||||
import {layer} from '@layui/layer-vue'
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  setup() {
 | 
			
		||||
 | 
			
		||||
    const submitModel = reactive({
 | 
			
		||||
        username: "",
 | 
			
		||||
        password: ""
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    const submit = function(isValidate, model, errors) {
 | 
			
		||||
      layer.open({
 | 
			
		||||
        type: 1,
 | 
			
		||||
        title:"表单提交结果", 
 | 
			
		||||
        content: `<div style="padding: 10px"><p>是否通过 : ${isValidate}</p> <p>表单数据 : ${JSON.stringify(model)} </p> <p>错误信息 : </br>${JSON.stringify(errors)}</p></div>`, 
 | 
			
		||||
        shade: false,
 | 
			
		||||
        isHtmlFragment: true,
 | 
			
		||||
        btn : [{ text: '确认', callback(index) {  layer.close(index) }}],
 | 
			
		||||
        area : '500px'
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      submitModel
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单(form)属性
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
| Name  | Description | Accepted Values |
 | 
			
		||||
| ----- | ----------- | --------------- |
 | 
			
		||||
| model | 表单绑定值  | --              |
 | 
			
		||||
| 属性          | 描述                                                            |   类型    |     可选值     | 默认值   |
 | 
			
		||||
| ------------- | -------------------------------------------------------------- | --------- | -------------- | ------- |
 | 
			
		||||
| model         | 表单绑定值                                                      | `object`  |        -       |    {}   |
 | 
			
		||||
| required      | 是否必填                                                        | `boolean` | `true` `false` | `false` |
 | 
			
		||||
| rules         | 表单校验规则; <br>可查看[async-validator](https://github.com/yiminghe/async-validator)  | `object`  |       -        |     -   |
 | 
			
		||||
| initValidate  | 是否一开始就校验表单                                             | `boolean` | `true` `false` | `false` |
 | 
			
		||||
| useCN         | 是否使用中文错误提示                                             | `boolean` | `true` `false` | `false` |
 | 
			
		||||
| requiredIcons | 必填前缀图标`class`                                             | `string`  |         -      | `*` |
 | 
			
		||||
| required-erroer-message | 必填错误提示信息                                    | `string`  |         -     | `%s不能为空`|
 | 
			
		||||
| validate-message | 自定义校验错误提示信息; <br>由于内置了中文错误提示,可按需求增量增加<br>可参考 [layui-vue 内置中文错误提示](https://gitee.com/layui-vue/layui-vue/tree/master/src/module/formItem/cnValidateMessage.ts)   | `string`  |         -     | `%s不能为空`|
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单事件
 | 
			
		||||
::: title 表单(form)事件
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
| Name   | Description | Accepted Values |
 | 
			
		||||
| ------ | ----------- | --------------- |
 | 
			
		||||
| submit | 提交事件    | --              |
 | 
			
		||||
|       属性     | 描述                            |     回调参数   |
 | 
			
		||||
| ------------- | ------------------------------- | -------------- |
 | 
			
		||||
| submit        | 提交事件`(不推荐使用)`              | (`isValidate`, `model`, `errors`)<br><br> `isValidate`: (`boolean`)是否校验通过<br><br> `model`: (`object`)表单绑定的值<br><br> `errors`: (`Array`)校验结果的错误信息 |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单项属性
 | 
			
		||||
::: title 表单(form)方法
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
| Name  | Description | Accepted Values |
 | 
			
		||||
| ----- | ----------- | --------------- |
 | 
			
		||||
| label | 标题名称    | --              |
 | 
			
		||||
|       属性     | 描述           |     入参       |
 | 
			
		||||
| ------------- | -------------- | -------------- |
 | 
			
		||||
| validate      | 表单校验; <br>如果没有`callback`回调,会返回`Promise`,  <br> `Promise`参数为{`isValidate`, `model`, `errors`}<br> 参数具体描述请看上面表单`submit`提交事件   | (`fields` `[可选]`, `callback` `[可选]`)<br><br> `fields`: (`string` / `string[]` / `function`)<br>单独校验的字段,<br>该字段如果为`function`, <br>则认为`callback`入参,校验全部字段;<br><br> `callback`: (`function`)校验之后的回调,<br>回调参数为(`isValidate`, `model`, `errors`);<br>参数具体描述请看上面表单`submit`提交事件|
 | 
			
		||||
| clearValidate  | 清除表单校验  | (`fields`[可选])<br><br> `fields`: (`string` / `string[]`)<br>需要进行清除校验的表单字段, 如果该字段为空则清除全部校验|
 | 
			
		||||
| reset          | 重置表单所有值 |  -  |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单项(form-item)属性
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
| 属性          | 描述                                                            |   类型    |     可选值     | 默认值   |
 | 
			
		||||
| ------------- | -------------------------------------------------------------- | --------- | -------------- | ------- |
 | 
			
		||||
| prop          | 在表单绑定值(`model`)中字段`key`                                 | `string`  |        -       |    -    |
 | 
			
		||||
| label         | 子项前边描述值,**尽量填写**,中文校验错误需要用到                 | `string`  |        -       |    -    |
 | 
			
		||||
| required      | 是否必填                                                        | `boolean` | `true` `false` | `false` |
 | 
			
		||||
| rules         |  表单校验规则; <br>可查看[async-validator](https://github.com/yiminghe/async-validator)  | `object` | - | - |
 | 
			
		||||
| error-message | 表单校验失败固定提示语                                           | `string`  |`block` `inline`| `block` |
 | 
			
		||||
| mode          | 表单项显示的模式,`块元素` / `行元素`                             | `string`  |`block` `inline`| `block` |
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单项(form-item)方法
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
|       属性     | 描述           |     入参       |
 | 
			
		||||
| ------------- | -------------- | -------------- |
 | 
			
		||||
| validate      | 表单校验; | (`callback` `[可选]`)<br><br> `callback`: (`function`)校验之后的回调,<br>回调参数为(`errors`, `fields`);<br><br> `errors`: (`Array`)校验结果的错误信息;<br><br> `fields`: (`Array`)当前校验的字段信息|
 | 
			
		||||
| clearValidate  | 清除表单校验   |      -         |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 表单项(form-item)插槽
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
 | 
			
		||||
|       属性    |                                    描述                           |                  可使用参数             |
 | 
			
		||||
| ------------- | ---------------------------------------------------------------- | --------------------------------------- |
 | 
			
		||||
|       -       | 默认插槽                                                          | 传递进来的`props`和表单绑定的值(`model`)  |
 | 
			
		||||
|     label     | 子项前边描述插槽<br>如果使用此插槽,`props`**尽量**也传递`label`参数 | 传递进来的`props`和表单绑定的值(`model`)  |
 | 
			
		||||
|    required   | 必填前缀插槽                                                      | `*` / `表单props` 中的 `requiredIcons`   |
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: title 关于 async-validator 的使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
查看: https://github.com/yiminghe/async-validator  
 | 
			
		||||
中文翻译: https://www.cnblogs.com/wozho/p/10955525.html
 | 
			
		||||
 | 
			
		||||
::: title async-validator 基本校验类型
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: table
 | 
			
		||||
|       属性   |         描述       |        使用方式     |
 | 
			
		||||
| -------------| ----------------- | ------------------- |
 | 
			
		||||
|     number   | 数字              | `{type : 'number'}`  |
 | 
			
		||||
|     boolean  | 布尔类型          | `{type : 'boolean'}` |
 | 
			
		||||
|     method   | 方法              | `{type : 'method'}`  |
 | 
			
		||||
|     regexp   | 正则表达式        | `{type : 'regexp'}`  |
 | 
			
		||||
|     integer  | 整型数字          | `{type : 'integer'}` |
 | 
			
		||||
|     float    | 浮点小数          | `{type : 'float'}` |
 | 
			
		||||
|     array    | 数组              | `{type : 'array'}` |
 | 
			
		||||
|     object   | 对象              | `{type : 'object'}` |
 | 
			
		||||
|     enum     | 枚举              | `{type : 'enum'}` |
 | 
			
		||||
|     date     | 日期              | `{type : 'date'}` |
 | 
			
		||||
|     url      | url               | `{type : 'url'}` |
 | 
			
		||||
|     hex      | 十六进制          | `{type : 'hex'}` |
 | 
			
		||||
|     email    | 邮箱              | `{type : 'email'}` |
 | 
			
		||||
:::  
 | 
			
		||||
 | 
			
		||||
::: title async-validator 中 validator参数使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
{
 | 
			
		||||
  validator: (rule, value) => value === 'root'
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
::: title async-validator 中 asyncValidator参数使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
{
 | 
			
		||||
  asyncValidator: (rule, value) => {
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
      if (value < 18) {
 | 
			
		||||
        reject('too young');  // reject with error message
 | 
			
		||||
      } else {
 | 
			
		||||
        resolve();
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
::: title form 配置 async-validator 的使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
{
 | 
			
		||||
    key : rule // key为表单子项需要校验的对应key名称, rule为校验规则,格式为{}
 | 
			
		||||
}
 | 
			
		||||
// 例如 表单绑定值为 {email : 'xxx', username: 'xxxxx'}
 | 
			
		||||
// 需要校验邮箱,而邮箱输入框在表单绑定的值中key为email,
 | 
			
		||||
// 需要校验用户名长度为8到16之间,而用户名输入框在表单绑定的值中key为username,
 | 
			
		||||
{
 | 
			
		||||
  email : {
 | 
			
		||||
    type :  'email'
 | 
			
		||||
  },
 | 
			
		||||
  username : {
 | 
			
		||||
    type :  'string',
 | 
			
		||||
    min : 8,
 | 
			
		||||
    max : 16
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
::: title form-item 配置 async-validator 的使用
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
// 例如 表单绑定值为 {email : 'xxx', phone: 'xxxxx'}
 | 
			
		||||
// 需要校验邮箱,而邮箱输入框在表单绑定的值中key为email
 | 
			
		||||
{
 | 
			
		||||
  type :  'email'
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
::: comment
 | 
			
		||||
:::
 | 
			
		||||
@ -38,6 +38,7 @@
 | 
			
		||||
    "@layui/hooks-vue": "^0.1.6",
 | 
			
		||||
    "@layui/icons-vue": "^1.0.1",
 | 
			
		||||
    "@layui/layer-vue": "^1.2.0",
 | 
			
		||||
    "async-validator": "^4.0.7",
 | 
			
		||||
    "evtd": "^0.2.3",
 | 
			
		||||
    "vue": "^3.2.26",
 | 
			
		||||
    "vue-router": "^4.0.12"
 | 
			
		||||
 | 
			
		||||
@ -1,25 +1,140 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <form class="layui-form" @submit="submit">
 | 
			
		||||
  <form class="layui-form" :onsubmit="submit">
 | 
			
		||||
    <slot />
 | 
			
		||||
  </form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup name="LayForm" lang="ts">
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
export default{
 | 
			
		||||
  name: 'LayForm'
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { toRefs, provide,reactive, onMounted } from "vue"
 | 
			
		||||
import { Rule, ValidateError, ValidateMessages } from "async-validator"
 | 
			
		||||
import {layFormKey, LayFormItemContext, FormCallback, modelType} from "../type/form"
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(
 | 
			
		||||
  defineProps<{
 | 
			
		||||
    model?: object
 | 
			
		||||
    model?: modelType
 | 
			
		||||
    required?: boolean,
 | 
			
		||||
    rules?: Rule,
 | 
			
		||||
    initValidate?: boolean,
 | 
			
		||||
    requiredIcons?: string,
 | 
			
		||||
    requiredErrorMessage?: string,
 | 
			
		||||
    validateMessage?: ValidateMessages,
 | 
			
		||||
    useCN?: boolean 
 | 
			
		||||
  }>(),
 | 
			
		||||
  {
 | 
			
		||||
    model: function(){
 | 
			
		||||
      return {}
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    useCN : true,
 | 
			
		||||
    requiredIcons : '',
 | 
			
		||||
    initValidate : false
 | 
			
		||||
  }
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const formItems : LayFormItemContext[] = [];
 | 
			
		||||
const formItemMap : {[key:string]:LayFormItemContext} = {};
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['submit'])
 | 
			
		||||
 | 
			
		||||
// 初始化表单就进行校验
 | 
			
		||||
onMounted(()=>{
 | 
			
		||||
  props.initValidate && validate()?.catch(err => {});
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// 原生提交表单事件
 | 
			
		||||
const submit = function () {
 | 
			
		||||
  emit('submit',props.model)
 | 
			
		||||
  let _isValidate = false;
 | 
			
		||||
  validate((isValidate, model, errors) => {
 | 
			
		||||
    _isValidate = isValidate as boolean;
 | 
			
		||||
    emit('submit', isValidate, model, errors);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // 如果表单失败则阻止提交表单,成功则进行提交表单
 | 
			
		||||
  return _isValidate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 校验表单数据
 | 
			
		||||
 * @param fields 需要校验的表单字段(string|string[]); 该字段如果为function, 则默认为回调函数,校验全部字段;
 | 
			
		||||
 * @param callback 校验表单之后的回调函数
 | 
			
		||||
 **/
 | 
			
		||||
const validate = function(fields?: string|string[]|FormCallback|null, callback?: FormCallback | null){
 | 
			
		||||
  // 根据参数识别需要校验的表单项
 | 
			
		||||
  let validateItems : LayFormItemContext[]  = formItems;
 | 
			
		||||
  if (typeof fields === 'function') {
 | 
			
		||||
    callback = fields;
 | 
			
		||||
  } else if (typeof fields === 'string' || (Array.isArray(fields) && fields.length > 0)) {
 | 
			
		||||
    validateItems = [];
 | 
			
		||||
    const validateFields = !fields ? [] : ([] as string[]).concat(fields);
 | 
			
		||||
    validateFields.forEach(field => formItemMap[field] && validateItems.push(formItemMap[field]));
 | 
			
		||||
  }
 | 
			
		||||
  // 通过调用每个子项进行校验
 | 
			
		||||
  let errorsArrs: ValidateError[] = [];
 | 
			
		||||
  validateItems.forEach(filed => {
 | 
			
		||||
    filed.validate((errors, _fields)=>{
 | 
			
		||||
      errorsArrs = errorsArrs.concat(errors as ValidateError[]);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  const isValidate = errorsArrs.length === 0;
 | 
			
		||||
  // 有回调则进行回调
 | 
			
		||||
  if (typeof callback === 'function') {
 | 
			
		||||
    isValidate ? callback(true, props.model, null) : callback(false, props.model, errorsArrs);
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 没有回调则创建一个Promise的链式调用
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    const callbackParams = {
 | 
			
		||||
      isValidate, 
 | 
			
		||||
      model : props.model, 
 | 
			
		||||
      errors: isValidate ? null : errorsArrs
 | 
			
		||||
    };
 | 
			
		||||
    callbackParams.isValidate ? resolve(callbackParams) : reject(callbackParams);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 清除校验
 | 
			
		||||
 * @param fields 需要进行清除校验的表单字段(string|string[]); 该字段如果为null, 则默认为全部字段清除校验;
 | 
			
		||||
 **/
 | 
			
		||||
const clearValidate = function(fields?: string | string[]){
 | 
			
		||||
  const clearFields = !fields ? [] : ([] as string[]).concat(fields);
 | 
			
		||||
  if (clearFields.length === 0) {
 | 
			
		||||
    formItems.forEach(filed => filed.clearValidate());
 | 
			
		||||
  } else {
 | 
			
		||||
    clearFields.forEach(field => formItemMap[field] && formItemMap[field].clearValidate());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 重置表单所有值
 | 
			
		||||
 **/
 | 
			
		||||
const reset = function(){
 | 
			
		||||
  for (const key in props.model) {
 | 
			
		||||
    props.model[key] = null;
 | 
			
		||||
  }
 | 
			
		||||
  // 重新校验
 | 
			
		||||
  setTimeout(()=>validate()?.catch(err => {}), 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 添加子项
 | 
			
		||||
const addField = function(item : LayFormItemContext) {
 | 
			
		||||
  formItems.push(item);
 | 
			
		||||
  formItemMap[item.prop as string] = item;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({validate, clearValidate, reset});
 | 
			
		||||
 | 
			
		||||
provide(layFormKey, reactive({
 | 
			
		||||
  formItems, 
 | 
			
		||||
  addField,
 | 
			
		||||
  clearValidate,
 | 
			
		||||
  validate,
 | 
			
		||||
  ...toRefs(props)
 | 
			
		||||
}));
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								src/module/formItem/cnValidateMessage.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/module/formItem/cnValidateMessage.ts
									
									
									
									
									
										Normal 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;
 | 
			
		||||
							
								
								
									
										47
									
								
								src/module/formItem/index.less
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/module/formItem/index.less
									
									
									
									
									
										Normal 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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -41,10 +41,10 @@ const props = defineProps<{
 | 
			
		||||
const openState = ref(false)
 | 
			
		||||
 | 
			
		||||
const open = function () {
 | 
			
		||||
  openState.value = true
 | 
			
		||||
  openState.value = !openState.value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const selectItem = reactive({ label: '', value: props.modelValue })
 | 
			
		||||
const selectItem = reactive({ label: null, value: props.modelValue })
 | 
			
		||||
 | 
			
		||||
provide('selectItem', selectItem)
 | 
			
		||||
provide('openState', openState)
 | 
			
		||||
@ -56,4 +56,12 @@ watch(selectItem, function (item) {
 | 
			
		||||
  emit('change', item.value)
 | 
			
		||||
  emit('update:modelValue', item.value)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(()=>props.modelValue, function (value) {
 | 
			
		||||
   if (!value) {
 | 
			
		||||
    selectItem.label = null;
 | 
			
		||||
    selectItem.value = '';
 | 
			
		||||
    emit('update:modelValue', null);
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								src/module/type/form.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/module/type/form.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
import type { ValidateCallback, ValidateError, ValidateMessages } from 'async-validator'
 | 
			
		||||
 | 
			
		||||
export const layFormKey = 'LayForm'
 | 
			
		||||
 | 
			
		||||
export interface LayFormContext {
 | 
			
		||||
    model: modelType
 | 
			
		||||
    required?: boolean
 | 
			
		||||
    requiredErrorMessage?: string
 | 
			
		||||
    validateMessage: ValidateMessages
 | 
			
		||||
    rules?: Record<string, unknown>
 | 
			
		||||
    useCN : boolean
 | 
			
		||||
    requiredIcons?: string
 | 
			
		||||
    addField: (field: LayFormItemContext) => void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface LayFormItemContext {
 | 
			
		||||
    prop?: string
 | 
			
		||||
    $el: HTMLDivElement
 | 
			
		||||
    required?: boolean
 | 
			
		||||
    rules?: Record<string, unknown>
 | 
			
		||||
    validate(callback?: ValidateCallback): void
 | 
			
		||||
    clearValidate(): void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export declare type modelType = { [key: string]: any }
 | 
			
		||||
 | 
			
		||||
export declare interface FormCallback {
 | 
			
		||||
    (
 | 
			
		||||
        isValid?: boolean, 
 | 
			
		||||
        model?: modelType,
 | 
			
		||||
        errors?: ValidateError[] | null
 | 
			
		||||
    ): void
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export declare interface FieldValidateError extends ValidateError {
 | 
			
		||||
    label ?: string
 | 
			
		||||
}
 | 
			
		||||
@ -1,2 +1,3 @@
 | 
			
		||||
export * from './public'
 | 
			
		||||
export * from './select'
 | 
			
		||||
export * from './form'
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user