首页 / 资讯中心 / 文章详情

微信小程序车牌输入组件:规则、自定义键盘与校验实现

微信小程序车牌输入组件:规则、自定义键盘与校验实现 ★ FEATURED ARTICLE
我做过好几个跟车相关的微信小程序停车缴费、洗车预约、加油开票绕来绕去都躲不开一个需求让用户把车牌号码输进来。很多刚上手的朋友第一反应是放个输入框不就行了真做起来才发现事情没那么简单。车牌里夹着汉字省份简称第二位是字母后面几位字母数字混着来新能源车牌还多一位再加上大小写、易混字母、位数判断这些问题用户输错一个字符后面的订单、缴费、查询全卡住。这个输入车牌号码的功能看起来是个小模块但它直接决定了整个流程能不能顺畅跑通也是很多汽车后市场类小程序绕不开的基础能力。这篇文章我打算把整套实现思路摊开讲一遍从车牌的编码规则、自定义键盘的设计、输入校验的写法到组件封装和实际踩过的坑。不管你是刚开始接触小程序开发还是已经做过几个项目想把这套东西沉淀成组件应该都能拿到能直接抄的东西。1. 车牌号码的底层编码规则与输入场景拆解1.1 普通车牌和新能源车牌的结构差异车牌这东西表面看就是几个字符实际上有一套很严格的编码逻辑你不把规则吃透校验就写不对用户也就永远在输错和改错之间来回折腾。先看普通燃油车牌一共七位第一位是省份简称是一个汉字常见的比如京、津、沪、粤、川、浙这些全国省级行政区简称加起来三十多个第二位是发牌机关代号是一个英文字母代表该省下面的具体城市或区域比如粤B是深圳、粤A是广州、京A是北京市区第三到第七位一共五位由字母和数字混合组成而且字母里明确规定不含 I 和 O。这一点特别关键因为 I 和 1、O 和 0 在视觉上极容易混相关部门干脆从编码字符集里把它俩去掉了。你写键盘的时候如果不把这些字母剔除用户点出来的本身就是非法字符。再看新能源车牌它是八位。第一位还是省份简称第二位还是发牌机关代号字母区别在第三位和后续小型新能源车第三位是 D 或 F大型新能源车则在第八位是 D 或 FD 代表纯电动F 代表非纯电动也就是混动。也就是说同样是粤B开头你光看前两位根本判断不出是普通牌还是新能源牌必须结合位数和第三位的字符一起判断。这就引出一个问题——用户输入的过程中你什么时候知道他到底要输七位还是八位我的做法是动态判断当用户输完前两位、准备输第三位的时候看第三位是不是 D 或 F如果是就按八位新能源处理否则按七位普通处理。这样键盘位数和校验规则可以自适应切换用户不用自己去选类型。还有一类特殊车牌比如挂车、警车、学车、港澳入出境的车辆它们的末位或者特定位置会有挂学警港澳这样的汉字。这些在普通民用场景里碰得少但你要是做的是货运、驾校、物流类小程序就必须考虑进去。我个人的经验是普通业务场景停车、洗车、加油只需要支持普通牌和新能源牌特殊号段可以放到配置里按需开启没必要一上来就把所有情况都堆进去键盘会变得特别臃肿。1.2 从真实使用场景倒推用户为什么会输错我做停车缴费的时候专门统计过一批用户行为输错车牌的原因其实很集中。第一大类是字母数字混淆尤其是把数字键上的字母漏掉比如把粤B输成粤8因为不少人对发牌机关代号是字母这件事没概念。第二大类是新能源位数搞不清七位输完了才发现还要再输一位。第三大类是省份简称记不住或打不对用户心里想的是我们这是鲁结果选了豫两个字形近。第四大类是大小写问题有人用系统键盘敲了小写字母校验直接没过。这些问题倒推回来其实给了设计上的明确指引键盘上必须把字母和数字都摆出来不能只给数字省份简称必须用汉字让用户点选不能让他打字输入位数要实时提示当前是第几位、还剩几位字母统一按大写处理用户敲小写也自动转大写。这些看着都是小细节但它们决定了用户是三次搞定还是折腾半分钟。你如果有线下门店扫码或者收银台代客输入的场景还要考虑大按钮、防误触因为很多时候是店员拿着手机帮客户输手指粗、屏幕小按错概率更高。1.3 一个能打的输入组件应该具备哪些能力我把这些年做下来认为必备的能力列一下方便你对照检查自己的实现。首先是省份选择区和字母数字键盘区要能联动切换最开始显示省份选完省份自动切到字母数字键盘。其次是输入位数的动态判断普通牌七位、新能源八位要自动识别。第三是实时校验每输入一位就判断当前字符是否合法不合法直接不让上屏而不是等用户输完再报错这种边输边拦的体验比事后报错好太多。第四是回填和编辑能力用户从历史记录或者拍照识别结果里带回来的车牌要能显示、能修改、能定位到某一位再改。第五是对外暴露清晰的接口比如bind:change返回当前车牌、bind:complete在输满时触发方便父页面调用。这几条看着简单但真要把每一步都打磨顺代码量和细节思考一点都不少。2. 方案选型为什么我最终放弃了系统键盘2.1 直接用系统输入框会遇到哪些麻烦一开始我也图省事直接放了个 input想着配合校验正则就完事了结果上线后被用户教做人。系统键盘的问题主要三个。第一个是汉字输入太麻烦车牌首位的省份简称是汉字用户要么用拼音打要么用手写光这一个字就能耗掉好几秒还经常打错。第二个是键盘类型不匹配车牌里既有字母又有数字手机的数字键盘没有字母字母键盘切数字又要来回切操作成本极高。第三个是没法做边输边拦系统键盘输入的内容你要在bindinput里过滤用户输入了非法字符再被删掉光标会跳体验很割裂尤其在 iOS 上光标跳动问题特别明显。我做测试的时候请了几个朋友体验用系统输入框平均要花十几到二十秒才能输对还得有人提醒他第二位是字母。换成自定义键盘之后熟练的人五六秒就输完了。这个差距在缴费、开票这种高频场景里非常明显用户等待时间直接影响转化。所以只要你的业务对车牌输入有稳定需求自定义键盘基本是唯一合理的解法。2.2 自定义键盘组件的整体设计思路我的组件拆成三层。最外面是组件容器负责接收父页面的属性比如是否自动聚焦、是否只读、初始值向内暴露change和complete两个事件。中间是显示层用一个个格子把当前已输入的字符显示出来没输入的位置显示占位符光标所在的位置高亮。最下面是键盘层分省份键盘和字母数字键盘两套根据输入进度自动切换。省份键盘我做成一个宫格把三十多个省份简称按顺序铺开每个格子是一个按钮。字母数字键盘则按类似手机全键盘的布局排但去掉了 I 和 O同时把数字单独放大或者标红让用户一眼能区分字母和数字。键盘上还单独放了删除键长按可以连续删除这个小细节体验提升很明显——用户输错一串的时候不用一下一下点。组件和父页面的通信我用事件来做这样组件可以被多个页面复用。数据在上层用properties传进来下层的变化用triggerEvent抛出去父子解耦后面维护起来也清爽。这套结构做出来之后我在三个项目里直接复用基本没怎么改。2.3 和拍照识别的配合怎么做现在很多小程序都会加拍照识别车牌的功能用户对着车牌拍一张自动识别出号码回填到输入框。这个功能很香但识别结果不能直接信因为光线、角度、遮挡都会导致识别错位。我的处理方式是识别结果回填到键盘组件里用户可以逐位检查发现错了直接定位到那一位改。组件必须支持部分回填继续编辑而不是识别完就锁死。识别回来之后我还会再跑一遍本地校验如果位数或者字符集不对直接在对应位置标红提示用户确认。这一套组合拳下来识别准的用户一步到位识别错的用户也能快速修正比纯手输和纯识别都靠谱。要注意的是拍照识别涉及到相机权限和图片上传这块记得做权限申请失败的降级处理别让用户卡在授权弹窗上动弹不得回归到手动输入也能完成流程。3. 从零构建车牌输入组件的核心实现3.1 先把数据准备好省份简称和键盘字符集所有逻辑的起点是数据。省份简称我整理成一个数组顺序上把热门省份放前面还是按行政区划顺序这个看业务我一般按常用度稍微调整一下把业务量大的地区往前放。因为我在好几个项目里都遇到用户抱怨翻半天找不到我这个省把高频省份前置能省不少事。// 省份简称数据 const PROVINCE_LIST [ 京, 津, 冀, 晋, 蒙, 辽, 吉, 黑, 沪, 苏, 浙, 皖, 闽, 赣, 鲁, 豫, 鄂, 湘, 粤, 桂, 琼, 渝, 川, 贵, 云, 藏, 陕, 甘, 青, 宁, 新 ]; // 字母数字字符集去掉 I 和 O const LETTERS [Q,W,E,R,T,Y,U,P,A,S,D,F,G,H,J,K,L,Z,X,C,V,B,N,M]; const NUMBERS [1,2,3,4,5,6,7,8,9,0];注意我把 I 和 O 从字母里彻底去掉了这是很多新手容易忽略的点。有人会问那用户要是真想输 I 怎么办答案是车牌里本来就不存在 I 和 O去掉才是对的。字符集准备好之后键盘布局用二维数组组织一行一个子数组渲染的时候双层循环就行后面想加特殊字符也方便。3.2 键盘布局与输入逻辑键盘的渲染我用 WXML 循环铺开每个键上绑一个点击事件把当前键值传进去。输入逻辑是核心我写一个handleKeyTap函数统一处理。第一步先判断当前处于省份段还是字母数字段如果是省份段并且点击的是省份键那就把省份写进第一位同时把键盘切到字母数字模式光标移到第二位。第二步如果是字母数字段先判断这一位的类型要求比如第二位必须是字母那用户点数字就不上屏并给个轻微提示。handleKeyTap(e) { const key e.currentTarget.dataset.key; const { value, activeIndex } this.data; // 省份段处理 if (activeIndex 0) { if (this.isProvince(key)) { const arr value.split(); arr[0] key; this.setData({ value: arr.join(), activeIndex: 1, keyboardType: letter }); this.emitChange(); } return; } // 第二位必须为字母 if (activeIndex 1 !this.isLetter(key)) { this.shakeTip(第二位应为字母); return; } // 已经输满则不再接受输入 if (activeIndex this.getMaxLength()) return; const arr value.split(); arr[activeIndex] key.toUpperCase(); const nextIndex activeIndex 1; this.setData({ value: arr.join(), activeIndex: nextIndex }); this.emitChange(); // 输满时触发完成事件 if (nextIndex this.getMaxLength()) { this.triggerEvent(complete, { value: arr.join() }); } }这里有几个关键点。长度判断用getMaxLength()动态返回七或八判断依据是第三位字符如果第三位是 D 或 F就返回八否则返回七。字母统一toUpperCase()用户就算点到了小写也自动转大写。输满的时候触发complete父页面可以在这里做提交或者查询。整个输入过程每次按键都校验非法字符直接拦截不会跑到显示层。3.3 校验算法的实现细节校验分成两层一层是实时输入校验前面说的边输边拦就是这层另一层是最终格式校验在complete事件里跑。实时校验保证每一位的字符集正确最终校验用正则确认整个车牌合法。普通车牌和新能源车牌我写了两条正则。// 普通燃油车牌七位 const NORMAL_REG /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$/; // 新能源车牌八位 const NEW_ENERGY_REG /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-HJ-NP-Z](([DF][A-HJ-NP-Z0-9]\d{4})|(\d{5}[DF]))$/;这两条正则里[A-HJ-NP-Z]的意思就是从 A 到 Z 去掉 I 和 O 的字母集合正好对应车牌规则。新能源正则里(([DF][A-HJ-NP-Z0-9]\d{4})|(\d{5}[DF]))分开处理了小型车第三位是 D/F 和大型车第八位是 D/F 两种情况。你直接拿去用的话记得把省份列表和字符集保持一致别正则里认的省份和键盘里给的省份对不上那样就会出现键盘能输但校验不过的诡异问题我早期就踩过这个坑。3.4 组件的对外接口设计组件封装好之后对外我只暴露几个必要的属性。value用来传入初始值实现回填disabled控制只读autoFocus控制是否自动弹出键盘。事件上暴露change每次变化抛出当前值和complete输满触发。父页面用起来就像这样car-plate-input value{{plate}} bind:changeonPlateChange bind:completeonPlateComplete /这种设计的好处是组件不关心业务逻辑只管输入和校验父页面拿到完整车牌去做自己的事。后面如果你要加拍照识别按钮放在父页面就行识别结果通过value传回组件组件自动显示并允许继续编辑。职责清晰复用性高。4. 避坑实录只有真做过才知道的那些细节4.1 车牌规则里的隐藏条款和易混点第一个坑是新能源的位数判断时机。如果你在用户输完第二位就开始判断那第三位还没输进来你怎么知道是不是新能源正确做法是等第三位输入之后再锁定长度或者在第三位输入时动态判断。我在线上见过有实现是让用户先选普通/新能源类型再输入的多一步选择用户就多一层烦恼能自动判断就别让用户选。第二个坑是字母数字的视觉混淆。除了 I 和 O实际用车牌里 1 和 7、8 和 B、0 和 D 也有认错的情况但车牌规则本身不禁止这些你不能替用户做过滤。我的做法是在显示层把字符间距拉开一点、字体用等宽字体减少误读。如果有条件在字母键上把字母做得和数字有明显视觉区分比如不同颜色。第三个坑是挂车、使领馆、警用等特殊号段的尾号汉字。这些车牌在做物流、驾校、租赁类小程序时确实会遇到但它们的规则各不相同。我的建议是把特殊号段做成可配置的开关默认关闭需要的时候再打开避免键盘被特殊字符撑爆。你如果业务里覆盖不到直接不支持就行不要为了少数场景牺牲大多数用户的体验。4.2 光标定位与输入回退的处理组件做编辑的时候用户点中间某一位去改这时光标要能定位到那一位并且后续输入要覆盖而不是往后插。我的实现是把已输入的每个字符当成一个独立格子点击哪个格子就把activeIndex设为那个位置下一次按键就替换这一位。删除键的逻辑是如果当前位置有字符先清空当前位如果没有就把光标往前移一格再清空。这个逻辑看着简单但顺序写反了就会出现删了半天删不掉的问题。长按删除连续清空这个功能我用bindlongpress触发一个定时器每隔一小段时间删一位松手就清除定时器。要注意页面销毁或者组件隐藏时把定时器清掉不然会内存泄漏这种小疏忽在小程序里排查起来特别费劲因为不一定每次都能复现。4.3 常见问题速查表我把实际项目里遇到的问题整理成一张表方便你对照排查。问题现象可能原因处理方式键盘能点但校验不过键盘省份列表与正则省份列表不一致统一维护一份省份常量两边共用iOS 上输入后光标跳动使用系统 input 做过滤改用自定义键盘避免过滤引起的重排新能源车牌输不满八位第三位判断逻辑写成了第二位在第三位输入后再判定长度小写字母被拒绝未做大小写转换输入时统一 toUpperCase删除键长按失效定时器未清除或事件未绑定检查 bindlongpress 和清定时器逻辑回填后无法编辑只读状态未随输入切换回填时把组件设为可编辑并重置光标键盘挡住了输入框页面滚动未处理键盘弹出时让容器上移或滚动到可视区注意省份列表、字符集、正则这三样东西必须同源建议抽成一个单独的常量文件所有地方引用同一份这是我在多个项目里反复吃亏之后总结出来的铁律。5. 落地场景与后续可以扩展的方向5.1 哪些业务会用到这个组件停车缴费是最典型的场景用户进场扫码缴费用车牌匹配输入准确率直接影响缴费效率。洗车预约、加油开票也是一样很多门店系统靠车牌来关联会员和订单。保险、二手车、租车这类小程序车牌是查询和下单的关键字段。还有一类是有车一族的社群或者工具类应用让用户输入车牌做违章查询、限行提醒。这些场景对输入组件的诉求高度一致快、准、可编辑。所以我一直说这个组件值得做成通用能力沉淀下来别每个项目都重新写一遍。5.2 可以继续深挖的功能点组件做扎实之后往上还能挂不少东西。比如接入车牌识别识别失败时再降级到手动输入两条路配合体验最顺。比如把用户最近输入过的车牌存到本地下次进入页面直接展示供快速选择复购场景特别有用。再比如加入车牌归属地的展示输入完自动显示粤B 广东深圳用户能顺带确认自己没输错。还有一点是校验要有容错用户输错的常见字符可以给出智能提示比如输成粤I的时候提示车牌中不含字母 I这样比单纯拒绝要友好得多。我个人在实际操作中的体会是这个组件最值钱的地方不在代码本身而在于把车牌规则、用户习惯和交互细节三者结合起来的那些判断。代码谁都能写但知道为什么第二位必须是字母、为什么 I 和 O 要去掉、为什么长度要动态判断这些才是让一个组件从能跑变成好用的关键。你要是正准备做这类功能建议先把本章的速查表和常量统一这两件事落实了能省掉后面一半的调试时间。
阅读完成 · 觉得有帮助?
咨询建站