<template> <div class="number-box"> <div @click.stop.prevent="btnTouchStart('minus')" :class="{ minus: true, disabled: disabled || inputVal <= min }" :style="{ background: bgColor, height: inputHeight + 'px', color: color, }" > <div :style="{ fontSize: size + 'px' }">-</div> </div> <input :disabled="disabledInput || disabled" :class="{ 'number-input': true, 'input-disabled': disabled }" v-model="inputVal" @blur="onBlur" @focus="onFocus" type="text" :style="{ color: color, fontSize: size + 'px', background: bgColor, height: inputHeight + 'px', width: inputWidth + 'px', }" /> <div class="plus" @click.stop.prevent="btnTouchStart('plus')" :class="{ disabled: disabled || inputVal >= max }" :style="{ background: bgColor, height: inputHeight + 'px', color: color, }" > <div :style="{ fontSize: size + 'px' }">+</div> </div> </div> </template> <script> export default { components: {}, props: { // 预显示的数字 value: { type: Number, default: 1, }, // 背景颜色 bgColor: { type: String, default: ' #F2F3F5', }, // 最小值 min: { type: Number, default: 0, }, // 最大值 max: { type: Number, default: 99999, }, // 步进值,每次加或减的值 step: { type: Number, default: 1, }, // 是否禁用加减操作 disabled: { type: Boolean, default: false, }, // input的字体大小,单位px size: { type: [Number, String], default: 14, }, // input宽度,单位px inputWidth: { type: [Number, String], default: 64, }, //字体颜色 color: { type: String, default: '#333', }, // input高度,单位px inputHeight: { type: [Number, String], default: 32, }, // index索引,用于列表中使用,让用户知道是哪个numberbox发生了变化,一般使用for循环出来的index值即可 index: { type: [Number, String], default: '', }, // 是否禁用输入框,与disabled作用于输入框时,为OR的关系,即想要禁用输入框,又可以加减的话 // 设置disabled为false,disabledInput为true即可 disabledInput: { type: Boolean, default: false, }, // 是否只能输入大于或等于0的整数(正整数) positiveInteger: { type: Boolean, default: true, }, asyncChange: { type: Boolean, default: false, }, }, watch: { value(v1, v2) { if (!this.changeFromInner) { this.inputVal = v1 this.$nextTick(function () { this.changeFromInner = false }) } }, inputVal(v1, v2) { if (v1 == '') return let value = 0 let tmp = /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(v1) if (tmp && v1 >= this.min && v1 <= this.max) value = v1 else value = v2 if (this.positiveInteger) { if (v1 < 0 || String(v1).indexOf('.') !== -1) { value = v2 this.$nextTick(() => { this.inputVal = v2 }) } } if (this.asyncChange) { return } // 发出change事件 this.handleChange(value, 'change') }, }, data() { return { inputVal: 1, // 输入框中的值,不能直接使用props中的value,因为应该改变props的状态 timer: null, // 用作长按的定时器 changeFromInner: false, // 值发生变化,是来自内部还是外部 innerChangeTimer: null, // 内部定时器 } }, created() { this.inputVal = Number(this.value) }, computed: {}, methods: { btnTouchStart(callback) { this[callback]() }, minus() { this.computeVal('minus') }, plus() { this.computeVal('plus') }, calcPlus(num1, num2) { let baseNum, baseNum1, baseNum2 try { baseNum1 = num1.toString().split('.')[1].length } catch (e) { baseNum1 = 0 } try { baseNum2 = num2.toString().split('.')[1].length } catch (e) { baseNum2 = 0 } baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)) let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2 return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed( precision ) }, calcMinus(num1, num2) { let baseNum, baseNum1, baseNum2 try { baseNum1 = num1.toString().split('.')[1].length } catch (e) { baseNum1 = 0 } try { baseNum2 = num2.toString().split('.')[1].length } catch (e) { baseNum2 = 0 } baseNum = Math.pow(10, Math.max(baseNum1, baseNum2)) let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2 return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed( precision ) }, computeVal(type) { if (this.disabled) return let value = 0 // 减 if (type === 'minus') { value = this.calcMinus(this.inputVal, this.step) } else if (type === 'plus') { value = this.calcPlus(this.inputVal, this.step) } // 判断是否小于最小值和大于最大值 if (value < this.min || value > this.max) { return } if (this.asyncChange) { this.$emit('change', value) } else { this.inputVal = value this.handleChange(value, type) } }, // 处理用户手动输入的情况 onBlur(event) { let val = 0 let value = event.target.value console.log(value) if (!/(^\d+$)/.test(value)) { val = this.min } else { val = +value } if (val > this.max) { val = this.max } else if (val < this.min) { val = this.min } this.$nextTick(() => { this.inputVal = val }) this.handleChange(val, 'blur') }, // 输入框获得焦点事件 onFocus() { this.$emit('focus') }, handleChange(value, type) { if (this.disabled) return // 清除定时器,避免造成混乱 if (this.innerChangeTimer) { clearTimeout(this.innerChangeTimer) this.innerChangeTimer = null } this.changeFromInner = true this.innerChangeTimer = setTimeout(() => { this.changeFromInner = false }, 150) this.$emit('input', Number(value)) this.$emit(type, { value: Number(value), index: this.index, }) }, }, } </script> <style lang="scss" scoped> .number-box { display: inline-flex; align-items: center; .number-input { position: relative; text-align: center; padding: 0; margin: 0 6px; align-items: center; justify-content: center; } .plus, .minus { width: 32px; display: flex; justify-content: center; align-items: center; cursor: pointer; } .plus { border-radius: 0 2px 2px 0; } .minus { border-radius: 2px 0 0 2px; } .disabled { color: #c8c9cc !important; background: #f7f8fa !important; } .input-disabled { color: #c8c9cc !important; background-color: #f2f3f5 !important; } } </style>