<template>
<view class="u-numberbox">
<view class="u-icon-minus" @touchstart.stop.prevent="btnTouchStart('minus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal <= min }"
:style="{
background: bgColor,
height: inputHeight + 'rpx',
color: color
}">
<u-icon name="minus" :size="size"></u-icon>
</view>
<input :disabled="disabledInput || disabled" :cursor-spacing="getCursorSpacing" :class="{ 'u-input-disabled': disabled }"
v-model="inputVal" class="u-number-input" @blur="onBlur" @focus="onFocus"
type="number" :style="{
color: color,
fontSize: size + 'rpx',
background: bgColor,
height: inputHeight + 'rpx',
width: inputWidth + 'rpx'
}" />
<view class="u-icon-plus" @touchstart.stop.prevent="btnTouchStart('plus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal >= max }"
:style="{
background: bgColor,
height: inputHeight + 'rpx',
color: color
}">
<u-icon name="plus" :size="size"></u-icon>
</view>
</view>
</template>
<script>
export default {
name: "u-number-box",
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
},
size: {
type: [Number, String],
default: 26
},
color: {
type: String,
default: '#323233'
},
inputWidth: {
type: [Number, String],
default: 80
},
inputHeight: {
type: [Number, String],
default: 50
},
index: {
type: [Number, String],
default: ''
},
disabledInput: {
type: Boolean,
default: false
},
cursorSpacing: {
type: [Number, String],
default: 100
},
longPress: {
type: Boolean,
default: true
},
pressTime: {
type: [Number, String],
default: 250
},
positiveInteger: {
type: Boolean,
default: true
}
},
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 = this.$u.test.number(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;
})
}
}
this.handleChange(value, 'change');
}
},
data() {
return {
inputVal: 1,
timer: null,
changeFromInner: false,
innerChangeTimer: null,
};
},
created() {
this.inputVal = Number(this.value);
},
computed: {
getCursorSpacing() {
return Number(uni.upx2px(this.cursorSpacing));
}
},
methods: {
btnTouchStart(callback) {
this[callback]();
if (!this.longPress) return;
clearInterval(this.timer);
this.timer = null;
this.timer = setInterval(() => {
this[callback]();
}, this.pressTime);
},
clearTimer() {
this.$nextTick(() => {
clearInterval(this.timer);
this.timer = null;
})
},
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) {
uni.hideKeyboard();
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;
}
this.inputVal = value;
this.handleChange(value, type);
},
onBlur(event) {
let val = 0;
let value = event.detail.value;
if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min;
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>
@import "../../libs/css/style.components.scss";
.u-numberbox {
display: inline-flex;
align-items: center;
}
.u-number-input {
position: relative;
text-align: center;
padding: 0;
margin: 0 6rpx;
@include vue-flex;
align-items: center;
justify-content: center;
}
.u-icon-plus,
.u-icon-minus {
width: 60rpx;
@include vue-flex;
justify-content: center;
align-items: center;
}
.u-icon-plus {
border-radius: 0 8rpx 8rpx 0;
}
.u-icon-minus {
border-radius: 8rpx 0 0 8rpx;
}
.u-icon-disabled {
color: #c8c9cc !important;
background: #f7f8fa !important;
}
.u-input-disabled {
color: #c8c9cc !important;
background-color: #f2f3f5 !important;
}
</style>