背景
在项目开发中,经常遇到左边一个+按钮,中间一个输入框,右边一个-按钮来允许用户修改数量的需求,并且常常更好的体验是,输入框只允许输入数字,在用户输入或者点击按钮改变数字超过设置的范围时,提示用户并能自动修正为设定的数字范围的数字。因为不想每次都要重新写一遍类似的代码,所以想着能不能抽出来做成通用的。
上面的需求每次设计图基本都是不同的,如果想要封装成组件,使用时外面控制自定义样式,就需要用到插槽或者使用深度选择器改变组件内部的样式,但这样总感觉不是很好,对于上面的需求,只有逻辑是可复用的,我们可以只抽出逻辑,样式怎么实现完全由使用者控制。
实现
/**
* 包含+、-以及输入框改变数值的方法
* 创建实例时,传入对象,包含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>