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

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

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容