微信小程序车牌号输入组件(虚拟键盘)

本文参考 微信小程序中实现车牌输入功能, 进行了更加细致的键盘划分、删除等功能
文中给出代码都是截图,完整代码在文末贴出。

组件效果

在这里插入图片描述
组件功能:

  1. 其中虚拟键盘、新能源车牌等功能均可使用
  2. 封装成一个组件可以直接 Import 使用
  3. 可以给定默认初始值(如 gif 中所示)
  4. 父组件通过 change 事件可以监听到车牌号每一次的变化
  5. 组件部分样式也可以给定 class 来自定义
  6. 可以规范用户输入车牌号,防止乱输引起的数据库错乱

组件思路

  • 根据车牌号的规则,根据不同的第几位生成键盘

例如:第一位为省份简称
在这里插入图片描述
第二位为各省份地区编码

在这里插入图片描述
后面几位为普通 数字和部分字母组合,字母中不包括 i 和 o ,此处也是我百度之后才发现这个规律。

在这里插入图片描述
最后一位有字母数字之类的,也有一些特殊的字,比如 “港”, “澳”, “学”, “挂”, “警”

在这里插入图片描述
所以一共有四中键盘类型。
在这里插入图片描述

  • 让当前输入框高亮,这样容易辨别。

在这里插入图片描述
在这里插入图片描述

  • 给定车牌号的渲染数据为数组,这样可以一连串显示,最后通过 join 方法转换成 字符串,并告知父组件变化。显示默认车牌的时候,用 split 方法 转换成数组给当前显示赋值。这样保证输入和输出的数据都是字符串,但是其实是利用数组完成的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 当前输入完成后,自动跳转到下一输入框。当前内容删除后,自动跳转到上一输入框。并且要保证 虚拟键盘显示的内容与车牌规则保持一致。
    这里主要利用的就是给定了一个参数,来告诉我点击到的是哪一个输入框,该显示哪一个虚拟键盘类型。

在这里插入图片描述
用省份显示为例

在这里插入图片描述
其他都是一样的处理方式,当普通几位显示的时候,不改变 虚拟键盘类型就可以了。

  • 还在组件中加入了自定义类的功能,这样可以在父组件中修改样式,按需求定制。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在父组件中修改与否如下图所示:

在这里插入图片描述
在这里插入图片描述

组件代码(完整代码)

同款代码 demo 在: https://download.csdn.net/download/lb1135909273/54815362,当然是不付费的啦!

wxml:

<!--components/CarNumber/index.wxml-->
<!-- 车牌号输入框 -->
<view class="inputs custom-input-class">
  <view class="inputs-item custom-item-class {{ currentInput === 0 ? 'item-active' : '' }}" bind:tap="showProvinceBoard">{{ carNum[0] }}</view>
  <view class="inputs-item custom-item-class {{ currentInput === 1 ? 'item-active' : '' }}" bind:tap="showAreaBoard">{{ carNum[1] }}</view>
  <view class="split">·</view>
  <view class="inputs-item custom-item-class {{ currentInput === 2 ? 'item-active' : '' }}" bind:tap="showNumberBoard" data-index="{{ 2 }}">{{ carNum[2] }}</view>
  <view class="inputs-item custom-item-class {{ currentInput === 3 ? 'item-active' : '' }}" bind:tap="showNumberBoard" data-index="{{ 3 }}">{{ carNum[3] }}</view>
  <view class="inputs-item custom-item-class {{ currentInput === 4 ? 'item-active' : '' }}" bind:tap="showNumberBoard" data-index="{{ 4 }}">{{ carNum[4] }}</view>
  <view class="inputs-item custom-item-class {{ currentInput === 5 ? 'item-active' : '' }}" bind:tap="showNumberBoard" data-index="{{ 5 }}">{{ carNum[5] }}</view>
  <view class="inputs-item custom-item-class {{ currentInput === 6 ? 'item-active' : '' }}" bind:tap="showNumberBoard" data-index="{{ 6 }}">{{ carNum[6] }}</view>
  <view wx:if="{{ !newEnergy }}" class="new-energy" bind:tap="changeCarToNewEnergy">
    <image class="new-energy-img custom-energy-icon" src="../../statics/img/new_energy.png" />
  </view>
  <view wx:else class="inputs-item custom-item-class {{ currentInput === 7 ? 'item-active' : '' }}" bind:tap="showLastBoard">{{ carNum[7] }}</view>
</view>
<!-- 虚拟键盘 -->
<view class="keyboard" hidden="{{ !keyboard }}">
  <view class="keyboardClose">
    <view class="keyboardClose_btn" bindtap='closeKeyboard'>关闭</view>
  </view>
  <!-- 省份简写键盘 -->
  <view class="keyboard-item" wx:if="{{ keyboardType === 1 }}">
    <view class="keyboard-line" wx:for="{{ provinces }}" wx:key="index">
      <view class="keyboard-btn" wx:for="{{ item }}" wx:key="index" data-val="{{ itemlist }}" wx:for-item="itemlist" bindtap='chooseProvince'>
        {{ itemlist }}
      </view>
    </view>
    <view class="keyboard-del" bindtap="delProvince">
      <text>清除</text>
    </view>
  </view>
  <!-- 地区简写键盘 -->
  <view class="keyboard-item" wx:if="{{ keyboardType === 2 }}">
    <view class="keyboard-line" wx:for="{{ areas }}" wx:key="index">
      <view class="keyboard-btn" wx:for="{{ item }}" wx:key="index" data-val="{{ itemlist }}" wx:for-item="itemlist" bindtap='chooseArea'>
        {{ itemlist }}
      </view>
    </view>
    <view class="keyboard-del" bindtap='delArea'>
      <text>清除</text>
    </view>
  </view>
  <!-- 普通类型键盘 -->
  <view class="keyboard-item" wx:if="{{ keyboardType === 3 }}">
    <view class="keyboard-line" wx:for="{{ numbers }}" wx:key="index">
      <view class="keyboard-btn" wx:for="{{ item }}" wx:key="index" data-val="{{ itemlist }}" wx:for-item="itemlist" bindtap='chooseNumber'>
        {{ itemlist }}
      </view>
    </view>
    <view class="keyboard-del" bindtap='delNumber'>
      <text>清除</text>
    </view>
  </view>
  <!-- 最后一位键盘 -->
  <view class="keyboard-item" wx:if="{{ keyboardType === 4 }}">
    <view class="keyboard-line" wx:for="{{ last }}" wx:key="index">
      <view class="keyboard-btn" wx:for="{{ item }}" wx:key="index" data-val="{{ itemlist }}" wx:for-item="itemlist" bindtap='chooseLast'>
        {{ itemlist }}
      </view>
    </view>
    <view class="keyboard-del" bindtap='delLast'>
      <text>清除</text>
    </view>
  </view>
</view>

wxss:

/* components/CarNumber/index.wxss */
.inputs {
  display: flex;
  align-items: center;
}

.split {
  font-weight: bold;
}

.inputs-item {
  border: 1rpx solid #333;
  border-radius: 10%;
  padding: 10rpx 0;
  margin: 10rpx;
  width: 65rpx;
  font-size: 34rpx;
  text-align: center;
  height: 50rpx;
}

.new-energy {
  display: flex;
  align-items: center;
}

.new-energy-img{
  height: 71rpx;
  width: 68rpx;
  margin: 10rpx;
  border: none;
}

.item-active {
  border: 5rpx solid #4b71fc;
  color: #4b71fc;
}

.keyboard {
  position: fixed;
  bottom: 0;
  /* height: 45vh; */
  background-color: #d1d5d9;
  width: 100%;
  left: 0;
  padding-bottom: 10rpx;
}

/* 关闭虚拟键盘 */
.keyboardClose {
  height: 70rpx;
  background-color: #f7f7f7;
  overflow: hidden;
}

.keyboardClose_btn {
  float: right;
  line-height: 70rpx;
  font-size: 15px;
  padding-right: 30rpx;
}

.keyboard-line {
  display: flex;
  justify-content: center;
}

/* 虚拟键盘-单个按钮 */
.keyboard-btn {
  font-size: 32rpx;
  color: #333333;
  background: #fff;
  display: inline-block;
  padding: 18rpx;
  text-align: center;
  box-shadow: 0 2rpx 0 0 #999999;
  border-radius: 10rpx;
  margin: 5rpx 6rpx;
}

/* 虚拟键盘-删除按钮 */
.keyboard-del {
  font-size: 32rpx;
  color: #333333;
  background: #A7B0BC;
  display: inline-block;
  padding: 18rpx;
  box-shadow: 0 2rpx 0 0 #999999;
  border-radius: 10rpx;
  margin: 5rpx;
  position: absolute;
  bottom: 10rpx;
  right: 10rpx;
}

js:

// components/CarNumber/index.js
Component({
  /**
   * 自定义样式
   */
  externalClasses: ['custom-input-class', 'custom-item-class', 'custom-energy-icon'],

  /**
   * 组件的属性列表
   */
  properties: {
    defaultNum: {
      type: String,
      default: ''
    }
  },

  /**
   * 页面展示
   */
  pageLifetimes: {
    show() {
      if (this.data.defaultNum) {
        // 存在默认车牌号
        const length = this.data.defaultNum.length
        if (length === 8) {
          // 新能源车牌号
          this.setData({
            newEnergy: true
          })
        }
        this.setData({
          carNum: this.data.defaultNum.split('')
        })
      }
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    carNum: [],
    currentInput: 0,
    keyboard: false,
    keyboardType: 1,
    newEnergy: false,
    // 省份输入
    provinces: [
      ['京', '沪', '粤', '津', '冀', '晋', '蒙', '辽', '吉', '黑'],
      ['苏', '浙', '皖', '闽', '赣', '鲁', '豫', '鄂', '湘'],
      ['桂', '琼', '渝', '川', '贵', '云', '藏'],
      ['陕', '甘', '青', '宁', '新'],
    ],
    // 地区输入
    areas: [
      ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K"],
      ["L", "M", "N", "P", "Q", "R", "S", "T", "U", "V"],
      ["W", "X", "Y", "Z"]
    ],
    // 车牌输入
    numbers: [
      ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
      ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K"],
      ["L", "M", "N", "P", "Q", "R", "S", "T", "U", "V"],
      ["W", "X", "Y", "Z"]
    ],
    // 最后一位输入
    last: [
      ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
      ["A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", ],
      ["N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"],
      ["港", "澳", "学", "挂", "警"]
    ]
  },

  /**
   * 组件的方法列表
   */
  methods: {
    /**
     * 关闭键盘
     */
    closeKeyboard() {
      this.setData({
        keyboard: false
      })
    },

    /**
     * 显示省份键盘
     */
    showProvinceBoard() {
      this.setData({
        keyboard: true,
        currentInput: 0,
        keyboardType: 1
      })
    },

    /**
     * 确定选择省份
     */
    chooseProvince(event) {
      const { val } = event.currentTarget.dataset
      this.setData({
        'carNum[0]': val,
        currentInput: 1,
        keyboardType: 2
      })
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },
    
    /**
     * 删除选定省份
     */
    delProvince() {
      this.setData({
        'carNum[0]': ''
      })
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 显示地区键盘
     */
    showAreaBoard() {
      this.setData({
        keyboard: true,
        currentInput: 1,
        keyboardType: 2
      })
    },

    /**
     * 选定地区
     */
    chooseArea(event) {
      const { val } = event.currentTarget.dataset
      this.setData({
        'carNum[1]': val,
        currentInput: 2,
        keyboardType: 3
      })
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 删除选定区域
     */
    delArea() {
      this.setData({
        'carNum[1]': '',
        currentInput: 0,
        keyboardType: 1
      })
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 显示普通键盘
     */
    showNumberBoard(event) {
      const { index } = event.currentTarget.dataset
      const keyboardType = index === 6 && !this.data.newEnergy ? 4 : 3
      this.setData({
        keyboard: true,
        currentInput: index,
        keyboardType: keyboardType
      })
    },

    /**
     * 选定车牌
     */
    chooseNumber(event) {
      const { val } = event.currentTarget.dataset
      const name = 'carNum[' + this.data.currentInput + ']'
      this.setData({
        [name]: val,
        currentInput: this.data.currentInput + 1,
        keyboardType: 3
      })
      // 跳到最后一位时,键盘不一样
      if (this.data.currentInput === 6 && !this.data.newEnergy) {
        this.setData({
          keyboardType: 4
        })
      } else if (this.data.currentInput === 7 && this.data.newEnergy) {
        this.setData({
          keyboardType: 4
        })
      }
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 删除车牌
     */
    delNumber() {
      const name = 'carNum[' + this.data.currentInput + ']'
      this.setData({
        [name]: '',
        currentInput: this.data.currentInput - 1,
        keyboardType: 3
      })
      // 如果删除到地区时,切换键盘类型
      if (this.data.currentInput === 1) {
        this.setData({
          keyboardType: 2
        })
      }
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 显示最后一位键盘
     */
    showLastBoard() {
      if (this.data.newEnergy) {
        // 新能源
        this.setData({
          keyboard: true,
          currentInput: 7,
          keyboardType: 4
        })
      } else {
        this.setData({
          keyboard: true,
          currentInput: 6,
          keyboardType: 4
        })
      }
    },

    /**
     * 选定最后一位
     */
    chooseLast(event) {
      const { val } = event.currentTarget.dataset
      if (this.data.newEnergy) {
        // 新能源
        this.setData({
          'carNum[7]': val,
          currentInput: this.data.currentInput + 1,
          keyboard: false
        })
      } else {
        this.setData({
          'carNum[6]': val,
          currentInput: this.data.currentInput + 1,
          keyboard: false
        })
      }
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 删除最后一位
     */
    delLast() {
      if (this.data.newEnergy) {
        this.setData({
          'carNum[7]': '',
          currentInput: this.data.currentInput - 1,
          keyboardType: 3
        })
      } else {
        this.setData({
          'carNum[6]': '',
          currentInput: this.data.currentInput - 1,
          keyboardType: 3
        })
      }
      // 每次都触发 change 事件,通知父组件
      this.triggerEvent('change', this.data.carNum.join(''))
    },

    /**
     * 切换输入新能源车牌号
     */
    changeCarToNewEnergy() {
      this.setData({
        newEnergy: true
      })
    }
  }
})

以上内容均由自己编写,可能对车牌号规则等理解有误解,或者测试有问题的,欢迎联系、交流。

PS:其中用的新能源的图片来自于 https://frontendmasters.com/sale/

  • 14
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值