vue封装自定义数字键盘组件

最近在公司做一个项目,简单来说就是web端微信公众号上的一个申请表单页面,随便如个图

在这里插入图片描述

环境: vue框架 + vant组件库 + 其它(这里用不上的,不说)

具体如图:

在这里插入图片描述

都知道,input输入框如果不去给它设置属性,它默认地在移动端访问时,获取焦点的时候弹出的是中文,或者英文之类的默认键盘,反正不是数字键盘,但是看图可知,有关数量或者电话之类的数字字符串输入,并不需要中文或者英文字母,只需要输入数字即可,这个时候如果使用数字键盘,就能有效的避免用户输入其它不必要的字符串

场景是这样的…

既然需要使用数字键盘,而vant组件库恰好有这个组件,第一时间想到的肯定是使用vant组件库自带的数字键盘,文档链接:vant组件库数字键盘

部分代码:(代码较多,粘贴部分,能达到效果就行)
页面展示部分:

<van-cell-group>
      <van-field
        v-model="hospitalInfo.bedNum"    // 绑定数据
        input-align="right"
        placeholder="请输入"
        label="编制床位数"
        @click="bedNumKeyboardUp"  // 点击该输入框触发该事件
        readonly  // 只读,如果不加,点击输入框会获取焦点,从而弹出默认键盘,到时会出现默认键盘和数字键盘都弹出到页面上的情况
        required
      />
      <van-field
        v-model="hospitalInfo.bedOpen"
        input-align="right"
        placeholder="请输入"
        label="开放床位数"
        @click="bedOpenKeyboardUp"
        readonly
        required
      />
      ......
</van-cell-group>

定义数据:

  data() {
    return {
      hospitalInfo: {
        bedNum: '', // 编制床位数
        bedOpen: '', // 开放床位数
        ......
      },
      keyShow: false, // 控制数字键盘显示/隐藏
      diffType: 'bedNum', // 区分点击弹出数字键盘的是哪一行
      ......
    };
  }

添加数字键盘组件

<van-number-keyboard
   :show="keyShow"  // 控制数字键盘显示/隐藏
   extra-key="."          // 数字键盘左下角内容为“.”
   close-button-text="完成"
   @blur="keyShow = false"   //  点击关闭按钮或非键盘区域时触发
   @input="onInput"               //  点击按键时触发,回调参数是按键内容
   @delete="onDelete"           // 点击删除键时触发
 />

输入框点击事件:

    // 点击编制床位数一栏
    bedNumKeyboardUp() {
      this.keyShow = true;
      this.diffType = 'bedNum'; // 用于区分点击的是呢个输入框,下面会用到
    },
    // 点击开放床位数一栏
    bedOpenKeyboardUp() {
      this.keyShow = true;
      this.diffType = 'bedOpen';
    },

数字键盘绑定事件:

    // 数字键盘 点击按键时触发
    onInput(value) {
      this.hospitalInfo[this.diffType] = `${this.hospitalInfo[this.diffType]}${value}`;  // 将点击的那个按键的值(value)加到输入框中已有的字符串最后
    },
    // 数字键盘 点击删除键时触发
    onDelete() {
      const value = this.hospitalInfo[this.diffType].toString();
      if (Number(value.slice(0, value.length - 1)) === 0) {
        this.hospitalInfo[this.diffType] = '';
      } else {
        this.hospitalInfo[this.diffType] = Number(value.slice(0, value.length - 1));
      }
    },

注:Number('') = 0

这样做,这个数字键盘就能用,PC端,Android端,iOS12上,都能正常使用,除了在iOS10系统上,万恶的iOS系统…

问题描述:

使用vant组件库数字键盘,在PC端,Android手机上,iOS12手机上(只知道iOS12,其它的暂时不知道),无论是本地测试,还是发布上线,都没问题。
但是iOS10是个意外,使用iOS10手机在本地测试没问题,但是等把代码发布上线,在用iOS10手机访问,就会出现以下妖魔鬼怪的现象:在iOS10系统的手机上访问线上的页面,点击数字键盘任意键,键盘都会异常收起,有时候还会出现键位错位的情况

当时的解决思路

思路一:
既然是每次点击数字键盘任意键异常收起键盘,那就给点击按键时触发的onInput函数一个this.keyShow = true;,每次点击任意按键,都让数字键盘显示

onInput(value) {
      this.keyShow = true;  // 数字键盘显示
      this.hospitalInfo[this.diffType] = `${this.hospitalInfo[this.diffType]}${value}`; 
},

这样做最开始能达到效果,但是后来不知道咋个回事,他又不行了

在这里插入图片描述

思路二:
去vant组件库的GitHub上的lssues中找找有没有这个问题,还真找了个差不多的,但是照他的方法做,也没有解决,留个链接供参考,
链接:自己参考

最终解决方案

在网上找了很久,看了很多文章,都没有一个有用的方法,最后还是经大佬提点,决定自己封装一个自定义的数字键盘组件

网上一搜,基于vue封装自定义数字键盘的文章一大堆,我是借鉴的这位大佬的文章:Vue自定义数字键盘组件

在他的基础上,进行了部分的修改,改成了适合本次项目所需要的样子,如图

在这里插入图片描述

自定义数字键盘代码:

=>  components/NumKeypad.vue
--------------
<template>
  <div class="key-container">
    <div class="keyboard" @click.stop="handleKeyPress">
      <div class="key-row">
        <div class="key-cell" data-num="7">7</div>
        <div class="key-cell" data-num="8">8</div>
        <div class="key-cell" data-num="9">9</div>
        <div class="key-cell" data-num=""></div>
      </div>
      <div class="key-row">
        <div class="key-cell" data-num="4">4</div>
        <div class="key-cell" data-num="5">5</div>
        <div class="key-cell" data-num="6">6</div>
        <div class="key-cell" data-num=""></div>
      </div>
      <div class="key-row">
        <div class="key-cell" data-num="1">1</div>
        <div class="key-cell" data-num="2">2</div>
        <div class="key-cell" data-num="3">3</div>
        <div class="key-cell" data-num=""></div>
      </div>
      <div class="key-row">
        <div class="key-cell" data-num=".">.</div>
        <div class="key-cell" data-num="0">0</div>
        <div class="key-cell" data-num="D">删除</div>
        <div class="key-cell" data-num=""></div>
      </div>
      <div class="key-clear" data-num="C">清空</div>
      <div class="key-confirm" data-num="S">确认</div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      num: '',
    };
  },
  props: ['fatherNum'],
  mounted() {
    console.log('父组件传过来的', this.fatherNum);
    this.num = this.fatherNum;  // 父组件通过props传过来的值,这个值是父组件中 点击了的那个 使用了数字键盘的 那个输入框绑定了的值,因为存在输入框输入了值后还要在原来的基础上加或减数字的情况
  },
  methods: {
    // 处理按键
    handleKeyPress(e) {
      const num = e.target.dataset.num; // 点击的那个键盘代表值,即上面data-num嵌入的自定义数据,D:删除,C:清空,S:确认,‘’:无效,0-9:数字
      // 不同按键处理逻辑
      // '' 代表无效按键,直接返回
      if (num === '') return false;
      switch (String(num)) {
        // 删除键
        case 'D':
          this.handleDeleteKey();
          break;
        // 清空键
        case 'C':
          this.handleClearKey();
          break;
        // 确认键
        case 'S':
          this.handleConfirmKey();
          break;
        default:
          this.handleNumberKey(num);
          break;
      }
    },
    // 处理删除键
    handleDeleteKey() {
      const S = this.num; // 因为上面mounted()函数赋值,这个值已经是父组件点击的那个输入框的值了,当点击了数字键盘上的数字后,它也会发生变化的
      // 如果没有输入(或者长度为0),直接返回
      if (!S.length) return false;
      // 否则删除最后一个
      this.num = S.substring(0, S.length - 1);
      console.log('删除:', this.num);
      this.$emit('deleteEvent', this.num); // 把删除处理后了的值返回给父组件
    },
    // 处理清空键
    handleClearKey() {
      this.num = ''; // 管它三七二十一,清空就赋值一个‘’,然后返回给父组件
      this.$emit('clearEvent', this.num);
    },
    // 处理数字
    handleNumberKey(num) {
      const S = this.num; // 拿到父组件点击的那个输入框的值
      this.num = S + num; // 在原有的基础上将点击的那个数字按键的数字加在原有数字字符串末尾,这里应该用模板字符串的,这里写笔记才看到,this.num = `${S}${num}`;  // 这里赋值是为了配合上面删除时使用
      this.$emit('numberEvent', num); // 注意传的是点击的那个数字字符串,不是处理后的整个数字字符串
    },
    // 确认键
    handleConfirmKey() {
      const S = this.num;
      console.log('点击确认键:', S);
      this.$emit('confirmEvent', S);
    },
  },
};
</script>

<style lang="scss" scoped>
.key-container {
  margin-top: 50px;
  width: 100%;
  display: flex;
  display: -webkit-flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;

  .keyboard {
    position: fixed;
    bottom: 0;
    left: 0;
    height: 240px;
    width: 100%;
    background-color: #fff;

    .key-row {
      display: flex;
      display: -webkit-flex;
      position: relative;
      height: 60px;
      line-height: 60px;

      &::before {
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        right: 0;
        height: 1px;
        border-top: 1px solid #d5d5d6;
        color: #d5d5d6;
        -webkit-transform-origin: 0 0;
        transform-origin: 0 0;
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
      }
    }

    .key-cell {
      flex: 1;
      -webkit-box-flex: 1;
      text-align: center;
      position: relative;

      &::after {
        content: "";
        position: absolute;
        overflow: hidden;
        top: 0;
        right: 0;
        bottom: 0;
        height: 200%;
        border-right: 1px solid #d5d5d6;
        color: #d5d5d6;
        -webkit-transform-origin: 0 0;
        transform-origin: 0 0;
        -webkit-transform: scaleY(0.5);
        transform: scaleY(0.5);
      }

      &:nth-last-child(1)::after {
        border-right: 0;
      }
    }

    .disabled {
      background: rgba(0, 0, 0, 0.2);
    }

    .key-confirm {
      position: absolute;
      text-align: center;
      height: 118px;
      width: 25%;
      line-height: 118px;
      background: #fff;
      z-index: 5;
      right: 0;
      bottom: 1px;
    }
    
    .key-clear {
      position: absolute;
      text-align: center;
      height: 118px;
      width: 25%;
      line-height: 118px;
      background: #fff;
      z-index: 5;
      right: 0;
      top: 1px;
    }
  }
}

</style>

父组件使用上面的自定义组件:

  1. 导入:import Calculation from '~/NumKeypad'; // 上面写的那个组件的路径,这个‘~’是自己设置的路径别名

  2. 注册:

  components: {
    Calculation,
  },
  1. 使用
<Calculation
	v-if="keyShow"
	:fatherNum="hospitalInfo[this.diffType]"
	@confirmEvent="confirmEvent"
	@numberEvent="numberEvent"
	@clearEvent="clearEvent"
	@deleteEvent="deleteEvent"
></Calculation>
  1. 页面展示部分:和上面一样,只是部分代码,且没有变化,只是绑定的函数有所变化
<van-cell-group>
      <van-field
        v-model="hospitalInfo.bedNum"    // 绑定数据
        input-align="right"
        placeholder="请输入"
        label="编制床位数"
        @click="bedNumKeyboardUp"  // 点击该输入框触发该事件
        readonly  // 只读,如果不加,点击输入框会获取焦点,从而弹出默认键盘,到时会出现默认键盘和数字键盘都弹出到页面上的情况
        required
      />
      <van-field
        v-model="hospitalInfo.bedOpen"
        input-align="right"
        placeholder="请输入"
        label="开放床位数"
        @click="bedOpenKeyboardUp"
        readonly
        required
      />
      ......
</van-cell-group>

定义数据:

  data() {
    return {
      hospitalInfo: {
        bedNum: '', // 编制床位数
        bedOpen: '', // 开放床位数
        ......
      },
      keyShow: false, // 控制数字键盘显示/隐藏
      diffType: 'bedNum', // 区分点击弹出数字键盘的是哪一行
      ......
    };
  }
  1. 输入框绑定的点击函数
    // 点击编制床位数一栏,弹出数字键盘
    bedNumKeyboardUp() {
      console.log('弹出编制床位数数字键盘');
      this.keyShow = false;
      setTimeout(() => { this.keyShow = true; }, 100); 
      this.diffType = 'bedNum';
    },
    // 点击开放床位数一栏,弹出数字键盘
    bedOpenKeyboardUp() {
      console.log('弹出开放床位数数字键盘');
      this.keyShow = false;
      setTimeout(() => { this.keyShow = true; }, 100);
      this.diffType = 'bedOpen';
    },

为啥先false了又true?

由于使用数字键盘的输入框都设置了只读属性,目的是为了在点击时不使输入框获取焦点,因为获取焦点会弹出系统自带的默认键盘,既然现在无法获取焦点了,点击输入框时就不会有那个竖杠杠(获取焦点时输入框中的那个),这里如果只设置显示数字键盘(this.keyShow = true;),点击一个输入框显示出数字键盘后,再点击其它输入框,键盘不会有任何行为,即一直显示在那里,这就导致给用户一种错误的反馈,感觉好像点击其它输入框没点到似的.

这两排代码的作用就是,当个点击输入框时,会有个键盘收起后紧接着弹出的过程,就能给用户一种切换输入框成功了的感觉,不过我觉得这个方法很挫,肯定还有更好的方法来解决这个问题,以后想起了再记得来加上

  1. 接收子组件传过来的值,在父组件上做出相应的操作
    // 点击数字键
    numberEvent(res) {
      // console.log('数字键:', res);
      this.hospitalInfo[this.diffType] = `${this.hospitalInfo[this.diffType]}${res}`;
    },
    // 点击清除键
    clearEvent(res) {
      // console.log('清除键:', res);
      this.hospitalInfo[this.diffType] = res; // 清除,子组件传过来的是‘’
    },
    // 点击删除键
    deleteEvent(res) {
      // console.log('删除键:', res);
      this.hospitalInfo[this.diffType] = res;
    },
    // 点击确认键
    confirmEvent(res) {
      console.log('确认键:', res);
      this.keyShow = false; // 点击确认,收起键盘
    },
  1. 如果希望,当点击不需要使用数字键盘的输入框时,也隐藏数字键盘,可直接给其一个点击事件,执行this.keyShow = false;

用到了的一些知识点:

  1. vue中传值,父传子,子传父,此处都有用到,不明白参考连接:vue传值方式

  2. data-* 属性, 参考链接:data-* 属性

  3. substring()方法:substring()方法

记录一下,以后如果还有vue+vant的web项目,如果有用到数字键盘时,直接用这个自定义的数字键盘,不使用vant自带的那个,在iOS10上就不会再有那个问题了

  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值