基于Vant的field输入框实现输入内容可见的密码输入框(v-
2020-04-23 本文已影响0人
KATENGC
在实际项目开发中,要实现密码输入框带密码可见切换按钮(右侧的👀),点击👀可以显示或隐藏密码。
我的项目中使用有赞的Vant组件,但在官方文档中Filed密码框没有右侧👀(没有使用过Vant的同学可以先了解下)。
image.png
出于这样的原因,我决定自己对field输入框进行二次封装,来实现这个功能
我们先来了解下v-model
v-model是什么?
- v-model 即可以作用在普通表单元素上,又可以作用在组件上。
- vue.js隐式添加 value 的 prop,子组件通过 props.value 接收值。
- 子组件通过 this.$emit('input'),改变父组件 v-model 绑定的值。
- v-model 可以实现双向绑定,无需定义接收事件。
那为什么v-model能实现双向绑定呢?
<input v-model="something">
经过vue转换后变成来这样
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
那开始我的组件封装之旅吧
- 先创建组件,命名为PasswordField
<template>
<!--带密码可见功能的密码输入框-->
<van-field
v-model="password"
type="password"
label='密码'
placeholder='请输入密码'>
<!--利用插槽添加了右侧图标-->
<template slot="right-icon">
<img style="width: 0.59rem;height: 0.59rem"
src="static/img/icon_login_show@2x.png"
alt="">
</template>
</van-field>
</template>
我们来看下初步效果
image.png
接下来实现下点击右侧眼睛切换图标的功能,需要在img上添加点击事件switchPasswordType,主要是先两个功能:
1.实现右侧图标切换
2.动态修改field的type
<template>
<!--带密码可见功能的密码输入框-->
<van-field
v-model="password"
:type="passwordType"
label='密码'
placeholder='请输入密码'>
<!--利用插槽添加了右侧图标-->
<template slot="right-icon">
<img style="width: 0.59rem;height: 0.59rem"
:src="passwordType==='password'?'static/img/icon_login_show@2x.png':'static/img/icon_login_hide@2x.png'"
alt=""
@click="switchPasswordType">
</template>
</van-field>
</template>
定义了变量passwordType,其默认值为password
data() {
return {
password:'',//当前输入框的值
passwordType: 'password'//输入框类型
}
}
添加switchPasswordType方法,动态修改passwordType的值
methods: {
switchPasswordType() {
this.passwordType = this.passwordType === 'password' ? 'text' : 'password'
}
}
接下来就是动态修改输入框类型type,同时改变显示的图标,这样就实现了点击👀实现输入框文字可见或不可见
//输入框类型修改
:type="passwordType"
//图标切换
:src="passwordType==='password'?'static/img/icon_login_show@2x.png':'static/img/icon_login_hide@2x.png'"
以上基本能实现我们要的功能,我们使用下这个组件
<template>
<div>
<title-bar></title-bar>
<!--自定义的密码输入框-->
<password-field></password-field>
</div>
</template>
image.png
切换后
image.png
但是我们如何能在外部获取到当前输入框的值呢,也就是说父组件如何得到子组件的数据?同时又要实现子组件中修改输入框的内容能实时更新到父组件中。接下来就是重点啦!!!(利用上面v-model来实现双向绑定)
对于一个子组件来说,首先要先接收一个value,同时value改变时又要通知父组件
<template>
<!--带密码可见功能的密码输入框-->
<van-field
v-model="password"
:type="passwordType"
label='密码'
placeholder='请输入密码'
@input="$emit('input',password)">
<!--利用插槽添加了右侧图标-->
<template slot="right-icon">
<img style="width: 0.59rem;height: 0.59rem"
:src="passwordType==='password'?'static/img/icon_login_show@2x.png':'static/img/icon_login_hide@2x.png'"
alt=""
@click="switchPasswordType">
</template>
</van-field>
</template>
<script>
export default {
name: "PasswordField",
model: {
prop: 'inputValue',
event: 'input'
},
props: {
/**
* 当前输入的值
*/
inputValue: {
type: String,
default: ''
}
},
data() {
return {
password: this.inputValue,
passwordType: 'password',
}
},
methods: {
switchPasswordType() {
this.passwordType = this.passwordType === 'password' ? 'text' : 'password'
}
}
}
</script>
这里主要做了两个工作:
1.添加model,将从父组件传入的inputValue赋值给子组件中的password,同时通过v-model实现绑定
model: {
prop: 'inputValue',
event: 'input'
},
props: {
/**
* 当前输入的值
*/
inputValue: {
type: String,
default: ''
}
},
data() {
return {
password: this.inputValue,
passwordType: 'password',
}
},
2.利用field的input事件,将子组件中的password通过$emit实时通知父组件
@input="$emit('input',password)"
现在我们来测试下
<template>
<div>
<title-bar></title-bar>
<!--自定义的密码输入框-->
<password-field v-model="parentPassword"></password-field>
</div>
</template>
我们来打印下parentPassword
image.png
可以看到已经获取到了子组件的输入框内容,目的达成👏👏👏
附上完整的代码(自己完善了下)
PasswordField.vue
<template>
<!--带密码可见功能的密码输入框-->
<van-field
class="field"
v-model="password"
:type="passwordType"
:label=label
:placeholder=placeholder
:required="required"
:rules="[{required: rulesRequired, message: placeholder},{ pattern,message: rulesMessage}]"
@input="$emit('input',password)">
<template slot="right-icon">
<img style="width: 0.59rem;height: 0.59rem"
:src="passwordType==='password'?'static/img/icon_login_show@2x.png':'static/img/icon_login_hide@2x.png'"
alt=""
@click.stop="switchPasswordType">
</template>
</van-field>
</template>
<script>
import * as _ from 'common/js/config'
export default {
name: "PasswordField",
model: {
prop: 'inputValue',
event: 'input'
},
props: {
/**
* 当前输入的值
*/
inputValue: {
type: String,
default: ''
},
/**
* 输入框左侧文本
*/
label: {
type: String,
default: '密码'
},
/**
* 占位提示文字
*/
placeholder: {
type: String,
default: '请输入密码'
},
/**
* 是否显示表单必填星号
*/
required: {
type: Boolean,
default: false
},
/**
* 校验提示文案
*/
rulesMessage: {
type: String,
default: '请输入正确的密码'
},
/**
* 校验规则:是否为必须字段
*/
rulesRequired: {
type: Boolean,
default: true
}
},
data() {
return {
password: this.inputValue,
passwordType: 'password',
pattern: _.pwdReg,//密码规则
}
},
methods: {
/**
* 密码是否可见
*/
switchPasswordType() {
this.passwordType = this.passwordType === 'password' ? 'text' : 'password'
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
.field {
padding 0.37rem 0.4rem
}
.field /deep/ .van-field__right-icon {
display flex
}
.field /deep/ .van-field__label {
width auto
margin-right 0.4rem
}
</style>
ModifyPsw.vue
<template>
<div>
<title-bar></title-bar>
<van-form @submit="onSubmit">
<password-field v-model="oldPassword" label="旧密码" placeholder="请输入旧密码"></password-field>
<password-field v-model="newPassword" label="新密码" placeholder="6-20位密码,至少含字母及数字"></password-field>
<div style="margin: 0.67rem 0.4rem">
<van-button class="btn-confirm" round block type="info" native-type="submit">确定修改</van-button>
</div>
</van-form>
</div>
</template>
<script>
import TitleBar from "components/base/TitleBar";
import PasswordField from "../../../base/field/PasswordField";
export default {
name: "ModifyPsw",
components: {
PasswordField,
TitleBar
},
data() {
return {
oldPassword: '',//旧密码
newPassword: '',//新密码
}
},
methods: {
/**
* 点击【确定修改】
*/
onSubmit() {
console.log('oldPassword:', this.oldPassword)
console.log('newPassword:', this.newPassword)
}
}
}
</script>
<style scoped lang="stylus" rel="stylesheet/stylus">
.btn-confirm {
height 1.2rem
font-size 0.48rem
}
</style>
最终效果
image.png