Skip to content

vant-身份证键盘的优化处理

NumberKeyboard 数字键盘 - Vant 3

最简单的处理方法,存在一个缺点,不支持将内容粘贴过来。

1、无法黏贴复制的内容
2、输入错误,需要都删掉在重新输入
3、粗的方法如下:

html
<van-field  
  v-else  
  v-model="cardNumber"  
  input-align="right"  
  label="身份证号"  
  readonly  
  placeholder="请输入身份证号"  
  style="margin-bottom: 40px"  
  maxlength="30"  
  @touchstart.stop="showCardIdBoard = true"  
/>  
<!--  手动添加遮罩层:目的是为了防止下层单选点击选中。 同时:hide-on-click-outside="false" 设置为false  -->  
<div  
  :class="{'x-keyboard-overlay':showCardIdBoard}"  
  @click.stop="showCardIdBoard = false"  
>  
</div>  
<van-number-keyboard  
  v-model="cardNumber"  
  :transition="false"  
  :title="cardNumber"  
  :show="showCardIdBoard"  
  extra-key="X"  
  :hide-on-click-outside="false"  
  close-button-text="完成"  
  @blur="showCardIdBoard = false"  
/>
<van-field  
  v-else  
  v-model="cardNumber"  
  input-align="right"  
  label="身份证号"  
  readonly  
  placeholder="请输入身份证号"  
  style="margin-bottom: 40px"  
  maxlength="30"  
  @touchstart.stop="showCardIdBoard = true"  
/>  
<!--  手动添加遮罩层:目的是为了防止下层单选点击选中。 同时:hide-on-click-outside="false" 设置为false  -->  
<div  
  :class="{'x-keyboard-overlay':showCardIdBoard}"  
  @click.stop="showCardIdBoard = false"  
>  
</div>  
<van-number-keyboard  
  v-model="cardNumber"  
  :transition="false"  
  :title="cardNumber"  
  :show="showCardIdBoard"  
  extra-key="X"  
  :hide-on-click-outside="false"  
  close-button-text="完成"  
  @blur="showCardIdBoard = false"  
/>

优化处理:

1、 为了避免光标存在时,弹出原生键盘,需要使用inputmode="none"
2、不在给键盘绑定数据,自己实现一套删除、修改的逻辑,同时处理光标的位置
3、将外部内容粘贴过来的时候,需要触发双向绑定
4、长按input框的时候,注意不要将键盘收起来(这里用的name进行区分当前光标组件 document.activeElement和键盘组件是否是同一个)
5、新版组件可参考如下
``

java
<template>  
  <div>  
    <!--  多嵌套一层div,防止出现底部border -->  
    <div>  
      <van-field  
        ref="idCardRef"  
        v-model="cardNumber"  
        :class="{'no_padding':noPadding}"  
        inputmode="none"  
        :name="name"  
        input-align="right"  
        :label="label"  
        :placeholder="placeholder"  
        maxlength="18"  
        @click="showKbTrigger(true)"  
      />  
    </div>  
  
    <van-number-keyboard  
      :transition="true"  
      title="身份证号"  
      :show="showCardIdBoard"  
      extra-key="X"  
      :hide-on-click-outside="true"  
      close-button-text="完成"  
      @blur="keyboardClickCloseOrOut"  
      @input="onInput"  
      @delete="onDelete"  
    />  
  </div>  
</template>  
  
<script>  
export default {  
  name: 'IdKeyboard',  
  props: {  
    label: {  
      type: String,  
      default: '身份证号'  
    },  
    noPadding: {  
      type: Boolean,  
      default: false  
    },  
    name: {  
      type: String,  
      default: ''  
    },  
    placeholder: {  
      type: String,  
      default: '请输入身份证号'  
    },  
    value: {  
      type: String,  
      default: ''  
    },  
    show: {  
      type: Boolean,  
      default: false  
    }  
  },  
  data () {  
    return {  
      cardNumber: '',  
      showCardIdBoard: false  
    };  
  },  
  watch: {  
    value: {  
      handler (newVal) {  
        if (this.cardNumber !== newVal) {  
          this.cardNumber = newVal;  
        }  
      },  
      immediate: true  
    },  
    show: {  
      handler (newVal) {  
        this.showCardIdBoard = newVal;  
      },  
      immediate: true  
    },  
    // 如果复制过来的,也需要触发一下  
    cardNumber: {  
      handler (newVal) {  
        this.$emit('input', newVal);  
      }  
    }  
  },  
  methods: {  
    keyboardClickCloseOrOut () {  
      // @blur 点击关闭按钮或非键盘区域时触发.  
      // 1. 如果是input被点击触发,不收起键盘  
      setTimeout(() => {  
        this.$nextTick(() => {  
          if (document && document.activeElement && this.name && document.activeElement.name === this.name) {  
            // do nothing  
          } else {  
            this.showKbTrigger(false);  
          }  
        });  
      }, 200);  
    },  
    // 键盘相关的方法  
    showKbTrigger (show) {  
      this.showCardIdBoard = show;  
      this.$emit('update:show', show);  
      // 键盘展示的时候发送消息  
      if (show) {  
        this.$emit('showIdKeyboard');  
      } else {  
        this.$emit('hideIdKeyboard');  
      }  
    },  
    selectionIndex () {  
      const inputElement = this.$refs.idCardRef.$refs.input;  
      const startIndex = inputElement.selectionStart;  
      const endIndex = inputElement.selectionEnd;  
      return [inputElement, startIndex, endIndex];  
    },  
    onInput (e) {  
      const [inputElement, startIndex, endIndex] = this.selectionIndex();  
      this.cardNumber = this.addContentText(this.cardNumber, startIndex, endIndex, e);  
      // 还原光标位置  
      this.$nextTick(() => {  
        inputElement.setSelectionRange(startIndex + 1, startIndex + 1);  
      });  
      this.$emit('input', this.cardNumber);  
    },  
    addContentText (soure, start, end, newStr) {  
      // 开始位置 + 新字符串  + 尾部字符串  
      return soure.slice(0, start) + newStr + soure.slice(end);  
    },  
    onDelete () {  
      const [inputElement, startIndex, endIndex] = this.selectionIndex();  
      // 光标在最前,不进行操作  
      if (startIndex === endIndex && startIndex === 0) {  
        return;  
      }  
      if (startIndex === endIndex) {  
        // 删除一个字符  
        this.cardNumber = this.addContentText(this.cardNumber, startIndex - 1, endIndex, '');  
        // 还原光标位置  
        this.$nextTick(() => {  
          const toIndex = startIndex - 1 < 0 ? 0 : startIndex - 1;  
          inputElement.setSelectionRange(toIndex, toIndex);  
        });  
      } else {  
        // 删除多个字符  
        this.cardNumber = this.addContentText(this.cardNumber, startIndex, endIndex, '');  
        // 还原光标位置  
        this.$nextTick(() => {  
          inputElement.setSelectionRange(startIndex, startIndex);  
        });  
      }  
      this.$emit('input', this.cardNumber);  
    }  
  }  
};  
</script>  
  
<style scoped lang="less">  
  
/deep/ .van-number-keyboard__title {  
  font-size: 16px;  
}  
  
.no_padding {  
  padding: 0 !important;  
}  
  
</style>  
  
  
<!--  
使用     <id-keyboard v-model="cardNumber" name="xxxx"/>  
需要传递一个特定的name,防止点击name的时候被意外收起键盘。  
-->
<template>  
  <div>  
    <!--  多嵌套一层div,防止出现底部border -->  
    <div>  
      <van-field  
        ref="idCardRef"  
        v-model="cardNumber"  
        :class="{'no_padding':noPadding}"  
        inputmode="none"  
        :name="name"  
        input-align="right"  
        :label="label"  
        :placeholder="placeholder"  
        maxlength="18"  
        @click="showKbTrigger(true)"  
      />  
    </div>  
  
    <van-number-keyboard  
      :transition="true"  
      title="身份证号"  
      :show="showCardIdBoard"  
      extra-key="X"  
      :hide-on-click-outside="true"  
      close-button-text="完成"  
      @blur="keyboardClickCloseOrOut"  
      @input="onInput"  
      @delete="onDelete"  
    />  
  </div>  
</template>  
  
<script>  
export default {  
  name: 'IdKeyboard',  
  props: {  
    label: {  
      type: String,  
      default: '身份证号'  
    },  
    noPadding: {  
      type: Boolean,  
      default: false  
    },  
    name: {  
      type: String,  
      default: ''  
    },  
    placeholder: {  
      type: String,  
      default: '请输入身份证号'  
    },  
    value: {  
      type: String,  
      default: ''  
    },  
    show: {  
      type: Boolean,  
      default: false  
    }  
  },  
  data () {  
    return {  
      cardNumber: '',  
      showCardIdBoard: false  
    };  
  },  
  watch: {  
    value: {  
      handler (newVal) {  
        if (this.cardNumber !== newVal) {  
          this.cardNumber = newVal;  
        }  
      },  
      immediate: true  
    },  
    show: {  
      handler (newVal) {  
        this.showCardIdBoard = newVal;  
      },  
      immediate: true  
    },  
    // 如果复制过来的,也需要触发一下  
    cardNumber: {  
      handler (newVal) {  
        this.$emit('input', newVal);  
      }  
    }  
  },  
  methods: {  
    keyboardClickCloseOrOut () {  
      // @blur 点击关闭按钮或非键盘区域时触发.  
      // 1. 如果是input被点击触发,不收起键盘  
      setTimeout(() => {  
        this.$nextTick(() => {  
          if (document && document.activeElement && this.name && document.activeElement.name === this.name) {  
            // do nothing  
          } else {  
            this.showKbTrigger(false);  
          }  
        });  
      }, 200);  
    },  
    // 键盘相关的方法  
    showKbTrigger (show) {  
      this.showCardIdBoard = show;  
      this.$emit('update:show', show);  
      // 键盘展示的时候发送消息  
      if (show) {  
        this.$emit('showIdKeyboard');  
      } else {  
        this.$emit('hideIdKeyboard');  
      }  
    },  
    selectionIndex () {  
      const inputElement = this.$refs.idCardRef.$refs.input;  
      const startIndex = inputElement.selectionStart;  
      const endIndex = inputElement.selectionEnd;  
      return [inputElement, startIndex, endIndex];  
    },  
    onInput (e) {  
      const [inputElement, startIndex, endIndex] = this.selectionIndex();  
      this.cardNumber = this.addContentText(this.cardNumber, startIndex, endIndex, e);  
      // 还原光标位置  
      this.$nextTick(() => {  
        inputElement.setSelectionRange(startIndex + 1, startIndex + 1);  
      });  
      this.$emit('input', this.cardNumber);  
    },  
    addContentText (soure, start, end, newStr) {  
      // 开始位置 + 新字符串  + 尾部字符串  
      return soure.slice(0, start) + newStr + soure.slice(end);  
    },  
    onDelete () {  
      const [inputElement, startIndex, endIndex] = this.selectionIndex();  
      // 光标在最前,不进行操作  
      if (startIndex === endIndex && startIndex === 0) {  
        return;  
      }  
      if (startIndex === endIndex) {  
        // 删除一个字符  
        this.cardNumber = this.addContentText(this.cardNumber, startIndex - 1, endIndex, '');  
        // 还原光标位置  
        this.$nextTick(() => {  
          const toIndex = startIndex - 1 < 0 ? 0 : startIndex - 1;  
          inputElement.setSelectionRange(toIndex, toIndex);  
        });  
      } else {  
        // 删除多个字符  
        this.cardNumber = this.addContentText(this.cardNumber, startIndex, endIndex, '');  
        // 还原光标位置  
        this.$nextTick(() => {  
          inputElement.setSelectionRange(startIndex, startIndex);  
        });  
      }  
      this.$emit('input', this.cardNumber);  
    }  
  }  
};  
</script>  
  
<style scoped lang="less">  
  
/deep/ .van-number-keyboard__title {  
  font-size: 16px;  
}  
  
.no_padding {  
  padding: 0 !important;  
}  
  
</style>  
  
  
<!--  
使用     <id-keyboard v-model="cardNumber" name="xxxx"/>  
需要传递一个特定的name,防止点击name的时候被意外收起键盘。  
-->

其他可能存在的优化
1、 处理键盘遮挡的问题,仅参考

js
let element = this.$refs.fatherIdCardInputRef; 
if (!element) {  
  return;  
}
// 获取键盘高度
const minOffsetY = window.screen.width / 504 * 408;  
// 当前组件距离widow顶部的高度(非scroll)
const top = element.getBoundingClientRect().top;  
// 当前组件距离widow顶部的高度(非scroll)
const bottomY = window.screen.height - top;  
if (minOffsetY > bottomY) {  
  // console.log('需要上移', minOffsetY - bottomY);    
  setTimeout(() => {  
    this.$nextTick(() => {  
      // 页面进行移动,调整到合适为止。 20是魔法数字,提供额外的间距
      document.getElementById('app').scrollTop = document.getElementById('app').scrollTop + (minOffsetY - bottomY) + 20;  
    });  
  }, 100);  
}
let element = this.$refs.fatherIdCardInputRef; 
if (!element) {  
  return;  
}
// 获取键盘高度
const minOffsetY = window.screen.width / 504 * 408;  
// 当前组件距离widow顶部的高度(非scroll)
const top = element.getBoundingClientRect().top;  
// 当前组件距离widow顶部的高度(非scroll)
const bottomY = window.screen.height - top;  
if (minOffsetY > bottomY) {  
  // console.log('需要上移', minOffsetY - bottomY);    
  setTimeout(() => {  
    this.$nextTick(() => {  
      // 页面进行移动,调整到合适为止。 20是魔法数字,提供额外的间距
      document.getElementById('app').scrollTop = document.getElementById('app').scrollTop + (minOffsetY - bottomY) + 20;  
    });  
  }, 100);  
}

使用方法举例:

  1. 简单使用
js
<id-keyboard v-model="cardNumber" name="xxxx"/>  
// 需要传递一个特定的name,防止点击name的时候被意外收起键盘。
<id-keyboard v-model="cardNumber" name="xxxx"/>  
// 需要传递一个特定的name,防止点击name的时候被意外收起键盘。
  1. 支持绑定状态 @hideIdKeyboard、@showIdKeyboard,可控制键盘收起状态、展开状态。 可用在键盘收起展开时,处理键盘遮挡等其他事件
js
<id-keyboard  
  v-model="basicForm.fatherCardid"  
  name="fatherCardid"  
  :no-padding="true"  
  label=""  
  @hideIdKeyboard="formatterFather(basicForm.fatherCardid)"  
  @showIdKeyboard="showFatherCardKeyborad(1)"  
/>
<id-keyboard  
  v-model="basicForm.fatherCardid"  
  name="fatherCardid"  
  :no-padding="true"  
  label=""  
  @hideIdKeyboard="formatterFather(basicForm.fatherCardid)"  
  @showIdKeyboard="showFatherCardKeyborad(1)"  
/>
  1. 可用通过:show.sync绑定身份证键盘的弹出状态
js
<IdKeyboard  
  v-model="cardNumber"  
  name="basicCardNum"  
  :show.sync="showCardIdBoard"  
  style="margin-bottom: 40px"  
/>
<IdKeyboard  
  v-model="cardNumber"  
  name="basicCardNum"  
  :show.sync="showCardIdBoard"  
  style="margin-bottom: 40px"  
/>