我们在不借助第三方组件库的情况下,自己制定表单验证
借助vee-validate
具体实现步骤
第一步:安装依赖包
npm i [email protected]
第二步:导入Form和Field组件并使用
import { Form, Field } from 'vee-validate
第三步:在Field组件上通过rules属性绑定验证规则,并且必须有name属性,其值可以自定义
通过Form组件的作用域插槽可以获取错误提示 errors.name属性值
接下来,我们来用代码实现以下表单验证:
首先,我们安装好依赖,并且导入 Form, Field,然后将验证规则单独封装起来
src/utils/validate-schema.js
*
// 统一管理表单的验证规则
// 定义表单-用户名验证规则
const checkAccount = value => {
// value是将来使用该规则的表单元素的值
// 1. 必填
// 2. 6-20个字符,需要以字母开头
// 如何反馈校验成功还是失败,返回true才是成功,其他情况失败,返回失败原因。
if (!value) return '请输入用户名'
if (!/^[a-zA-Z]\w{5,19}$/.test(value)) return '字母开头且6-20个字符'
return true
}
// 密码验证规则
const checkPwd = value => {
if (!value) return '请输入密码'
if (!/^\w{6,24}$/.test(value)) return '密码是6-24个字符'
return true
}
// 验证手机号
const checkMobile = value => {
if (!value) return '请输入手机号'
if (!/^1[3-9]\d{9}$/.test(value)) return '手机号格式错误'
return true
}
// 验证手机验证码
const checkCode = value => {
if (!value) return '请输入验证码'
if (!/^\d{6}$/.test(value)) return '验证码是6个数字'
return true
}
// 验证是否同意
const isAgree = value => {
if (!value) return '请勾选同意用户协议'
return true
}
export default {
account: checkAccount,
password: checkPwd,
mobile: checkMobile,
code: checkCode,
isAgree: isAgree
}
然后,使用这些表单规则
<template>
<div class="account-box">
<div class="toggle">
<a @click="isMsgLogin = false" href="javascript:;" v-if="isMsgLogin">
<i class="iconfont icon-user"></i> 使用账号登录
</a>
<a @click="isMsgLogin = true" href="javascript:;" v-else> <i class="iconfont icon-msg"></i> 使用短信登录 </a>
</div>
<Form class="form" v-slot="{ errors }">
<template v-if="!isMsgLogin">
<div class="form-item">
<div class="input">
<i class="iconfont icon-user"></i>
<Field
v-model="form.account"
name="account"
autocomplete="off"
:rules="schema.account"
type="text"
placeholder="请输入用户名或手机号"
/>
</div>
<!-- 错误提示信息 -->
<div class="error" v-if="errors.account"><i class="iconfont icon-warning" /> {{ errors.account }}</div>
</div>
<div class="form-item">
<div class="input">
<i class="iconfont icon-lock"></i>
<Field
autocomplete="off"
v-model="form.password"
name="password"
:rules="schema.password"
type="password"
placeholder="请输入密码"
/>
</div>
<!-- 错误提示信息 -->
<div class="error" v-if="errors.password"><i class="iconfont icon-warning" /> {{ errors.password }}</div>
</div>
</template>
<template v-else>
<div class="form-item">
<div class="input">
<i class="iconfont icon-user"></i>
<Field
autocomplete="off"
v-model="form.mobile"
type="text"
name="mobile"
:rules="schema.mobile"
placeholder="请输入手机号"
/>
</div>
<!-- 错误提示信息 -->
<div class="error" v-if="errors.mobile"><i class="iconfont icon-warning" /> {{ errors.mobile }}</div>
</div>
<div class="form-item">
<div class="input">
<i class="iconfont icon-code"></i>
<Field
autocomplete="off"
v-model="form.code"
name="code"
:rules="schema.code"
type="password"
placeholder="请输入验证码"
/>
<span class="code">发送验证码</span>
</div>
<!-- 错误提示信息 -->
<div class="error" v-if="errors.code"><i class="iconfont icon-warning" /> {{ errors.code }}</div>
</div>
</template>
<div class="form-item">
<div class="agree">
<!-- as表示Field组件最终渲染成Checkbox ,这里我们使用的多选框,是我们之前自己封装好的-->
<Field as="Checkbox" name="isAgree" :rules="schema.isAgree" v-model="form.isAgree" />
<span>我已同意</span>
<a href="javascript:;">《隐私条款》</a>
<span>和</span>
<a href="javascript:;">《服务条款》</a>
</div>
<!-- 错误提示信息 -->
<div class="error" v-if="errors.isAgree"><i class="iconfont icon-warning" /> {{ errors.isAgree }}</div>
</div>
<a href="javascript:;" class="btn">登录</a>
</Form>
<div class="action">
<img src="https://qzonestyle.gtimg.cn/qzone/vas/opensns/res/img/Connect_logo_7.png" alt="" />
<div class="url">
<a href="javascript:;">忘记密码</a>
<a href="javascript:;">免费注册</a>
</div>
</div>
</div>
</template>
<script>
import { ref, reactive } from 'vue'
import { Form, Field } from 'vee-validate'
import schema from '@/utils/validate-schema.js'
export default {
name: 'LoginForm',
components: { Form, Field },
setup() {
const isMsgLogin = ref(false)
// 表单数据
const form = reactive({
// 用户名
account: null,
// 密码
password: null,
// 手机号
mobile: null,
// 验证码
code: null,
// 是否同意
isAgree: false
})
return { isMsgLogin, form, schema }
}
}
</script>
<style scoped lang="less">
// 账号容器
.account-box {
.toggle {
padding: 15px 40px;
text-align: right;
a {
color: @xtxColor;
i {
font-size: 14px;
}
}
}
.form {
padding: 0 40px;
&-item {
margin-bottom: 28px;
.input {
position: relative;
height: 36px;
> i {
width: 34px;
height: 34px;
background: #cfcdcd;
color: #fff;
position: absolute;
left: 1px;
top: 1px;
text-align: center;
line-height: 34px;
font-size: 18px;
}
input {
padding-left: 44px;
border: 1px solid #cfcdcd;
height: 36px;
line-height: 36px;
width: 100%;
&.error {
border-color: @priceColor;
}
&.active,
&:focus {
border-color: @xtxColor;
}
}
.code {
position: absolute;
right: 1px;
top: 1px;
text-align: center;
line-height: 34px;
font-size: 14px;
background: #f5f5f5;
color: #666;
width: 90px;
height: 34px;
cursor: pointer;
}
}
> .error {
position: absolute;
font-size: 12px;
line-height: 28px;
color: @priceColor;
i {
font-size: 14px;
margin-right: 2px;
}
}
}
.agree {
a {
color: #069;
}
}
.btn {
display: block;
width: 100%;
height: 40px;
color: #fff;
text-align: center;
line-height: 40px;
background: @xtxColor;
&.disabled {
background: #cfcdcd;
}
}
}
.action {
padding: 20px 40px;
display: flex;
justify-content: space-between;
align-items: center;
.url {
a {
color: #999;
margin-left: 10px;
}
}
}
}
</style>
效果展示