zoukankan      html  css  js  c++  java
  • 金额输入框 在 hybrid 下 ios/android h5 处理情况

    目标

    做一个金额输入框

    要求

    只需输入 数字 或者 小数点
    不准输入非法数字  如  1.1.  或者 1..2
    数字最大输入到两位小数  如  1.22,    不可输入 1.222
    
     此时我们只讨论系统键盘。 自定义键盘不可取因为输入框不能获取焦点, 自定义键盘需要搭配特定的输入框也就是没有光标的那种才合适
    

    解决方案的常规手段无外乎一下三种

    • 监听 keyup/keydown/keypress 事件 通过 event.keycode 判断keycode的值来 return false还是true 此种体验最优,页面没有闪动 ( 存在部分兼容性)

    • 监听 keyup/keydown/keypress 事件 通过 event.target.value 得到当前输入的值 通过 正则匹配来过滤是否替换为空 ( 存在部分兼容性)

    • 监听 input 事件 通过 event.target.value 得到当前全部的输入值,但是当用户移动光标后在输入无法得知当前输入的是什么值, ( 完全兼容,但是没有什么乱用)

      所以解决方案只有通过第一条或者第二条

    在分析一下 input type 的取值

    • type =text 弹出的非数字键盘 体验最差 有限排除
    • type = number 弹出数字键盘,缺点 event.target.value 只能取到被过滤后的数字
    • type= tel 弹出数字键盘 event.target.value 可以取到正确值, 但是在ios下弹出的软键盘 没有'.'

    先来一段小前提,一下为亲测

    • 在中文状态下在keycode 都是229

    • Android手机 弹出的软键盘默认为中文状态 所以code码 都是229

    • ios手机 弹出的软键盘默认为英文文状态 所以code码可以用

    • android手机无法监听到keypress事件,只可以监听到keyup keydown

    • ios手机在 <input type=tel> 弹出的软键盘 只有1-9 没有 ‘.’

    • <input type="number"> 这种形式 event.target.value 得到的值只有纯数字,也就是输入12.... 的时候 event.target.value =12


    所以解决方案呼之欲出


    • 在android 下input type=tel监听 keyup事件, event.target.value 通过正则过滤输入
    • 在ios下 input type=number 通过 keypress 事件 event.keycode 过滤输入,

    部分参考 https://cloud.tencent.com/developer/article/1098877


    一下是我对以上的一个简单实现

    
    
    <template>
      <x-cell
        :title="label"
        :isBordorBootom = 'isBordorBootom'
        v-clickoutside="doCloseActive"
        :class="[{
          'is-textarea': type === 'textarea',
          'is-nolabel': !label
        }]">
        <div class="mint-field">
          <textarea
            @change="$emit('change', currentValue)"
            ref="textarea"
            class="mint-field-core"
            :placeholder="placeholder"
            v-if="type === 'textarea'"
            :rows="rows"
            :disabled="disabled"
            :readonly="readonly"
            v-model="currentValue">
          </textarea>
          <div class="input-waper" v-else >
            <div class="prefix">
              <slot name="prefix"></slot>
            </div>
            <input
              @change="$emit('change', currentValue)"
              ref="input"
              class="mint-field-core"
              :placeholder="placeholder"
              :maxlength="maxlength"
              :number="type === 'number'"
              :type="type"
              :style="`font-size:${fontSize}; paddingLeft:${paddingLeft}`"
              @focus="focus"
              @blur="$emit('blur')"
              :disabled="disabled"
              :readonly="readonly"
              :value="currentValue"
              @keyup="handleKeyup"
              @keypress="handleKeypress"
              @input="handleInput">
            <div
            @click="handleClear"
            class="mint-field-clear"
            v-if="!disableClear"
            v-show="(currentValue !== '') && type !== 'textarea' && active">
            <!-- <i class="mintui mintui-field-error"></i> -->
              <img src="./clean_text_small@3x.png" class="mint-field-clear-img">
          </div>
             <div class="afterfix">
              <slot name="afterfix"></slot>
            </div>
    
          </div>
    
      </div>
      </x-cell>
    </template>
    
    <script>
    import XCell from '../../cell/src/cell';
    import Clickoutside from '../../../src/utils/clickoutside';
    import icon from '../../icon/src/icon'
    
    /**
     * mt-field
     * @desc 编辑器,依赖 cell
     * @module components/field
     *
     * @param {string} [type=text] - field 类型,接受 text, textarea 等
     * @param {string} [label] - 标签
     * @param {string} [rows] - textarea 的 rows
     * @param {string} [placeholder] - placeholder
     * @param {string} [disabled] - disabled
     * @param {string} [readonly] - readonly
     * @param {string} [state] - 表单校验状态样式,接受 error, warning, success
     *
     * @example
     * <mt-field v-model="value" label="用户名"></mt-field>
     * <mt-field v-model="value" label="密码" placeholder="请输入密码"></mt-field>
     * <mt-field v-model="value" label="自我介绍" placeholder="自我介绍" type="textarea" rows="4"></mt-field>
     * <mt-field v-model="value" label="邮箱" placeholder="成功状态" state="success"></mt-field>
     */
    export default {
      name: 'CommonField',
      data() {
        return {
          active: false,
          currentValue: this.value
        };
      },
      directives: {
        Clickoutside
      },
    
      props: {
        type: {
          type: String,
          default: 'text'
        },
        rows: String,
        label: String,
        placeholder: String,
        readonly: Boolean,
        disabled: Boolean,
        disableClear: Boolean,
        state: {
          type: String,
          default: 'default'
        },
        value: {},
        attr: Object,
        isBordorBootom: {
          type: Boolean,
          default: false
        },
        fontSize: String,
        paddingLeft: String,
        maxlength: Number
      },
    
      components: { XCell, icon },
    
      methods: {
        doCloseActive() {
          this.active = false;
        },
    
        handleInput(evt) {
          this.currentValue = evt.target.value
           console.log('handleInput:', evt)
        },
        handleKeydown(evt) {
          console.log('handleKeydown:', evt)
          if (this.type === 'number') {
            const keyCode = evt.keyCode;
            const inputVal = this.$refs.input.value
            if (this.isNotNumberKeycode(keyCode) || this.isDotStart(keyCode, inputVal)) {
              console.log('组织')
              evt.preventDefault();
              evt.stopPropagation();
              return false;
            }
          }
        },
        handleKeypress(evt) {
          console.log('handleKeypress:', evt)
          if (this.type === 'number') {
            const keyCode = evt.keyCode;
            const inputVal = this.$refs.input.value
            if (this.isNotNumberKeycode(keyCode) || this.isDotStart(keyCode, inputVal)) {
              evt.preventDefault();
              evt.stopPropagation();
              return false;
            }
          }
        },
        isBackspace(keyCode) {
          return keyCode === 8;
        },
        isDot(keyCode) {
          return keyCode === 46 || keyCode === 190;
        },
        isNumber(keyCode) {
          return (keyCode >= 48 && keyCode <= 57);
        },
        isDotStart(keyCode, inputVal) {
          return this.isDot(keyCode) && (!inputVal || inputVal === '' || /./.test(inputVal));
        },
        isNotNumberKeycode(keyCode) {
          return !this.isBackspace(keyCode) && !this.isDot(keyCode) && !this.isNumber(keyCode);
        },
        handleKeyup(evt) {
           console.log('handleKeyup:', evt)
          if (this.type === 'number' || this.type === 'tel') {
            let val = evt.target.value
            console.log('原来的val:', val)
            val = val.replace(/[^d.]/g, '') //清除“数字”和“.”以外的字符
            val = val.replace(/.{2,}/g, '.') //只保留第一个. 清除多余的 连续两个
            val = val.replace('.', '$#$').replace(/./g, '').replace('$#$', '.') //只保留第一个. 清除多余的 非连续两个
            val = val.replace(/^(-)*(d+).(dd).*$/,'$1$2.$3') //只能输入两个小数
            if(val.indexOf('.')< 0 && val != ''){ //以上已经过滤,此处控制的是如果没有小数点,首位不能为类似于 01、02的金额
              val = parseFloat(val)
            }
            console.log(val)
            // this.currentValue = evt.target.value.replace(/[^d]/g,'');
            this.currentValue = val
          }
        },
        handleClear() {
          if (this.disabled || this.readonly) return;
          this.currentValue = '';
          this.$emit('handleClear')
        },
        focus() {
          this.active = true
          this.$emit('focus')
        }
      }, 
      computed: {
        iconState() {
          if (this.state === 'default') {
            return ''
          } else if (this.state === 'error') {
            return ''
          } else if (this.state === 'warning') {
            return ''
          } else if (this.state === 'success') {
            return ''
          } else {
            return ''
          }
        },
        showClear() {
          
        }
    
      },
      watch: {
        value(val) {
          this.currentValue = val;
        },
    
        currentValue(val) {
          this.$emit('input', val);
        },
    
        attr: {
          immediate: true,
          handler(attrs) {
            this.$nextTick(() => {
              const target = [this.$refs.input, this.$refs.textarea];
              target.forEach(el => {
                if (!el || !attrs) return;
                Object.keys(attrs).map(name => el.setAttribute(name, attrs[name]));
              });
            });
          }
        }
      }
    };
    </script>
    
    <style lang="scss" scoped>
    @import "../../../src/style/var.scss";
    input {
      font-family: The1Official_Bold ;
    }
    
    .mint-field {
       100%;
    }
    
    .mint-field.is-textarea {
        -webkit-box-align: inherit;
            -ms-flex-align: inherit;
                align-items: inherit;
    }
    .input-waper {
      // border-bottom: 1px solid #EEEEEE;
      // padding: .3rem 0 .3rem 0;
    
      border-bottom: 1px solid #A1A5B9; 
      padding-right: 5px;
      display: flex;
      // height: 1.1rem;
      // padding: .28rem 0rem .27rem 0rem;
      position: relative;
      box-sizing: border-box;
      .mint-field-core {
        border: 0;
        padding: 0;
        display: block;
         100%;
        resize: none;
        box-sizing: border-box;
        font-size: .46rem;
        outline:none;
        color: $color-red;
        padding-right: .5rem;
    
      }
    
      .prefix {
        display: flex;
        flex-direction: column;
        justify-content: center;
      }
      .afterfix {
           display: flex;
        flex-direction: column;
        justify-content: flex-end;
        padding-bottom: .4rem;
      }
    
      ::-webkit-input-placeholder { /* WebKit browsers */
        color: $color-red;
        opacity: .5;
      }
      :-moz-placeholder { /* Mozilla Firefox 4 to 18 */
        color: $color-red;
         opacity: .5;
      }
      ::-moz-placeholder { /* Mozilla Firefox 19+ */
        color: $color-red;
         opacity: .5;
      }
      :-ms-input-placeholder { /* Internet Explorer 10+ */
        color: $color-red;
         opacity: .5;
      }
    }
    // 去除number右侧箭头
    input::-webkit-outer-spin-button,
    input::-webkit-inner-spin-button {
        -webkit-appearance: none !important;
        margin: 0;
    }
    
    
    .mint-field-clear {
        display: flex;
        justify-content: center;
        align-items: flex-end;
        padding-bottom: .4rem;
        padding-right: .2rem;
    }
    .mint-field-clear-img {
       14px;
      height: 14px;
    }
    .mint-field-state {
        color: inherit;
        margin-left: 20px;
    }
    .mint-field-state .mintui {
        font-size: 20px;
    }
    .mint-field-state.is-default {
        margin-left: 0;
    }
    .mint-field-state.is-success {
        color: #4caf50;
    }
    .mint-field-state.is-warning {
        color: #ffc107;
    }
    .mint-field-state.is-error {
        color: #f44336;
    }
    .mint-field-other {
        top: 0;
        right: 0;
        position: relative;
    }
    </style>
    
    
    
    
    
    
    
    
  • 相关阅读:
    1031.查询-集合操作
    1030.查询-select子句
    1029.查询-复杂分组grouping子句
    1028.查询-group by和having子句
    1027.查询-where子句
    1026.查询-from子句2
    1025.查询-from子句
    1024.查询-概述
    1023.表-数据操作
    图片隐写之stegsolve使用(转载)
  • 原文地址:https://www.cnblogs.com/WhiteHorseIsNotHorse/p/9668820.html
Copyright © 2011-2022 走看看