123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401 |
- <template>
- <view class="tn-number-box-class tn-number-box">
- <!-- 减 -->
- <view
- class="tn-number-box__btn__minus"
- :class="[
- backgroundColorClass,
- fontColorClass,
- {'tn-number-box__btn--disabled': disabled || inputValue <= min}
- ]"
- :style="{
- backgroundColor: backgroundColorStyle,
- height: $t.string.getLengthUnitValue(inputHeight),
- color: fontColorStyle,
- fontSize: fontSizeStyle
- }"
- @touchstart.stop.prevent="touchStart('minus')"
- @touchend.stop.prevent="clearTimer"
- >
- <view class="tn-icon-reduce"></view>
- </view>
-
- <!-- 输入框 -->
- <input
- v-model="inputValue"
- :disabled="disabledInput || disabled"
- :cursor-spacing="getCursorSpacing"
- class="tn-number-box__input"
- :class="[
- fontColorClass,
- {'tn-number-box__input--disabled': disabledInput || disabled}
- ]"
- :style="{
- width: $t.string.getLengthUnitValue(inputWidth),
- height: $t.string.getLengthUnitValue(inputHeight),
- color: fontColorStyle,
- fontSize: fontSizeStyle,
- backgroundColor: backgroundColorStyle
- }"
- @blur="blurInput"
- @focus="focusInput"
- />
-
- <!-- 加 -->
- <view
- class="tn-number-box__btn__plus"
- :class="[
- backgroundColorClass,
- fontColorClass,
- {'tn-number-box__btn--disabled': disabled || inputValue >= max}
- ]"
- :style="{
- backgroundColor: backgroundColorStyle,
- height: $t.string.getLengthUnitValue(inputHeight),
- color: fontColorStyle,
- fontSize: fontSizeStyle
- }"
- @touchstart.stop.prevent="touchStart('plus')"
- @touchend.stop.prevent="clearTimer"
- >
- <view class="tn-icon-add"></view>
- </view>
- </view>
- </template>
- <script>
- import componentsColor from '../../libs/mixin/components_color.js'
-
- export default {
- mixins: [componentsColor],
- name: 'tn-number-box',
- props: {
- value: {
- type: Number,
- default: 1
- },
- // 索引
- index: {
- type: [Number, String],
- default: ''
- },
- // 最小值
- min: {
- type: Number,
- default: 0
- },
- // 最大值
- max: {
- type: Number,
- default: 99999
- },
- // 步进值
- step: {
- type: Number,
- default: 1
- },
- // 禁用
- disabled: {
- type: Boolean,
- default: false
- },
- // 是否禁用输入
- disabledInput: {
- type: Boolean,
- default: false
- },
- // 输入框的宽度
- inputWidth: {
- type: Number,
- default: 88
- },
- // 输入框的高度
- inputHeight: {
- type: Number,
- default: 50
- },
- // 输入框和键盘之间的距离
- cursorSpacing: {
- type: Number,
- default: 100
- },
- // 是否开启长按进行连续递增减
- longPress: {
- type: Boolean,
- default: true
- },
- // 长按触发间隔
- longPressTime: {
- type: Number,
- default: 250
- },
- // 是否只能输入正整数
- positiveInteger: {
- type: Boolean,
- default: true
- }
- },
- computed: {
- getCursorSpacing() {
- return Number(uni.upx2px(this.cursorSpacing))
- }
- },
- data() {
- return {
- // 输入框的值
- inputValue: 1,
- // 长按定时器
- longPressTimer: null,
- // 标记值的改变是来自外部还是内部
- changeFromInner: false,
- // 内部定时器
- innerChangeTimer: null
- }
- },
- watch: {
- value(val) {
- // 只有value的改变是来自外部的时候,才去同步inputValue的值,否则会造成循环错误
- if (!this.changeFromInner) {
- this.updateInputValue()
- // 因为inputValue变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
- // 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
- // 将changeFromInner设置为false
- this.$nextTick(() => {
- this.changeFromInner = false
- })
- }
- },
- inputValue(newVal, oldVal) {
- // 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
- if (newVal === '') return
- let value = 0
- // 首先判断是否数值,并且在min和max之间,如果不是,使用原来值
- let isNumber = this.$t.test.number(newVal)
- if (isNumber && newVal >= this.min && newVal <= this.max) value = newVal
- else value = oldVal
-
- // 判断是否只能输入大于等于0的整数
- if (this.positiveInteger) {
- // 小于0或者带有小数点
- if (newVal < 0 || String(newVal).indexOf('.') !== -1) {
- value = Math.floor(newVal)
- // 双向绑定input的值,必须要使用$nextTick修改显示的值
- this.$nextTick(() => {
- this.inputValue = value
- })
- }
- }
- this.handleChange(value, 'change')
- },
- min() {
- this.updateInputValue()
- },
- max() {
- this.updateInputValue()
- }
- },
- created() {
- this.updateInputValue()
- },
- methods: {
- // 开始点击按钮
- touchStart(func) {
- // 先执行一遍方法,否则会造成松开手时,就执行了clearTimer,导致无法实现功能
- this[func]()
- // 如果没有开启长按功能,直接返回
- if (!this.longPress) return
- // 清空长按定时器,防止重复注册
- if (this.longPressTimer) {
- clearInterval(this.longPressTimer)
- this.longPressTimer = null
- }
- this.longPressTimer = setInterval(() => {
- // 执行加减操作
- this[func]()
- }, this.longPressTime)
- },
- // 清除定时器
- clearTimer() {
- this.$nextTick(() => {
- if (this.longPressTimer) {
- clearInterval(this.longPressTimer)
- this.longPressTimer = null
- }
- })
- },
- // 减
- minus() {
- this.computeValue('minus')
- },
- // 加
- plus() {
- this.computeValue('plus')
- },
- // 处理小数相加减出现溢出问题
- calcPlus(num1, num2) {
- let baseNum = 0, baseNum1 = 0, baseNum2 = 0
- 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 = 0, baseNum1 = 0, baseNum2 = 0
- 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)
- },
- // 处理操作后的值
- computeValue(type) {
- uni.hideKeyboard()
- if (this.disabled) return
- let value = 0
-
- if (type === 'minus') {
- // 减
- value = this.calcMinus(this.inputValue, this.step)
- } else if (type === 'plus') {
- // 加
- value = this.calcPlus(this.inputValue, this.step)
- }
- // 判断是否比最小值小和操作最大值
- if (value < this.min || value > this.max) return
-
- this.inputValue = value
- this.handleChange(value, type)
- },
- // 处理用户手动输入
- blurInput(event) {
- let val = 0,
- value = event.detail.value
- // 如果为非0-9数字组成,或者其第一位数值为0,直接让其等于min值
- // 这里不直接判断是否正整数,是因为用户传递的props min值可能为0
- if (!/(^\d+$)/.test(value) || value[0] == 0) {
- val = this.min
- } else {
- val = +value
- }
-
- if (val > this.max) {
- val = this.max
- } else if (val < this.min) {
- val = this.min
- }
- this.$nextTick(() => {
- this.inputValue = val
- })
- this.handleChange(val, 'blur')
- },
- // 获取焦点
- focusInput() {
- this.$emit('focus')
- },
- // 初始化inputValue
- updateInputValue() {
- let value = this.value
- if (value <= this.min) {
- value = this.min
- } else if (value >= this.max) {
- value = this.max
- }
-
- this.inputValue = Number(value)
- },
- // 处理值改变状态
- handleChange(value, type) {
- if (this.disabled) return
- // 清除定时器,防止混乱
- if (this.innerChangeTimer) {
- clearTimeout(this.innerChangeTimer)
- this.innerChangeTimer = null
- }
-
- // 内部修改值
- this.changeFromInner = true
- // 一定时间内,清除changeFromInner标记,否则内部值改变后
- // 外部通过程序修改value值,将会无效
- 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>
-
- .tn-number-box {
- display: inline-flex;
- align-items: center;
-
- &__btn {
- &__plus,&__minus {
- width: 60rpx;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- background-color: $tn-font-holder-color;
- }
-
- &__plus {
- border-radius: 0 8rpx 8rpx 0;
- }
-
- &__minus {
- border-radius: 8rpx 0 0 8rpx;
- }
-
- &--disabled {
- color: $tn-font-sub-color !important;
- background: $tn-font-holder-color !important;
- }
- }
-
- &__input {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- position: relative;
- text-align: center;
- box-sizing: border-box;
- padding: 0 4rpx;
- margin: 0 6rpx;
- background-color: $tn-font-holder-color;
-
- &--disabled {
- color: $tn-font-sub-color !important;
- background: $tn-font-holder-color !important;
- }
- }
- }
- </style>
|