大家在做移动端开发的时候,想必一定会遇到拉起系统自带的键盘吧。最近接到需求, 想要拉起数字的键盘,只允许用户输入数字和小数点,而且在用户失去焦点的同时,将输入框的内容进行千分位,聚焦的时候还可以删除。 看似几个需求,但还真不好整。针对此需求,笔者考虑了三种方案,建议选择第二种(简单粗暴),因为第一种真的是在浪费生命,它会给你无尽的深渊~
废话不多说,先来分析一波。
- 拉起数字键盘,那么input框的type值为number
- 禁止用户输入除数字、小数点其他异样字符,正则替换
- 输入框的内容千分位,eg: 1,000.00。失去焦点,将内容格式化,字符串类型,那么type值为text
- 聚焦的时候,还必须调起来的是数字键盘,所以改变type的类型为number,在把千分位的内容换成数字
以上是实现这个的具体思路,接下来就迎接各大机型的兼容性问题吧,放心,会让你开心到飞起。
第一种方案(先上代码,再来分析)
input.tsx
import React from 'react'
import {Images} from "config/index";
import './style.scss'
interface Props {
isFormat?: Boolean,
isShowAll?: Boolean,
val?: any,
handleFormat: Function,
handleChange?: Function,
handleDel?: Function,
handleAll?: Function,
placeholder?: any,
small?: any
isShowDelIcon?: Boolean,
disabled?: Boolean
}
export default class BcInputMoney extends React.Component<any, any>{
constructor(props) {
super(props)
this.state = {
inputType: 'number' // 初始是number类型
}
}
handleChange = (e) => {
let val = e.target.value
// 动态替换异样字符
val = val.replace(/^\D*(\d*(?:\.\d{0,2})?).*$/g, '$1').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3')
val = val.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')
this.props.handleChange(e, val)
}
handleDel = () => {
const { isFormat } = this.props
if (isFormat) { // 表示需要格式化
this.setState({
inputType: 'number'
})
}
this.props.handleDel()
}
handleAll = () => {
this.props.handleAll()
}
handleBlur = () => {
const { isFormat, val } = this.props
if (isFormat && val) { // 表示需要格式化
if (val <= 999) {
// 因为 999.00 格式化的时候 和 不格式化的时候 值是一致的,所以动态改变成text的时候第一次拉不起键盘
this.setState({
inputType: 'number'
})
} else {
this.setState({
inputType: 'text'
})
}
this.props.handleFormat('blur')
}
}
handleFoucs = () => {
const { isFormat, val } = this.props
if (isFormat && val) { // 表示需要格式化
this.setState({
inputType: 'number'
})
this.props.handleFormat('focus')
}
}
render() {
const { isShowAll, val, placeholder, small, isShowDelIcon = true, disabled = false, type } = this.props
let { inputType } = this.state
return (
<div className="bc-money-input-box">
<span className="unit">¥</span>
<div className="inputs">
<input
pattern="[0-9]*"
className={"money-input " + (small ? small : '')}
disabled={disabled}
type={inputType}
value={ val == null ? '' : val }
placeholder={ placeholder ? placeholder : '请输入金额'}
onBlur={this.handleBlur}
onFocus={this.handleFoucs}
onChange={this.handleChange}/>
</div>
<div className={`del-img ${isShowDelIcon ? '' : 'hidden'}`} onClick={ this.handleDel }>
<img src={Images.delIcon} alt="" width="16" height="17"/>
</div>
<div className={`all ${isShowAll ? '' : 'hidden'}`} onClick={ this.handleAll }>全部</div>
</div>
)
}
}
style.scss
.bc-money-input-box{
background: #FFFFFF;
min-height: px2rem(38);
display: flex;
align-items: center;
justify-content: space-between;
font-family:PingFangSC-Regular;
font-weight:400;
margin-left: px2rem(15);
.unit{
padding-right: px2rem(10);
font-size: px2rem(22);
color:rgba(51,51,51,1);
width: px2rem(12);
}
.inputs{
flex: 1;
.money-input{
height: px2rem(38);
width: 100%;
border: none;
outline: none;
box-sizing: border-box;
font-size: px2rem(24);
font-family:PingFangSC-Medium;
font-weight:500;
color:rgba(51,51,51,1) !important;
background: transparent;
}
}
.del-img{
width: px2rem(20);
height: px2rem(38);
margin-right: px2rem(10);
display: flex;
align-items: center;
}
.all{
width: px2rem(35);
margin-left: px2rem(-4);
padding-right: px2rem(15);
font-size: px2rem(16);
color:rgba(80,140,238,1);
line-height: px2rem(38);
}
.hidden{
display: none;
}
}
.money-input::-webkit-input-placeholder {
font-size: px2rem(24);
font-family:PingFangSC-Regular;
display: flex;
line-height:normal;
align-items: center;
color:rgba(153,153,153,1);
background: transparent;
transform: translate(0, 0);
-ms-transform:translate(0, 0); /* IE 9 */
-moz-transform:translate(0,0); /* Firefox */
-webkit-transform:translate(0, 0); /* Safari 和 Chrome */
-o-transform:translate(0, 0);
}
.s::-webkit-input-placeholder {
font-size: px2rem(16);
line-height: px2rem(38);
font-family:PingFangSC-Regular;
color:rgba(153,153,153,1);
background: transparent;
transform: translate(0, 0);
-ms-transform:translate(0, 0); /* IE 9 */
-moz-transform:translate(0,0); /* Firefox */
-webkit-transform:translate(0, 0); /* Safari 和 Chrome */
-o-transform:translate(0, 0);
}
页面中使用该组件的方法
import React from 'react'
import {observer, inject} from 'mobx-react'
import { BcButton, BcInputMoney } from 'container/index'
import help from 'utils/Tool'
@inject('store')
@observer
export default class Demo extends React.Component<any, any> {
state = {
money: ''
}
changeMoney = (e, val) => { // 子组件调用的方法
this.setState({
money: val
})
}
FormatMoney = (type) => { // 格式话当前的内容
let { money } = this.state
if (money.indexOf('.') == money.length - 1) {
money = money.replace('.', '')
}
if (type == 'focus') {
this.setState({
money: help.clearComma(money)
})
}
if (type == 'blur') {
this.setState({
money: help.formatNum(money.toString())
})
}
}
delMoney = () => { // 清空当前值
if (this.state.money) {
this.setState({
money: ''
})
}
}
render () {
let { money } = this.state
return (
<div className="box">
<BcInputMoney
isFormat={true}
isShowDelIcon={money}
isShowAll={false}
val={money}
handleDel={this.delMoney}
handleChange={this.changeMoney}
handleFormat={this.FormatMoney}
placeholder={'请输入充值金额'} />
<BcButton className="recharge">充值</BcButton>
</div>
)
}
}
以上呢就是组件之间的通信,大家花下心思必懂,给大家说下以下代码存在的问题。
iOS
- 单纯的type值拉起数字键盘是可以的,但是在格式化完内容之后,在拉起键盘去修改输入框的内容的时候,拉起来的是英文26键,所以加上这个 pattern="[0-9]*",但是弊端是没有小数点。
- 输入框的内容进行回删,光标的位置回错乱,原因就是在输入的时候,针对异样字符替换。
- 可以往输入框里面输入+-符号。
Android
锤子
- 用户输入错误字符会被置空(change的时候针对异样字符替换为‘’)
华为
- 回删光标的位置回退倒最后一位
- 输入小数点的时候光标错乱
- 输入异样字符内容置空,必须在末位
- 在中间输入异样字符,光标位置错乱
小米、vivo
- 输入异样字符内容置空,必须在末位
- 在中间输入异样字符,光标位置错乱
这只是针对几个问题做出来的处理。再次细化安卓各种的机型,问题想必也不会少,所以别浪费生命啦~
第二种方案。
我们可以转换一种思路,第一种我们一直在input上做事情,做完可能这个机型可以,另一个机型又是另一种问题,所以我们 可不可以把侧重点放在输入完内容之后点击的按钮上,输入完内容,用户肯定操作按钮下一步操作,我们在这里拦截是否符合我们的规范 ,不用考虑兼容问题,我们开发也方便,更何况输入错误的格式的用户也应该不多。
上代码(同上,就是添加下一步按钮的事件,把input的change事件的替换正则的给干掉)
let { money } = this.state
const regMoney = /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/ // 金钱
const fixedTwo = /^\d*(\.?\d{0,2})/g // 输入框对金额限制小数点后两位
let val = help.clearComma(money)
if (regMoney.test(val) && fixedTwo.test(val)) {
console.log('温馨提示,您输入的内容格式有误')
} else {
console.log('输入框的内容格式正确,下一步吧')
}
第三种方案。
脱离了需求的轨迹,不在动态改变input的type值,上来就是text,这样存在的问题少,但是就是拉起键盘的是英文键盘。代码同上,把input的type值写死就好。我们目前采用的方案就是这种。 点击查看自定义键盘和自定义input输入框。
第四种方案。
综合以上的几种方案都是我目前所踩的坑,到最后发现anth-mobile里面的input就有他自己实现的一套数字的方案,看看官方api就可以使用,如果大家采用react开发的话,可以直接用它的键盘就好了,感兴趣的话看看实现的源码,会有意想不到的收获。
阅读的童靴如有用,请点个小赞再走呗。如有错误,欢迎指正。