模仿支付宝密码输入框,支持明文密文切换

需求:不能让浏览器提示保存密码!同时支持明文密文切换。
思考:输入时,将输入的内容保存,然后转换成点,显示给Input,或者直接覆盖一个点在矿上,通过修改Input文字颜色来隐藏:会出现光标错位的情况,且无法判断用户将光标左右移动后的操作()
支付宝的密码框将input隐藏,使用样式来模拟框中光标,偷懒还是用Input :)
写到一半想到好像input和数组绑定,方便父组件操作,但是…
百度了好多解决方案,什么onfocus,多个Input,提交改type,

<template>
  <div class="password-input-el">
    <div
      class="passwordContainer"
      @click="focusOnInput"
      :style="!disabled?'cursor:pointer':'cursor:not-allowed'"
    ></div>
    <template v-if="canSee">
      <template v-for="index in maxlength">
        <input
          ref="input"
          :maxlength="1"
          type="text"
          style="float:left"
          @keyup="keyup(index-1,$event)"
          oncopy="return false"
          oncontextmenu="return false"
          onpaste="return false"
          oncut="return false"
          :disabled="disabled"
          @keydown="keydown"
        />
      </template>
      <img
        :style="!disabled?'cursor:pointer':'cursor:not-allowed'"
        class="eye"
        src="@/assets/images/zy.png"
        @click="canSee= !canSee"
      />
    </template>
    <template v-else>
      <template v-for="index in maxlength">
        <input
          ref="input"
          :maxlength="1"
          type="text"
          style="float:left"
          @keyup="keyup(index-1,$event)"
          oncopy="return false"
          oncontextmenu="return false"
          onpaste="return false"
          oncut="return false"
          :disabled="disabled"
          @keydown="keydown"
        />
      </template>
      <img
        :style="!disabled?'cursor:pointer':'cursor:not-allowed'"
        class="eye"
        src="@/assets/images/by.png"
        @click="canSee= !canSee"
      />
    </template>
    <div
      class="border"
      :style="{'transform':this.value.length==this.maxlength?'translateX('+(this.maxlength-1)*30+'px)':'translateX('+this.value.length*30+'px)'}"
    ></div>
  </div>
</template>

<script>
export default {
  name: 'passwordInput',
  props: {
    maxlength: {
      type: Number,
      default: 6
    },
    enter: {
      type: Function,
      default: () => {
        console.log('enter')
      }
    },
    disabled: {
      type: Boolean,
      default: false
    },
    value: {
      default: ''
    }
  },
  data() {
    return {
      canSee: false,
      type: 'text'
    }
  },
  watch: {
    canSee(newVal) {
      if (newVal) {
        for (let i = 0; i < this.value.length; i++) {
          this.$refs.input[i].value = this.value.substr(i, 1)
        }
      } else {
        for (let i = 0; i < this.value.length; i++) {
          this.$refs.input[i].value = '●'
        }
      }
    },
    value(val) {
      if (val == '') {
        this.$refs.input.forEach(item => {
          item.value = ''
        })
      }
    }
  },
  created() {},
  mounted() {},
  methods: {
    keydown(e) {
      if (e.preventDefault) {
        e.preventDefault()
      } else {
        // IE中阻止默认事件
        window.event.returnValue = false
      }
    },
    focusOnInput() {
      if (this.value.length == this.maxlength) {
        this.$refs.input[this.value.length - 1].focus()
        this.$refs.input[this.value.length - 1].setSelectionRange(1, 1)
        return
      }
      this.$refs.input[this.value.length].focus()
      this.$refs.input[this.value.length].setSelectionRange(1, 1)
    },
    keyup(index, e) {
      var obj = e.target
      var key = e.key
      if (/^[0-9]$/.test(key)) {
        if (this.value.length < this.maxlength) {
          this.up(this.value + (key + ''))
          if (this.canSee) {
            obj.value = key
          } else {
            obj.value = '●'
          }
          if (index < this.maxlength - 1) {
            this.$refs.input[index + 1].focus()
            // this.$refs.input[index + 1].setSelectionRange(1, 1)
          }
        }
      } else if (key == 'Backspace') {
        if (
          index == this.maxlength - 1 &&
          this.value.length == this.maxlength
        ) {
          obj.value = ''
          this.up(this.value.substring(0, this.value.length - 1))
        } else {
          this.up(this.value.substring(0, this.value.length - 1))
          if (index != 0) {
            this.$refs.input[index - 1].value = ''
            // this.$refs.input[index - 1].setSelectionRange(1, 1)
            this.$refs.input[index - 1].focus()
          }
        }
        // this.value = this.value.substring(0, this.value.length - 1)
      } else if (key == 'Enter') {
        obj.blur()
        this.enter()
      } else if (obj.value != '') {
        // 防止输完密码后,输入别的内容将最后一位删除
      } else {
        obj.value = ''
      }
    },
    up(newVal) {
      this.$emit('update:value', newVal)
    }
  }
}
</script>

<style lang='stylus' scoped>
div.password-input-el
  display inline-block
  position relative
  font-size 14px
  padding-right 56px
  .passwordContainer
    position absolute
    top 0
    left 0
    right 56px
    bottom 0
  input
    box-sizing border-box
    width 30px
    height 40px
    outline none
    box-shadow none
    font-family 'PingFang', 'songti', 'Avenir', Helvetica, Arial, sans-serif
    font-size 16px
    text-align center
    border 1px solid #DCDFE6
    border-right-width 0px
    &:last-of-type
      border-right-width 1px
    &:focus
      &~.border
        display block
        transition all 0.1s linear
  img
    width 56px
    height 40px
    position absolute
    z-index 1
    top 0
    right 0
  .border
    display none
    width 30px
    height 40px
    box-sizing border-box
    position absolute
    top 0
    left 0
    border 1px solid #409EFF
</style>


调用

<password-input :disabled="false" :value.sync="password" :maxlength="6" :enter="pay" ></password-input>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值