基于VUE和小程序的车牌号输入

3,336 阅读5分钟

preview pic

基于h5的车牌号输入组件(VUE)

手写了一个车牌号输入的组件。

首先看下车牌号的规则

  • 车牌号规则

除了如今的自选个性车牌以外,当下使用的九二式机动车号牌,由中文与英文字母的发牌机关代码与五位号码组成。比如“苏A-12345”,“苏”代表江苏省,A是南京市公安局车辆管理所发牌代码,代表此车是由南京市公安局车辆管理所发牌。后面五位数是序号。 - 百度百科

  • 新能源车牌

与普通汽车号牌相比,新能源汽车号牌号码增加了1位,如原“粤B·D1234”可升位至“粤B·D12345”。升位后,号码编排更加科学合理,避免了与普通汽车号牌“重号”,有利于在车辆高速行驶时更准确辨识。

新能源汽车号牌按照不同车辆类型实行分段管理,字母“D”代表纯电动汽车,字母“ F”代表非纯电动汽车(包括插电式混合动力和燃料电池汽车等)。小型汽车号牌中“D”或“F”位于号牌序号的第一位,大型汽车号牌中“D”或“F”位于号牌序号的最后一位。
- 百度百科

想象一下用户操作流程,根据车牌号的规则,用户的输入过程是‘省’->‘城市’->‘序号’->‘新能源位数’(可选)。用户可能会点击某一位数进行修改,也可以从后往前删除,新能源又要单独区分开,每一位数的键盘分布又是不同的。

那我们定义一个光标,记录输入的位数。根据这个光标的位置来获取键盘类型,来更新键盘的内容和事件。

/** 标识输入框的位数
 * @param type {number} 键盘类型
 * 1:省份输入。键盘:省份简称。
 * 2:区号输入。键盘:数字+字母。数字和I不可选
 * 3:序号选择。键盘:数字+字母。I、O不可选
 * 4:特殊位数。键盘:数字+字母+特殊字符('字'切换显示'学挂警'按钮)。I、O不可选
 * 5:新能源标识位。键盘:数字+字母。I、O不可选
 */
 const InputAreaList = [
  { index: 0, type: 1 },
  { index: 1, type: 2 },
  { index: -1 }, // 分割点,例如:川A·12345
  { index: 2, type: 3 },
  { index: 3, type: 3 },
  { index: 4, type: 3 },
  { index: 5, type: 3 },
  { index: 6, type: 4 },
  { index: 7, type: 5 }
];

定义一些常量

const ProvinceList = "京津沪渝晋蒙吉黑苏浙皖闽赣鲁豫鄂湘粤桂琼川云藏青宁新港澳台甘辽冀贵陕".split("");
const NumberList = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const LetterList = "QWERTYUIOPASDFGHJKLZXCVBNM".split("");
const AdditionKeyList = "学挂警".split(""); // 可选中文字符(倒数第二位)

定义props参数

props: {
    initValue: {
      type: String,
      default: ""
    },
    limitProvince: {
      type: String,
      default: ""
    }, // 限制省份列表, 不用间隔
    limitCity: {
      type: String,
      default: ""
    } // 限制城市列表, 不用间隔
}

定义光标

data(){
    return {
        activeIndex: -1, // 输入进程,不同的进程显示不同的键盘布局和功能.输入第一位字符为0,第二位字符为1
    }
}

然后就是根据触发事件获取光标 更新键盘

/** 第1位 省份选择 只有省份简称列表可选 */
  if (keyboardType === 1) {
    this.iptKeyList = ProvinceList.map(e => ({
      key: e,
      disabled: this.checkProvinceDisabled(e)
    }));
  }
  /** 第2位 区号选择 英文字母+数字 数字和I不可选 */
  if (keyboardType === 2) {
    this.iptKeyList = NumberList.concat(LetterList).map(e => ({
      key: e,
      disabled: this.checkCityDisabled(e)
    }));
  }
  /** 第3-6位 序号选择 英文字母+数字 I、O不可选 */
  if (keyboardType === 3) {
    this.iptKeyList = NumberList.concat(LetterList).map(e => ({
      key: e,
      disabled: e === "I" || e === "O"
    }));
  }
  /** 第7位 序号选择 英文字母+数字+特殊字符切换('学挂警') I、O不可选 */
  if (keyboardType === 4) {
    let iptKeyList = NumberList.concat(LetterList);
    // 第三排最后一个字符插入'字',点击之后弹出'学挂警'三个可选字符按钮
    iptKeyList.splice(29, 0, "字");
    this.iptKeyList = iptKeyList.map(e => ({
      key: e,
      disabled: e === "I" || e === "O"
    }));
    this.showAdditionList = false;
  }
  /** 第8位 新能源车最后一位选择 英文字母+数字 I、O不可选 */
  if (keyboardType === 5) {
    this.iptKeyList = NumberList.concat(LetterList).map(e => ({
      key: e,
      disabled: e === "I" || e === "O"
    }));
  }
}

根据传入的props和不同位置,键盘有些按钮需要禁用。

需要注意的就是 第7位是可能会有特殊字符输入,在写模板时需要特殊处理

<div
  v-if="showAdditionList && item.key === '字'"
  class="addtion-list"
>
  <div
    v-for="item in additionKeyList"
    :key="item"
    @click="handleInput(item)"
  >
    {{ item }}
  </div>
</div>

后面就是处理删除按钮逻辑了,每次更新键盘,都会把删除按钮加上

/** 删除按钮 */
handleRemove() {
  const process = this.activeIndex;
  let plateNum = this.plateNum;
  // 如果位置上有值先删除
  if (plateNum[process] && plateNum[process] !== " ") {
    plateNum = this.replaceIndexChar(plateNum, process);
    this.plateNum = plateNum;
    return;
  }
  if (process === 7) {
    return;
  }
  //位置上没有值,光标回到上一个输入框,并删除上一个输入框的值
  const index = process - 1 > -1 ? process - 1 : 0;
  plateNum = this.replaceIndexChar(plateNum, index);
  this.plateNum = plateNum;
  this.activeIndex = index;
},

删除时,点一次是删除当前光标上的值,再点一次是删除上一位,光标移动到上一位。

这里比较特殊,新能源车牌符号位,需要手动点击把光标移入才能输入和删除,且删除不会自动前移。这个设定是产品需求,市面上也有其他交互方式,比如新能源可以自动删除回退的,可以对应修改。

最后再补上输入校验和点击确认返回值了。
mouted钩子中校验并修正props的值

mounted() {
    let plateNum = this.initValue;
    // 目前只做前两位校验,如果不合法,置为空
    if (plateNum[0] && this.checkProvinceDisabled(plateNum[0])) {
      plateNum = "";
    }
    if (plateNum[1] && this.checkCityDisabled(plateNum[1])) {
      plateNum = plateNum[0];
    }
    plateNum = plateNum.replace(/(·|\s*)/g, "").toUpperCase();
    this.plateNum = plateNum;
    this.activeIndex = !plateNum.length
      ? 0
      : plateNum.length > 7
      ? 7
      : plateNum.length === 7
      ? 6
      : plateNum.length;
},

输入合法车牌号时点击确认返回父组件对应的值

confirm() {
  const plateNum = this.plateNum.replace(/\s*/g, "");
  this.$emit("confirm", {
    plateNum,
    plateNumFor: plateNum.slice(0, 2) + "·" + plateNum.slice(2),
    isNewEnergy: plateNum.length === 8,
    isProvincialCity: plateNum[1] === "A",
    province: plateNum[0]
  });
},

到此就结束了,有一些模板代码和样式代码就没贴出来了,完整的代码在:
vue版: github.com/stoneNIK/vu…
小程序版:github.com/stoneNIK/we…

觉得不错的小伙伴帮我点个star鼓励下