前言 今天乘着休息来总结下之前用的到自定义键盘来输入车辆号牌微信组件。下面是效果图,请欣赏: 背景 近期做了一个和车有关的项目,有车肯定就有车牌,我们都知道车牌是有一定规律的,如果简单的给个输入框的话这里省略一万字哈,从小我的编程老师告诉我不要相信任何用户输入的东西。嗯嗯!现在想想还真的是这样,总有一些别具一格的用户就喜欢反着来,也不知道是真不懂还是搞破坏哈。还有个老师告诉我永远要把用户当SB,虽然自己也是用户,感觉不好,但是话糙理不糙啊,因为你永远不知道使用你写的东西的是什么(甚至是不是人)。在这样的背景下你说要让用户输入一个正确的车牌号,那可真是比登天还难啊,太南了,臣妾做不到啊。大猜想 既然这样哈,我们自己也来当一回用户看看大概有什么情况发生哈,来咯,不要走开哦,下面就是发挥用户脑洞的时候了。我a。12345鄂a。ii111Xa。6666666za。哈哈哈哈哈 哇哦,估计写一天都写不完呢,后面看大家的啦,这里有一个前提就是咱不考虑以英文开头的车牌,如果需要考虑大家可以举一反三啦。找规律 其实没啥规律可找的,直接上百度一搜就知道车牌的规则。前面是省份简称,第二位是字母,后面是字母和数字的混合,这里有几个特殊的就是教练车、港澳车进大陆、领事馆的车。另外字母和字母o和数字1和0不好辨认,所以字母i和字母o去掉了,最后规律就有了。结构和样式 知道规律后就可以开始动手操作了,先看看设计图的键盘布局: 上面图片是省份简称键盘布局 上面图片是第二位字母键盘布局 上面图片是后面几位的键盘布局 下面就根据这几个布局来写结构了:viewclasskeyboardwx:if{{isShow}}viewclassitemviewwx:for{{dataArr1}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemviewwx:for{{dataArr2}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemviewwx:for{{dataArr3}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemwx:if{{dataArr4。length}}viewwx:for{{dataArr4}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassdelbtnbindtapdelKeywx:if{{keyTypecarlicense}}imagesrca2020imgdataimg。jpgdatasrc。icondel。pngimageviewviewviewclassmaskbindtaphideKeyBoardwx:if{{isShow}}view 分别把数据放入四个数组中,由于第二位少了一些,所以干脆少一行,还有删除的按钮,以及最后的遮罩层,用于点击其他地方关闭键盘 接下来先加几个测试的数据把基本的颜色写好:data:{dataArr1:〔京,沪,粤,津,冀,晋,辽,蒙,黑,吉〕,dataArr2:〔苏,浙,皖,闽,赣,鲁,豫,鄂,湘〕,dataArr3:〔川,贵,云,渝,桂,琼,藏,陕〕,dataArr4:〔甘,青,宁,新,台〕} 这里以输入省份的为例 接下来就是把我们的样式添加上去就可以看到效果了:。keyboard{position:left:0;bottom:0;zindex:99999;width:100;background:d1d6d9;padding:20rpx10rpx0;paddingbottom:calc(30pxenv(safeareainsetbottom)2);boxsizing:}。keyboard。item{display:alignitems:justifycontent:marginbottom:20}。keyboard。itemview{width:9;height:70lineheight:70textalign:marginright:10background:borderradius:10fontsize:28}。keyboard。itemview:lastchild{marginright:0;}。keyboard。delbtn{position:right:10bottom:calc(40pxenv(safeareainsetbottom)2);height:70width:18;zindex:999999;background:adb3borderradius:10display:alignitems:justifycontent:}。keyboard。delbtnimage{width:60height:60}。mask{position:left:0;top:113bottom:0;right:0;zindex:99998;} 这里样式就不多说了,大家可以按照自己的喜好来就行 现在已经可以看到效果了,如图: 组件实现 组件的实现其实很简单,主要需要知道微信组件的用法就ok。参数 实现之前来看看需要什么参数,由于是键盘肯定是需要显示和隐藏的,所以需要一个显示和隐藏的参数这里就命名为isShow。再看我们的样式如果是输入第二位或者后面的呢是不是之前定义的几个数组数据就得改变,问题来了什么时候改变呢?是不是应该在点击输入的地方就打开这个时候就知道了是输入省份还是输入第二位还是后面的,所以这里需要有一个参数来表示当前是需要显示什么类型的键盘暂命名为keyType。到这里参数有了,再来看看是否需要设置默认值是啥,对于isShow来说开始肯定是false即为不显示的,至于keyType默认那个都行,这里默认值设为省份的。这里的参数其实就是需要调用组件时传递过来的参数,所以这两个参数就是设为组件的属性。 有了上面的分析,组件的大概就出来了,代码如下:Component({properties:{keyType:{type:String,value:province},isShow:{type:Boolean,value:false}},data:{dataArr1:〔京,沪,粤,津,冀,晋,辽,蒙,黑,吉〕,dataArr2:〔苏,浙,皖,闽,赣,鲁,豫,鄂,湘〕,dataArr3:〔川,贵,云,渝,桂,琼,藏,陕〕,dataArr4:〔甘,青,宁,新,台〕}})键盘类型的判断 ok,上面已经把键盘所需参数考虑好了,接下来就是需要根据传入的keyType来切换不同的键盘数组的值了。其实就是在组件方法里面写一个方法来切换就行了,实现如下:Component({properties:{keyType:{type:String,value:province},isShow:{type:Boolean,value:false}},data:{dataArr1:〔〕,dataArr2:〔〕,dataArr3:〔〕,dataArr4:〔〕},methods:{changeType(){if(this。data。keyTypeletter){this。setData({dataArr1:〔Q,W,E,R,T,Y,U,P,N,M〕,dataArr2:〔A,S,D,F,G,H,J,K,L〕,dataArr3:〔Z,X,C,V,B〕,dataArr4:〔〕})}elseif(this。data。keyTypeprovince){this。setData({dataArr1:〔京,沪,粤,津,冀,晋,辽,蒙,黑,吉〕,dataArr2:〔苏,浙,皖,闽,赣,鲁,豫,鄂,湘〕,dataArr3:〔川,贵,云,渝,桂,琼,藏,陕〕,dataArr4:〔甘,青,宁,新,台〕})}elseif(this。data。keyTypecarlicense){this。setData({dataArr1:〔1,2,3,4,5,6,7,8,9,0〕,dataArr2:〔A,B,C,D,E,F,G,H,J,K〕,dataArr3:〔L,M,N,P,Q,R,S,T,U,V〕,dataArr4:〔W,X,Y,Z,港,澳,学,领〕})}}}}) 切的方法有了,现在就是在初始化的时候调用这个方法就行了,修改代码如下:Component({properties:{keyType:{type:String,value:province},isShow:{type:Boolean,value:false}},data:{dataArr1:〔〕,dataArr2:〔〕,dataArr3:〔〕,dataArr4:〔〕},attached(){this。changeType()},methods:{changeType(){if(this。data。keyTypeletter){this。setData({dataArr1:〔Q,W,E,R,T,Y,U,P,N,M〕,dataArr2:〔A,S,D,F,G,H,J,K,L〕,dataArr3:〔Z,X,C,V,B〕,dataArr4:〔〕})}elseif(this。data。keyTypeprovince){this。setData({dataArr1:〔京,沪,粤,津,冀,晋,辽,蒙,黑,吉〕,dataArr2:〔苏,浙,皖,闽,赣,鲁,豫,鄂,湘〕,dataArr3:〔川,贵,云,渝,桂,琼,藏,陕〕,dataArr4:〔甘,青,宁,新,台〕})}elseif(this。data。keyTypecarlicense){this。setData({dataArr1:〔1,2,3,4,5,6,7,8,9,0〕,dataArr2:〔A,B,C,D,E,F,G,H,J,K〕,dataArr3:〔L,M,N,P,Q,R,S,T,U,V〕,dataArr4:〔W,X,Y,Z,港,澳,学,领〕})}}}}) 到这里组件的基本功能已经完成了,但是当我们点击键盘上的内容的时候,你会发现没有反应,嗯嗯这里好像漏了一个东西,既然是键盘肯定是需要得到输入结果的啦。获取输入内容 要获取键盘输入的内容就需要给键盘的每个按键新增事件来获取输入内容,将之前的结构中新增事件,具体可参见文章第一段代码,具体的实现代码如下:methods:{inputKey(e){console。log(e。currentTarget。dataset。value)},delKey(e){console。log(e。currentTarget。dataset。value)},hideKeyBoard(){this。setData({isShow:false})}} 这里将删除和关闭键盘的时间也添加上了,通过以上两个方法就能获取输入的值了。这时当我们点击某个字符时就会看到控制面板里打印出刚刚选择的值了。 可是我需要不是打印出值啊,是需要将值显示在页面上才行,这里就需要将组建获取到的值传递给调用改组件的页面了,如何传递给调用该组件的页面呢?组件传参 这里咱们就可以去微信小程序的官方文档看看了,毕竟官方文档是个好东西嘛,找到下图所示的地方: 我们这里用到的就是组件通信的第二点事件,可以传递任意数据的,那么根据官方文档的介绍我相信大家一看就知道怎么使用了,代码如下:methods:{inputKey(e){this。triggerEvent(inputword,{type:input,value:e。currentTarget。dataset。value})},delKey(e){this。triggerEvent(inputword,{type:del})},hideKeyBoard(){this。setData({isShow:false})this。triggerEvent(inputword,{type:blur})}} 这里呢是将所以的操作都通过一个事件来传递数据,通过增加一个type来区分到底是输入、删除还是关闭键盘。组件使用 嗯嗯,不容易啊,终于到了使用组件的时候了,这里根据微信组件的用法如下操作:在页面配置文件中引入组件地址在页面中添加组件的标签在添加的标签上面添加需要传递的参数和事件 相关代码如下:{navigationBarTitleText:新增车辆,usingComponents:{keyboard:。。。。componentskeyboardindex}} 在需要用到改组件的页面的配置文件中加入上面带代码viewclassbindcarviewclasscarprovince{{focusProvince?active:}}viewbindtapchooseProvinceCn{{provinceCn}}viewviewbindtapchooseProvinceCode{{provinceCode}}viewviewviewclasscarnumber{{focusCode?active:}}bindtapchooseCarCode{{carCode}}viewviewkeyboardbindinputwordinputWordkeyType{{setKeyType}}isShow{{showKeyBoard}}keyboard 在使用到的页面中加入上面最后一行的代码,并添加上相信的参数和事件 下一步就是在页面的js中来处理相应的参数和事件,具体代码如下:Page({data:{focusProvince:false,focusCode:false,setKeyType:province,showKeyBoard:false,keyIndex:0,provinceCn:,provinceCode:,carCode:},inputWord(e){if(e。detail。typeinput){if(this。data。keyIndex0){this。setData({provinceCn:e。detail。value})}elseif(this。data。keyIndex1){this。setData({provinceCode:e。detail。value})}elseif(this。data。keyIndex2){if(this。data。carCode。length6){this。setData({carCode:this。data。carCodee。detail。value})}}}elseif(e。detail。typedel){if(this。data。carCode){this。setData({carCode:this。data。carCode。substr(0,this。data。carCode。length1)})}}elseif(e。detail。typeblur){this。setData({focusCode:false,focusProvince:false})}},chooseProvinceCn(){this。setData({setKeyType:province,showKeyBoard:true,keyIndex:0,focusProvince:true,focusCode:false})},chooseProvinceCode(){this。setData({setKeyType:letter,showKeyBoard:true,keyIndex:1,focusProvince:true,focusCode:false})},chooseCarCode(){this。setData({setKeyType:carlicense,showKeyBoard:true,keyIndex:2,focusCode:true,focusProvince:false})}}) 页面中的inputWord就是我们最终需要处理的事件,另外几个是输入后控制焦点并显示相应的红色方框的开关。测试 这一步就是程序小哥哥小姐姐比较害怕的,测试的小哥哥小姐姐就那咋自己写的代码不停的乱搞一通,哈哈哈,最后骨头里挑刺。这里呢当我们分别选择不同输入的type时会发现键盘的内容没有改变,嗯嗯,原来还是写了个bug给自己啊。解决键盘类型判断的bug 通过上面自己简单的测试后发现有个bug需要去修复,其实这个问题的点在于每次去显示键盘时传递给组件的keyType有缓存导致的,所以需要再组件中属性声明的地方来个监听变化咯,说干就干,改好了马上下班,代码如下:Component({properties:{keyType:{type:String,value:letter,observer:function(newVal,oldVal){this。changeType()}},isShow:{type:Boolean,value:false}}}) 在属性中新增observer的监听函数,当有变化的时候就去调用判断类型的函数就可以了。这样就可以愉快的使用。最后打卡,背包走人咯等等后面还有呢结束语 目前这个组件也只是为了完成需求而写的,可能实际使用过程中还是会有很多问题的,欢迎大家提出哦。最后放上每个文件完整的代码哦组件的代码WXMLviewclasskeyboardwx:if{{isShow}}viewclassitemviewwx:for{{dataArr1}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemviewwx:for{{dataArr2}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemviewwx:for{{dataArr3}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassitemwx:if{{dataArr4。length}}viewwx:for{{dataArr4}}wx:keyuniquebindtapinputKeydatavalue{{item}}{{item}}viewviewviewclassdelbtnbindtapdelKeywx:if{{keyTypecarlicense}}imagesrca2020imgdataimg。jpgdatasrc。icondel。pngimageviewviewviewclassmaskbindtaphideKeyBoardwx:if{{isShow}}viewWXSS。keyboard{position:left:0;bottom:0;zindex:99999;width:100;background:d1d6d9;padding:20rpx10rpx0;paddingbottom:calc(30pxenv(safeareainsetbottom)2);boxsizing:}。keyboard。item{display:alignitems:justifycontent:marginbottom:20}。keyboard。itemview{width:9;height:70lineheight:70textalign:marginright:10background:borderradius:10fontsize:28}。keyboard。itemview:lastchild{marginright:0;}。keyboard。delbtn{position:right:10bottom:calc(40pxenv(safeareainsetbottom)2);height:70width:18;zindex:999999;background:adb3borderradius:10display:alignitems:justifycontent:}。keyboard。delbtnimage{width:60height:60}。mask{position:left:0;top:113bottom:0;right:0;zindex:99998;}JSComponent({properties:{keyType:{type:String,value:letter,observer:function(newVal,oldVal){this。changeType()}},isShow:{type:Boolean,value:false}},data:{dataArr1:〔〕,dataArr2:〔〕,dataArr3:〔〕,dataArr4:〔〕},attached(){this。changeType()},methods:{changeType(){if(this。data。keyTypeletter){this。setData({dataArr1:〔Q,W,E,R,T,Y,U,P,N,M〕,dataArr2:〔A,S,D,F,G,H,J,K,L〕,dataArr3:〔Z,X,C,V,B〕,dataArr4:〔〕})}elseif(this。data。keyTypeprovince){this。setData({dataArr1:〔京,沪,粤,津,冀,晋,辽,蒙,黑,吉〕,dataArr2:〔苏,浙,皖,闽,赣,鲁,豫,鄂,湘〕,dataArr3:〔川,贵,云,渝,桂,琼,藏,陕〕,dataArr4:〔甘,青,宁,新,台〕})}elseif(this。data。keyTypecarlicense){this。setData({dataArr1:〔1,2,3,4,5,6,7,8,9,0〕,dataArr2:〔A,B,C,D,E,F,G,H,J,K〕,dataArr3:〔L,M,N,P,Q,R,S,T,U,V〕,dataArr4:〔W,X,Y,Z,港,澳,学,领〕})}},inputKey(e){this。triggerEvent(inputword,{type:input,value:e。currentTarget。dataset。value})},delKey(e){this。triggerEvent(inputword,{type:del})},hideKeyBoard(){this。setData({isShow:false})this。triggerEvent(inputword,{type:blur})}}})使用页面代码JSON{navigationBarTitleText:新增车辆,usingComponents:{keyboard:。。。。componentskeyboardindex}}WXMLviewclassbindcarviewclasscarprovince{{focusProvince?active:}}viewbindtapchooseProvinceCn{{provinceCn}}viewviewbindtapchooseProvinceCode{{provinceCode}}viewviewviewclasscarnumber{{focusCode?active:}}bindtapchooseCarCode{{carCode}}viewviewkeyboardbindinputwordinputWordkeyType{{setKeyType}}isShow{{showKeyBoard}}keyboardWXSS。bindcar{display:alignitems:background:FFFFFF;padding:25rpx30}。bindcar。carprovince{marginright:30width:170height:88border:1rpxsolid999999;borderradius:4display:}。bindcar。carprovinceview{width:86height:88lineheight:88textalign:fontsize:34fontweight:500;color:333333;}。bindcar。carprovinceview:firstchild{position:}。bindcar。carprovinceview:firstchild::after{content:;position:right:0;top:50;width:1height:40background:999999;margintop:20}。bindcar。carprovince。active{bordercolor:FF5152;}。bindcar。carprovince。activeview:firstchild::after{background:E83333;}。carnumber{width:100;height:88lineheight:88border:1rpxsolid999999;borderradius:4fontsize:34fontweight:500;color:333333;textalign:}。carnumber。active{bordercolor:E83333;}JSPage({data:{focusProvince:false,focusCode:false,setKeyType:province,showKeyBoard:false,keyIndex:0,provinceCn:,provinceCode:,carCode:},inputWord(e){if(e。detail。typeinput){if(this。data。keyIndex0){this。setData({provinceCn:e。detail。value})}elseif(this。data。keyIndex1){this。setData({provinceCode:e。detail。value})}elseif(this。data。keyIndex2){if(this。data。carCode。length6){this。setData({carCode:this。data。carCodee。detail。value})}}}elseif(e。detail。typedel){if(this。data。carCode){this。setData({carCode:this。data。carCode。substr(0,this。data。carCode。length1)})}}elseif(e。detail。typeblur){this。setData({focusCode:false,focusProvince:false})}},chooseProvinceCn(){this。setData({setKeyType:province,showKeyBoard:true,keyIndex:0,focusProvince:true,focusCode:false})},chooseProvinceCode(){this。setData({setKeyType:letter,showKeyBoard:true,keyIndex:1,focusProvince:true,focusCode:false})},chooseCarCode(){this。setData({setKeyType:carlicense,showKeyBoard:true,keyIndex:2,focusCode:true,focusProvince:false})}})