基于Vant的field输入框实现输入内容可见的密码输入框(v-

2020-04-23  本文已影响0人  KATENGC

在实际项目开发中,要实现密码输入框带密码可见切换按钮(右侧的👀),点击👀可以显示或隐藏密码。

image.png
我的项目中使用有赞的Vant组件,但在官方文档中Filed密码框没有右侧👀(没有使用过Vant的同学可以先了解下)。
image.png

出于这样的原因,我决定自己对field输入框进行二次封装,来实现这个功能

我们先来了解下v-model

v-model是什么?
  1. v-model 即可以作用在普通表单元素上,又可以作用在组件上。
  2. vue.js隐式添加 value 的 prop,子组件通过 props.value 接收值。
  3. 子组件通过 this.$emit('input'),改变父组件 v-model 绑定的值。
  4. v-model 可以实现双向绑定,无需定义接收事件。
那为什么v-model能实现双向绑定呢?
<input v-model="something">

经过vue转换后变成来这样

<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

那开始我的组件封装之旅吧

  1. 先创建组件,命名为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
上一篇下一篇

猜你喜欢

热点阅读