阅读 2863

移动端拉起手机数字键盘,只允许用户输入数字和小数点?

大家在做移动端开发的时候,想必一定会遇到拉起系统自带的键盘吧。最近接到需求, 想要拉起数字的键盘,只允许用户输入数字和小数点,而且在用户失去焦点的同时,将输入框的内容进行千分位,聚焦的时候还可以删除。 看似几个需求,但还真不好整。针对此需求,笔者考虑了三种方案,建议选择第二种(简单粗暴),因为第一种真的是在浪费生命,它会给你无尽的深渊~

废话不多说,先来分析一波。

  • 拉起数字键盘,那么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

  1. 单纯的type值拉起数字键盘是可以的,但是在格式化完内容之后,在拉起键盘去修改输入框的内容的时候,拉起来的是英文26键,所以加上这个 pattern="[0-9]*",但是弊端是没有小数点。
  2. 输入框的内容进行回删,光标的位置回错乱,原因就是在输入的时候,针对异样字符替换。
  3. 可以往输入框里面输入+-符号。

Android

锤子

  1. 用户输入错误字符会被置空(change的时候针对异样字符替换为‘’)

华为

  1. 回删光标的位置回退倒最后一位
  2. 输入小数点的时候光标错乱
  3. 输入异样字符内容置空,必须在末位
  4. 在中间输入异样字符,光标位置错乱

小米、vivo

  1. 输入异样字符内容置空,必须在末位
  2. 在中间输入异样字符,光标位置错乱

这只是针对几个问题做出来的处理。再次细化安卓各种的机型,问题想必也不会少,所以别浪费生命啦~

第二种方案。

我们可以转换一种思路,第一种我们一直在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值写死就好。我们目前采用的方案就是这种。

以上呢,是在写这个东西踩的坑,阅读的童靴如有用,请点个小赞再走呗。如有错误,欢迎指正。

关注下面的标签,发现更多相似文章
评论