支付密码输入框必须用view模拟而非原生input,通过自定义键盘控制passwordvalue字符串,严格限制长度为6、仅允许数字和退格键,并在vue3中使用definemodel实现v-model双向绑定。

uni-app 里别用原生 input 做支付密码输入框
原生 input 在微信小程序、支付宝小程序甚至 iOS App(nvue)中,会触发系统软键盘,既不安全(可能被截屏/录屏),也不可控(无法禁用长按复制、无法屏蔽输入法候选词)。更关键的是:它根本没法和自定义数字键盘联动——你点键盘上的“5”,input 不知道该不该响应,也拦不住用户切回拼音键盘乱输。
所以真实项目里,支付密码输入框必须「视觉上像输入框 + 行为上完全脱离 input」。核心做法是:用纯 view 模拟密码格,用自定义键盘控制状态,所有输入逻辑走 JS 控制。
- 密码显示区用 6 个
<view class="pwd-cell"></view>并列排布,每个里面放<text>●</text>或明文字符 - 禁止任何
input、textarea绑定v-model或监听@input - 键盘点击事件只修改一个字符串变量(如
passwordValue),再靠长度驱动视图更新
onKeyClick 回调里要严格限制长度和按键类型
用户点一次键盘,你得立刻判断:这是数字?是删除?还是无效键?并且在第 6 位之后,不能再接受新数字——这点极易漏掉,导致数组越界或 UI 错乱。
常见错误现象:输满 6 位后还能继续点数字,UI 显示 7 个圆点;或者点了“删除”却清空了整个密码;又或者小数点 .、字母键被意外录入(尤其在 H5 端用 PC 浏览器调试时)。
- 只允许 keyCode 48–57(0–9)、8(Backspace)进入主逻辑
- 在
if (passwordValue.length >= 6)后直接return,不处理任何新增数字 - 删除逻辑写成
passwordValue = passwordValue.slice(0, -1),别用splice配索引,易错 - H5 端需额外监听
@keydown阻止event.key === '.'或event.key.match(/[^0-9]/)
键盘组件的 show 和 focus 必须手动控制显隐与焦点
uni-app 没有统一的「安全键盘 API」,所谓「安全」全靠你控制:键盘弹出时,页面其他区域必须失焦、不可滚动、不可点击;键盘收起时,要确保密码状态已提交或重置。
典型翻车场景:用户点支付按钮 → 键盘弹出 → 用户切到微信后台 → 再切回来,键盘卡住不响应;或者键盘没关,用户误点页面其他按钮触发重复下单。
- 用
ref拿到键盘组件实例,调用.show()/.hide(),别只靠v-show或v-if - 键盘弹出前,先
uni.hideKeyboard()主动收起系统键盘(防 H5/Android 双键盘) - 监听键盘的
@close或@blur事件,在回调里重置passwordValue = ''并清空 UI 圆点 - 微信小程序端务必加
safe-area-inset-bottom样式,避免键盘遮挡底部按钮
Vue3 + v-model 封装时,别忘了 defineModel 的响应式穿透
如果你封装成 <pay-password v-model="pwd"></pay-password> 这种用法,Vue3 下必须用 defineModel(),否则子组件内部改 modelValue,父组件的 pwd 不会同步更新——这问题在真机调试时才暴露,模拟器常表现正常。
另一个坑是:完成输入后 emit complete 事件,但没等父组件处理完就立刻 hide(),导致回调丢失。
- 子组件脚本开头必须写
const modelValue = defineModel({ type: String }) - 输入完成时,先
modelValue.value = finalPassword,再emit('complete', finalPassword) - 父组件接收
@complete后,再执行业务逻辑(比如调支付接口),不要在子组件里自动跳转或关闭 - 别在键盘组件里写
setTimeout(() => hide(), 100)等异步关闭,要用nextTick或 Promise 链确保 DOM 更新完毕










