1. 程式人生 > 其它 >Vue獲取掃碼槍輸入內容(可一定程度無視輸入法)

Vue獲取掃碼槍輸入內容(可一定程度無視輸入法)

技術標籤:開發例項vuejsjavascript

Vue獲取掃碼槍輸入內容(可一定程度無視輸入法)

背景

WMS專案需要使用掃碼槍,但當我輸入法為中文輸入法時,掃碼槍輸入會出現中文或者缺字母少數字的情況,嘗試了不少方法,最終於找到了一種比較合適的解決辦法。

環境

名稱內容
語言Vue、ElementUI、Js
掃碼槍型號OBM-3802

前提

經過不停的掃碼和觀察,發現了一點掃碼槍的規律

  1. 掃碼槍掃描之後模擬了keyboardEvent鍵盤輸入(廢話
  2. 掃碼槍掃描到大寫字母時,在傳送key為該字母的keyboardEvent事件後,還會附帶一個key為shift的keyboardEvent事件(這個事件在keydown裡面監聽不到),但是輸入小寫字母時不會附帶這個事件
  3. 掃碼槍模擬的事件很快,單個事件基本低於20~30毫秒,有的只要幾毫秒
  4. 當中文輸入法開啟後,在字母的keyboardEvent事件前都會帶一個key為Process的keyboardEvent事件(這個事件在keydown裡面是監聽不到)(這很重要)

方案

基於上述前提,我在此有兩種比較好的解決方案

一、隱藏密碼框法

眾所周知,密碼框是無視輸入法的,管你輸的什麼,只要不是特殊的按鍵,一律小黑點

那我是不是可以整一個密碼框,放在一個文字框下面?當然可以

程式碼

<template>
  <div>
    <el-form>
      <el-form-item label="輸入條碼">
        <el-input v-model="barcode" type="password" ref="barcode"></el-input>
        <el-input v-model="barcode"
type="text" placeholder="輸入條碼" readonly="readonly" @focus="handleFocus" style="top:-36px;"></el-input> </el-form-item> </el-form> </div> </template> <script> export default { name: "Barcodescan", data() { return { barcode: undefined } }, methods: { handleFocus() { this.$refs.barcode.select(); } } } </script>

測試

好了,就這麼簡單,每次只要點選文字框,然後隨便你怎麼掃,都是對的,如果你需要重新掃,只需要再點一下文字框即可。然後就是無盡的調樣式之路。
在這裡插入圖片描述

優點

簡單

缺點

在你以為問題解決了的時候,作為一個成熟的程式設計師,你該想到,產品經理會問:我點了怎麼沒有選中樣式?我的滑鼠游標不在裡面,客戶會覺得自己不能掃碼,我的游標呢?為什麼這個框不能選中?客戶想複製怎麼辦?

結果

本人半路出家vue,雖然是個全棧,但是學藝不精,調了一上午樣式,一臉頭大的我,實在不知道游標怎麼模擬(這字母、數字、中文、符號長度都不一致太難搞了,我弄成長度一致了,小黑點又只能調字型大小,小黑點太大了,我的滑鼠游標也會跟著變大,凸(⊙▂⊙ )),網上也沒有找到大佬搞這個,然後果斷放棄了方案一。

二、輸入時間間隔法

根據掃碼槍輸的快,外加WMS倉庫人員輸條碼應該不會很快,再加個輸入法會有Process鍵盤事件這幾個特性。我有了個想法,要不不管輸入框的內容,他愛咋樣咋樣,我自己記keyboardEvent事件的輸入值好了。

程式碼

<template>
  <div>
    <!-- 阻止掃碼槍的Enter事件自動提交el-form的提交特性 -->
    <el-form @submit.native.prevent>
      <el-form-item label="輸入條碼">
        <el-input v-model="barcode" type="text" placeholder="輸入條碼" @keyup.native="handleKeyUp"></el-input>
      </el-form-item>
    </el-form>
  </div>
</template>
<script>
let barcodeVue = {
  name: "Barcodescan",
  data() {
    return {
      barcode: "",
      realBarcode: "",
      keyupLastTime: undefined,
    };
  },
  methods: {
    // 處理keyup事件
    handleKeyUp(e) {
      let gap = 0;
      if (this.keyupLastTime) {
        gap = new Date().getTime() - this.keyupLastTime;
        if (gap > 50) {
          gap = 0;
          this.realBarcode = "";
        }
      }
      this.keyupLastTime = new Date().getTime();
      // 輸入法會觸發keyup事件,key為Process,跳過即可
      if (e.key != "Process" && gap < 50) {
        if (e.key.trim().length == 1) {
          // 輸入單個字母或者數字
          this.realBarcode += e.key;
        } else if (e.key.trim() == "Enter") {
          // 根據規則,判斷barcode型別,返回資料(自定義規則)
          if (this.realBarcode) {
            this.barcode = this.realBarcode;
            this.realBarcode = "";
          }else{
            return;
          }
        }
      }
    }
  }
};

export default { ...barcodeVue };
</script>

測試

在這裡插入圖片描述

優點

基本考慮了人正常的行為方式去開發的一個元件,當然體驗下來肯定是比方案一要好很多的,起碼focus之後框顏色會變,重要的是還有游標了。只要對方拿的是掃碼槍,不是掃碼機關槍,或者遇上人形掃碼槍,基本不會出什麼問題。

不足

儘管我們覺得體驗好了很多。但是作為一個成熟的程式設計師,應該會想到產品經理會說:你這個框還要點一下才能輸入啊,一點都不智慧!如果我有多個碼怎麼辦?就不能我開著頁面隨便掃,然後智慧的去判斷是什麼碼呢?
okokok,那我們在這個方案二的基礎上再改良一下。

結果

接著幹

三、頁面事件監聽+輸入事件間隔法

在方案二的基礎上,我決定從created執行盤古開天闢地之時就開始監聽事件,然後再根據自定義的業務規則去判斷掃碼的型別,最後根據門店門稱對比來確定本地還是其他門店單子。

程式碼

呼叫元件方

<template>
  <div style="height:800px;width:800px;border:1px solid;padding:20px;margin:0 auto;margin-top:20px;">
    <p style="text-align:center;">測試區域</p>
    <barcodescan shopName="GZ1" @handle="handleBarcode"></barcodescan>
    {{value}}
  </div>
</template>

<script>
import Barcodescan from './components/Barcodescan'

export default {
  name: 'Index',
  components: {
    Barcodescan
  },
  data() {
    return {
      value: undefined
    }
  },
  methods: {
    handleBarcode(barcodeMap) {
      this.value = barcodeMap;
    }
  }
}
</script>

元件方

<template>
  <div>
  </div>
</template>
<script>
let barcodeVue = {
  name: "Barcodescan",
  props: {
    shopName: {
      type: String
    }
  },
  data() {
    return {
      realBarcode: "",
      keyupLastTime: undefined,
      name: undefined,
      regexRules: []
    };
  },
  created() {
    let that = this;
    // 監聽頁面的keyup事件
    document.onkeyup = function (e) {
      that.handleKeyUp(e);
    };
    this.name = this.shopName;
    this.initRegexRules();
  },
  methods: {
    // 初始化條碼規則(自定義)
    initRegexRules() {
      this.regexRules = [
        {
          regex: "/^IN(\\w|\\d)+$/",
          value: "putInStorageNumber"
        },
        {
          regex: "/^CH(\\w|\\d)+$/",
          value: "checkNumber"
        },
        {
          regex: "/^AL(\\w|\\d)+$/",
          value: "allocateNumber"
        },
        {
          regex: "/^\\d{12}$/",
          value: "orderNumber"
        },
        {
          regex: "/^SR(\\w|\\d)+$/",
          value: "transferNumber"
        },
        {
          regex: "/^\\d{12}-\\d{3}$/",
          value: "sendNo"
        },
        {
          regex: "/^PL\\d{10}$/",
          value: "wavePickingNumber"
        },
        {
          regex: "/^PL\\d{10}-\\d{3}$/",
          value: "wavePickingGroupNumber"
        },
        {
          regex: "/^(\\w|\\d)*-[\\w|\\d]*-\\d*-[A-Z]-\\d*/",
          value: "location"
        }
      ]
    },
    // 處理keyup事件
    handleKeyUp(e) {
      let gap = 0;
      if (this.keyupLastTime) {
        gap = new Date().getTime() - this.keyupLastTime;
        if (gap > 50) {
          gap = 0;
          this.realBarcode = "";
        }
      }
      this.keyupLastTime = new Date().getTime();
      // 輸入法會觸發keyup事件,key為Process,跳過即可
      if (e.key != "Process" && gap < 50) {
        if (e.key.trim().length == 1) {
          // 輸入單個字母或者數字
          this.realBarcode += e.key;
        } else if (e.key.trim() == "Enter") {
          // 根據規則,判斷barcode型別,返回資料(自定義規則)
          if (this.realBarcode) {
            let data = {
              type: this.barcodeRule(this.realBarcode),
              code: this.realBarcode,
              isLocal: this.isLocal(this.realBarcode)
            };
            this.$emit('handle',data);
            this.realBarcode = "";
          }
        }
      }
    },
    // 判斷條碼型別,如果沒找到,則返回型別為barCode
    barcodeRule(barcode) {
      let value;
      this.regexRules.some((item,index)=>{
         let regex = eval(item.regex);
         if(regex.test(barcode)){
           value = item.value;
           return true;
         }
      })
      return value?value:"barCode";
    },
    // 根據條碼是否包含門店名,判斷是否本地條碼
    isLocal(barcode) {
      return this.name?barcode.indexOf(this.name)!=-1:undefined;
    }
  },
};

export default { ...barcodeVue };
</script>

測試

我在呼叫時設定的shopName為GZ1,表示本地門店名為GZ1
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

優點

頁面只需要設定@handle的方法,即可對掃描的內容去賦值、渲染,或者做什麼奇怪的操作都可以,最關鍵的是頁面只需要加一個標籤就行。隨便掃,隨便用。

不足

作為一個成熟的產品經理,差不多得了。

結果

就這樣吧