vue 移动端车牌键盘

1.npm install input-plate-number --save 样式可以,键盘相对严谨,但是输入有bug,

2.npm install vue-carplate --save         插件样式个人看来是最好看的,键盘的输入最严谨,但是没有新能源车牌的选项,

3.https://github.com/Pinenutss/LicensePlate  插件是样式不好看,键盘的输入也不严谨,但是有新能源车牌,

https://github.com/liaoyinglong/plate-number-input依赖  样式、键盘输入都可以,但和项目(37.5px)不适配,也不好修改

4.最终使用手写的车牌键盘(vue+vant),易于修改各种需求

车牌键盘
<template>
  <div class="plateNumber">
    <div class="wrap">
      <van-tabs v-model="formData.commonCard">
        <van-tab title="普通车牌" name="1"></van-tab>
        <van-tab title="新能源车牌" name="2"></van-tab>
      </van-tabs>
      <!-- 车牌号码输入框 -->
      <div class="num-box">
        <div :class="{ active: activeKeyWordIndex == 0, num0: true }" @click="clickFirstWrap()">
          <span>{{ formData.num0 }}</span>
        </div>
        <div :class="{ active: activeKeyWordIndex == 1, num1: true }" @click="clickKeyWordWrap(1)">
          <span>{{ formData.num1 }}</span>
        </div>
        <em class="spot"></em>
        <div :class="{ active: activeKeyWordIndex == 2, num1: true }" @click="clickKeyWordWrap(2)">
          <span>{{ formData.num2 }}</span>
        </div>
        <div :class="{ active: activeKeyWordIndex == 3, num1: true }" @click="clickKeyWordWrap(3)">
          <span>{{ formData.num3 }}</span>
        </div>
        <div :class="{ active: activeKeyWordIndex == 4, num1: true }" @click="clickKeyWordWrap(4)">
          <span>{{ formData.num4 }}</span>
        </div>
        <div :class="{ active: activeKeyWordIndex == 5, num1: true }" @click="clickKeyWordWrap(5)">
          <span>{{ formData.num5 }}</span>
        </div>
        <div :class="{ active: activeKeyWordIndex == 6, num1: true }" @click="clickKeyWordWrap(6)">
          <span>{{ formData.num6 }}</span>
        </div>
        <div
          v-if="formData.commonCard == '2'"
          :class="{ active: isActive == 7, num1: true }"
          @click="clickKeyWordWrap(7)"
        >
          <span>{{ formData.num7 }}</span>
        </div>
      </div>
      <div style="margin: 16px; margin-top: 1rem">
        <van-button round block type="info" @click="submitFn()">确认</van-button>
      </div>
    </div>
    <div class="keyboard-wrap" v-if="firstWrapStatus">
      <div class="keyboard" >
        <van-button v-for="(item, index) in abbPlate._1" :key="index" @click="selectFirstWord(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard" >
        <van-button v-for="(item, index) in abbPlate._2" :key="index" @click="selectFirstWord(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard" >
        <van-button v-for="(item, index) in abbPlate._3" :key="index" @click="selectFirstWord(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard" >
        <van-button v-for="(item, index) in abbPlate._4" :key="index" @click="selectFirstWord(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard" >
        <van-button v-for="(item, index) in abbPlate._5" :key="index" @click="selectFirstWord(item)">{{ item }}</van-button>
        <span class="bordernone"></span>
      </div>

    </div>
    <div class="keyboard-wrap" v-if="keyBoardStatus">
      <!-- <div class="number-wrap"></div>
      <div class="letter-wrap"></div>
      <div class="cn-wrap"></div> -->
      <div class="keyboard"  v-if="activeKeyWordIndex !== 1">
        <van-button v-for="(item, index) in allKeyWord._1" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard" v-if="activeKeyWordIndex !== 1">
        <van-button v-for="(item, index) in allKeyWord._2" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
      </div>
      <div class="keyboard">
        <van-button v-for="(item, index) in allKeyWord._3" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard">
        <van-button v-for="(item, index) in allKeyWord._4" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard">
         <van-button v-for="(item, index) in allKeyWord._5" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
      </div>
      <div class="keyboard">
        <van-button v-for="(item, index) in allKeyWord._6" :key="index" @click="clickKeyBoard(item)">{{ item }}</van-button>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
        <!-- <span class="delete" @click="deleteWord"><img src="@/assets/images/icon-delete.png" alt="" /></span> -->
      </div>
      <div class="keyboard" v-if="(activeKeyWordIndex === 6 && formData.commonCard == '1') || activeKeyWordIndex === 7">
        <span v-for="(item, index) in allKeyWord._7" :key="index" @click="clickKeyBoard(item)">{{ item }}</span>
        <span class="bordernone"></span>
        <span class="bordernone"></span>
      </div>
      <div class="cancel">
        <span @click="clearInput()">清除</span>
        <span @click="keyBoardStatus = false">关闭</span>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      formData: {
        commonCard: '1',
        num0: '',
        num1: '',
        num2: '',
        num3: '',
        num4: '',
        num5: '',
        num6: '',
        num7: ''
      },
      abbPlate: {
        _1: ['京','沪','津','渝','鲁','冀','晋'],
        _2: ['蒙','辽','吉','黑','苏','浙','皖'],
        _3: ['闽','赣','豫','湘','鄂','粤','桂'],
        _4: ['琼','川','贵','云','藏','陕','甘'],
        _5: ['青','宁','新','港','澳','台'],
      },
      allKeyWord: {
        _1: [1, 2, 3, 4, 5, 6, 7],
        _2: [8, 9, 0],
        _3: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
        _4: ['H', 'J', 'K', 'L', 'M', 'N', 'O'],
        _5: ['P', 'Q', 'R', 'S', 'T', 'U', 'V'],
        _6: ['W', 'X', 'Y', 'Z'],
        _7: ['港', '澳', '学', '领', '警']
      },
      activeKeyWordIndex: 0, // 当前车牌号
      keyBoardStatus: false,
      firstWrapStatus: false, // 选择弹窗
      confirmTitle: '',
      submitConfirm: false,
      submitConfirmFalse: false,
      submitConfirmText: '',

      isActive: 0
    }
  },
  mounted() {},
  methods: {
    clearInput() {
      this.formData = {
        commonCard: '1',
        num0: '',
        num1: '',
        num2: '',
        num3: '',
        num4: '',
        num5: '',
        num6: '',
        num7: ''
      }
    },
    clickFirstWrap() {
      // 点击第一个输入框
      this.firstClickStatus = true
      this.firstWrapStatus = true
      this.keyBoardStatus = false
      this.formData.num0 = ''

      this.activeKeyWordIndex = 0
    },
    selectFirstWord(value) {
      // 选择省份
      // if (event.target.localName !== 'span') {
      //   return
      // }
      // this.formData.num0 = event.target.innerText
      this.formData.num0 = value
      this.firstSelectStatus = true
      this.firstWrapStatus = false
      this.firstClickStatus = false
      this.keyBoardStatus = true
      this.activeKeyWordIndex = 1
      // this.$refs.num1.focus()
      // document.getElementById('num1').focus()
    },
    clickKeyBoard(item) {
      // 点击自定义键盘
      console.log(item)
      this.formData['num' + this.activeKeyWordIndex] = item

      if (this.formData.commonCard === '1') {
        this.activeKeyWordIndex++
        if (this.activeKeyWordIndex > 6) {
          this.keyBoardStatus = false
          this.submitFn()
        }
      } else {
        this.activeKeyWordIndex++
        if (this.activeKeyWordIndex > 7) {
          this.keyBoardStatus = false
          this.submitFn()
        }
      }
    },
    deleteWord() {
      // 退格
      // console.log(this.activeKeyWordIndex)
      // console.log(this.formData['num' + (this.activeKeyWordIndex - 1)])
      if (this.activeKeyWordIndex > 1) {
        this.formData['num' + (this.activeKeyWordIndex - 1)] = ''
        this.activeKeyWordIndex--
      }
    },
    clickKeyWordWrap(activeKeyWordIndex) {
      console.log(activeKeyWordIndex)
      this.keyBoardStatus = true
      this.activeKeyWordIndex = activeKeyWordIndex
      this.formData['num' + this.activeKeyWordIndex] = ''

      this.isActive = activeKeyWordIndex
    },
    submitFn() {
      let plateLicense
      if (this.formData.commonCard === '1') {
        plateLicense = this.plate_license_1
        plateLicense = this.palindrome(plateLicense)
        if (plateLicense.length < 7) {
          // alert('请输入正确的车牌号')
          this.$toast('请输入正确的车牌号')
          return
        }
      }
      if (this.formData.commonCard === '2') {
        plateLicense = this.plate_license_2
        plateLicense = this.palindrome(plateLicense)
        if (plateLicense.length < 8) {
          // alert('请输入正确的车牌号')
           this.$toast('请输入正确的车牌号')
          return
        }
      }
      this.$emit('getPlateLicense', plateLicense)
      console.log(plateLicense)
      // alert(plateLicense)
    },
    palindrome(str) {
      var arr = str.split('')
      arr = arr.filter(function (val) {
        return (
          val !== ' ' &&
          val !== ',' &&
          val !== '.' &&
          val !== '?' &&
          val !== ':' &&
          val !== ';' &&
          val !== '`' &&
          val !== "'" &&
          val !== '_' &&
          val !== '/' &&
          val !== '-' &&
          val !== '\\' &&
          val !== '' &&
          val !== '(' &&
          val !== ')'
        )
      })
      return arr.join('')
    },
    checkIsHasSpecialStr(str) {
      var flag = false
      var arr = str.split('')
      arr.forEach(val => {
        if (
          val === '!' ||
          val === '}' ||
          val === '{' ||
          val === ']' ||
          val === '[' ||
          val === '&' ||
          val === '$' ||
          val === '@' ||
          val === ' ' ||
          val === ',' ||
          val === '.' ||
          val === '?' ||
          val === ':' ||
          val === ';' ||
          val === '`' ||
          val === "'" ||
          val === '_' ||
          val === '/' ||
          val === '-' ||
          val === '\\' ||
          val === '' ||
          val === '(' ||
          val === ')'
        ) {
          flag = true
        }
      })
      return flag
    },
    checkIsHasChineseStr(str) {
      var Reg = /.*[\u4e00-\u9fa5]+.*/
      if (Reg.test(str)) {
        return true
      }
      return false
    }
  },
  computed: {
    plate_license_1() {
      return (
        this.formData.num0 +
        this.formData.num1 +
        this.formData.num2 +
        this.formData.num3 +
        this.formData.num4 +
        this.formData.num5 +
        this.formData.num6
      )
    },
    plate_license_2() {
      return (
        this.formData.num0 +
        this.formData.num1 +
        this.formData.num2 +
        this.formData.num3 +
        this.formData.num4 +
        this.formData.num5 +
        this.formData.num6 +
        this.formData.num7
      )
    }
  }
}
</script>
<style lang="scss" scoped>
/deep/.van-tabs__line {
    background-color: #1989fa;
}
/deep/.van-button--normal {
    padding: 0;
}
.plateNumber {
  // font-size: 24px;
}
.flex-items-center {
  display: flex;
  align-items: center;
}
@-webkit-keyframes active {
  0% {
    opacity: 0.8;
  }
  50% {
    opacity: 0.4;
  }
  100% {
    opacity: 0.8;
  }
}

@keyframes active {
  0% {
    opacity: 0.8;
  }
  50% {
    opacity: 0.4;
  }
  100% {
    opacity: 0.8;
  }
}

// .active {
//   -webkit-animation: active 2.2s infinite;
//   animation: active 2.2s infinite;
//   background-color: #1989fa;
// }
.wrap {
  height: 11.1rem;
  padding: 0.85rem 0.6rem;
  background-color: #fff;
  border-radius: 0.5rem;
  // input输入框
  .num-box {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 1rem;
    .spot {
      width: 0.1rem;
      height: 0.1rem;
      border-radius: 50%;
      background-color: #d8d8d8;
    }
    & > div {
      width: 1rem;
      height: 1rem;
      border: 1px solid #e4e4e4;
      &.first {
        position: relative;
        text-align: center;
        line-height: 1.7rem;
        font-weight: 200;
        .input-wrap {
          position: absolute;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          &.active {
            z-index: 100;
          }
        }
        em {
          color: #979797;
          // font-size: 1.6rem;
          line-height: 1.7rem;
        }
        span {
          display: inline-block;
          width: 100%;
          height: 100%;
          // background-color: #9cbce2;
          color: #000;
          line-height: 1rem;
        }
      }
      &.active {
        border: 1px solid #4a90e2;
        &:after {
          border-bottom: 0.5rem solid #4a90e2;
        }
      }
      span {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        // font-size: 1rem;
        color: #000;
        &.first {
          background-color: #9cbce2;
          color: #fff;
          text-indent: 0.4rem;
          border-radius: 0;
        }
      }
    }
  }

  .info {
    // font-size: 0.5rem;
    margin-top: 0.9rem;
    color: #000;
    text-align: left;
    img {
      width: 0.6rem;
      vertical-align: middle;
    }
  }
}
.first-word-wrap {
  // height: 9.4rem;
  background-color: #e0e0e0;
  padding: 0.4rem 0.4rem 0.6rem;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  .first-word {
    display: flex;
    justify-content: space-between;
    margin-bottom: 0.45rem;
    .word {
      box-sizing: border-box;
      width: 1rem;
      height: 1rem;
      // border: 1px solid #9cbce2;
      box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.35);
      border-radius: 0.16rem;
      text-align: center;
      &.bordernone {
        border: none;
        box-shadow: none;
      }
      span {
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        width: 100%;
        height: 100%;
        background-color: #fff;
        color: #000;
        // border: 1px solid #fff;
        border-radius: 0.125rem;
      }
      img {
        width: 1.6rem;
      }
    }
    &:nth-last-of-type(1) {
      margin-bottom: 0rem;
    }
  }
}
.keyboard-wrap {
  background-color: #e0e0e0;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 0.7rem 0.6rem 0.4rem;
  .keyboard {
    display: flex;
    justify-content: space-between;
    align-items: center;
    height: 1rem;
    margin-bottom: 0.3rem;
    span {
      text-align: center;
      display: flex;
      width: 1rem;
      align-items: center;
      justify-content: center;
      height: 1rem;
      box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.35);
      background-color: #fff;
      border-radius: 0.125rem;
      &:active {
        background-color: #e4e4e4;
      }
      &.bordernone {
        border: none;
        box-shadow: none;
        background-color: #e0e0e0;
        &:active {
          background-color: #e0e0e0;
        }
      }
      &.delete {
        background-image: linear-gradient(to left, #25aae1, #1989fa, #25aae1);
        img {
          width: 1.15rem;
        }
      }
    }
  }
  .cancel {
    display: flex;
    justify-content: space-between;
    align-items: center;
    // margin-top: -1.25rem;
    span {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 3.6rem;
      height: 1rem;
      background-image: linear-gradient(to left, #25aae1, #1989fa, #25aae1);
      color: #fff;
      border-radius: 0.125rem;
    }
  }
}
</style>

 5.不用插件的舶来品

  

  

posted @   lzhflzjx  阅读(1448)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示