7.2、利用vee-validate封装一个验证表单
文档地址:https://vee-validate.logaretm.com/v4/
整体架构类似于Elmentui的el-Form + el-Form-Item + 表单
一些可能会有疑惑的地方的解释:
- 这里的很多属性都利用了透传机制:即父组件身上的属性会透传到子组件的跟组件身上(子组件内必须有唯一一个跟组件才行)
- 主要使用作用域插槽来将子组件的数据和方法传递给父组件使用
- YuInput是我自己封装的输入框
Validate组件
- 该组件结构介绍:
- 插槽一slot是默认插槽里面用来放YuValidateItem组件
- 插槽二buttonGroup是作用域插槽,默认提供一个提交按钮,并将表单清空的方法提供出去
- 功能介绍:
- 绑定一个提交的方法,该提交方法触发自定义事件,一旦验证通过会将表单数据提交出去,如果验证失败,点击提交按钮没效果
<template>
<Form @submit="onSubmit" v-slot="{ resetForm }">
<slot />
<slot name="buttonGroup" :resetForm="resetForm">
<YuButton type="submit" :content="content" />
</slot>
</Form>
</template>
<script lang='ts'>
export default {};
</script>
<script setup lang='ts'>
import { Form } from "vee-validate";
import { defineEmits } from "vue";
const props = defineProps({
content: {
type: String,
default: "提交",
},
}),
emit = defineEmits(['onSubmit']),
onSubmit = (values: any) => {
emit('onSubmit',values)
};
</script>
<style lang='scss' scoped>
</style>
validateItem组件
- 组件结构介绍:
- 插槽一默认插槽,该地方写我们的表单输入框多选框等等。
- p标签用来错误提示
- 组件功能介绍
- 框架本身通过默认插槽提供了一些方法和属性,filed用来绑定表单数据的
- errorMessage是表单验证失败的错误提示
- 我将框架提供的filed 和 errorMessage向外暴露让父组件可以访问到
<template>
<Field
:name="name"
:rules="(rules as any)"
#default="{ field, errorMessage }"
:validate-on-blur="blur"
>
<slot :field="field" :errorMessage="errorMessage" />
<p class="text-red-600 text-xs ml-3">{{ errorMessage }}</p>
</Field>
</template>
<script lang='ts'>
export default {};
</script>
<script setup lang='ts'>
import { Field, ErrorMessage } from "vee-validate";
const props = defineProps({
rules: {
type: [Object,Function],
default: {},
},
name: {
type: String,
default: "",
},
blur: {
type: Boolean,
default: true,
},
input: {
type: Boolean,
default: false,
},
});
</script>
<style lang='postcss' scoped>
input {
@apply w-full;
}
</style>
<登录页面中使用效果展示和代码
- 结构介绍
- css用的是TailWindCss框架,占篇幅就不贴了
- 最外层YuValidate内部必须嵌套YuValidateItem使用,在YuValidateItem内部写我们的表单
- 功能介绍
- name字符串要和我们绑定的v-model一致,否则提交时无法获取。rules是我们绑定的自定义规则
- 输入框等表单需要绑定插槽提供的field
<template>
<div class="container">
<div class="login-wrapper">
<div class="login-form">
<h1 class="text-center text-gray-700 text-lg">会员登录</h1>
<YuValidate @onSubmit="onSubmit">
<YuValidateItem
name="formData.userName"
:rules="userNamerules"
v-slot="{ field, errorMessage }"
v-model="formData.userName"
>
<YuInput
v-bind="field"
placeholder="请输入手机号或邮箱"
type="text"
/>
</YuValidateItem>
<YuValidateItem
name="formData.password"
v-model="formData.password"
:rules="userPassword"
v-slot="{ field, errorMessage }"
>
<YuInput
v-bind="field"
placeholder="请输入密码"
type="password"
/>
</YuValidateItem>
<template #buttonGroup="{ resetForm }">
<YuButton type="submit" content="登录" />
<YuButton content="清空" @click="resetForm"/>
</template>
</YuValidate>
<ul class="login-options">
<li>网站首页</li>
<li>会员注册</li>
<li>找回密码</li>
</ul>
</div>
<div class="login-logo">
<img src="@/public/images/login-logo.jpg" alt="" />
</div>
</div>
</div>
</template>
<script setup lang='ts'>
import useLogin from './hooks'
const { formData,userNamerules,userPassword,loginHnadler,onSubmit } = useLogin()
</script>
<hooks
- 功能介绍
- 该hooks提供与登录表单相关的数据和功能
import useUtils from "../../utils/utils";
import { ref } from "vue";
import * as yup from "yup";
import { useRouter } from "vue-router";
import { loginApi } from "../../api/user";
interface IUserForm {
userName: string,
password: string
}
export default function useLogin() {
const utils = useUtils(),
formData = ref<IUserForm>({
userName: "",
password: "",
}),
router = useRouter(),
userNamerules = utils.Validate.validateEmail,
userPassword = yup.string().required().min(8),
loginHnadler = () => {
loginApi({ userName: "Yu123123", password: "123456" }).then((res) => {
utils.Storage.set({
key: "token",
value: res.data.token,
expire: "1m",
});
router.push("/admin/user");
});
},
onSubmit = (values: any) => {
console.log(values.formData,);
};
return {
formData,
userNamerules,
userPassword,
loginHnadler,
onSubmit
}
}
<邮箱验证的代码
const validateEmail = (value: string) => {
// if the field is empty
if (!value) {
return '必填项不能为空';
}
// if the field is not a valid email
const regex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i;
if (!regex.test(value)) {
return '格式不正确';
}
// All is good
return true;
}
<还可以优化的地方
- 可以直接给Validate传递rules数组来实现验证,弥补现在的问题(表单项多就会增加许多验证规则,从而导致代码不易维护):每个YuValidateItem如果需要验证都必须传递一个rules
- 解决方法:最外层YuValidate用一个rules数组来收集规则,数组接受对象,然后传递给YuValidateItem组件,该组件再根据自身需要的rules自行遍历取即可
- 样式目前都是写死的,可以通过外部传参动态调整。