实现InputNum类方法--封装输入框限制输入数字以及+、-按钮改变数字

315 阅读2分钟

背景

在项目开发中,经常遇到左边一个+按钮,中间一个输入框,右边一个-按钮来允许用户修改数量的需求,并且常常更好的体验是,输入框只允许输入数字,在用户输入或者点击按钮改变数字超过设置的范围时,提示用户并能自动修正为设定的数字范围的数字。因为不想每次都要重新写一遍类似的代码,所以想着能不能抽出来做成通用的。

上面的需求每次设计图基本都是不同的,如果想要封装成组件,使用时外面控制自定义样式,就需要用到插槽或者使用深度选择器改变组件内部的样式,但这样总感觉不是很好,对于上面的需求,只有逻辑是可复用的,我们可以只抽出逻辑,样式怎么实现完全由使用者控制。

实现

/**
 * 包含+、-以及输入框改变数值的方法
 * 创建实例时,传入对象,包含min(最小值,必传),max(最大值,必传),
 * touchMinTip(-到最小时的提示,非必传),touchMaxTio(+到最大时的提示,非必传)
 *
 */
 //使用mint-ui的Toast,超出范围时toast提示用户
import { Toast } from 'mint-ui';

export default class InputNum {
  // 初始化参数
  constructor({ min, max, touchMinTip = '', touchMaxTip = '' }) {
    this.min = min;
    this.max = max;
    this.touchMinTip = touchMinTip;
    this.touchMaxTip = touchMaxTip;
  }
  // 限制数值在区间内
  clampNum(val, min = this.min, max = this.max) {
    return val > max ? max : val < min ? min : val;
  }
  // 判断数值是否在区间内
  checkInRange(val, min = this.min, max = this.max) {
    return val >= min && val <= max;
  }
  // 格式化为数字(非数字替换为'')
  formatNum(val) {
    return Number(val.replace(/\D|^0/g, ''));
  }
  /**
   *  +、-按钮的处理方法,如果传入了touchMinTip/touchMaxTip会有对应的toast
   * @param {*} oriVal 当前数值
   * @param {*} step   改变的间距值
   * @param {*} min   限制的最小值,默认为实例初始化传入的值
   * @param {*} max   限制的最大值,默认为实例初始化传入的值
   * @returns     返回变化后的满足限制区间的数值
   */
  changeNum(oriVal, step = 1, min = this.min, max = this.max) {
    const tempVal = oriVal + step;
    if (tempVal < min) {
      this.touchMinTip && Toast(this.touchMinTip);
      return min;
    } else if (tempVal > max) {
      this.touchMaxTip && Toast(this.touchMaxTip);
      return max;
    } else {
      return tempVal;
    }
  }
  /**
   * 处理输入框用户输入,格式化为数值,如果不在限制区间内,toast提示,并且限制在区间内
   * 如果用户未输入值,不处理,返回''
   * @param {*} val 
   * @returns 限制范围内数值或者'';
   */
  checkInputNum(val) {
    if (!val) {
      return '';
    }
    const filterVal = this.formatNum(val);
    const validNum = this.checkInRange(filterVal);
    if (!validNum) {
      Toast(`您输入的数量有误,请输入${this.min}-${this.max}的整数`);
    }
    return this.clampNum(filterVal);
  }
}

实例实践

  • 以Vue实现为例
<template>
  <div>
    <div class="btn decrease-btn" @click="handleChangeNum(-1)">-</div>
    <div class="input-box">
      <input
        type="text"
        v-model="ticketNum"
        class="input-content"
        @input="handleInputNum"
      />
    </div>
    <div class="btn increase-btn" @click="handleChangeNum(+1)">+</div>
  </div>
</template>
<script>
import { InputNum } from 'utils/input-num';
export default {
  data() {
    return {
      ticketNum: 0,
    };
  },
  mounted() {
    this.inputInstance = new InputNum({ min: 1, max: 100 });
  },
  methods: {
    handleChangeNum(step) {
      this.ticketNum = this.inputInstance.changeNum(this.ticketNum, step);
    },
    handleInputNum(e) {
      this.ticketNum = this.inputInstance.checkInputNum(e.target.value);
    },
  },
};
</script>