input-number
组件是用来输入数字的,也不是很复杂。
最外层
最外层是一个div.el-input-number
,上面有一些动态的class
。
<div class="el-input-number"
:class="[
size ? 'el-input-number--' + size : '',
{ 'is-disabled': disabled },
{ 'is-without-controls': !controls}
]"
>
</div>
size
size
是一个prop
,用来控制其大小的。
props: {
size: String,
}
disabled
disabled
也是prop
,用来控制是否禁用。
props: {
disabled: Boolean,
}
controls
controls
也是prop
,用来控制是否有控制器。
props: {
controls: {
type: Boolean,
default: true
}
}
减少按钮
减少按钮是一个span
。
<span
v-if="controls"
class="el-input-number__decrease el-icon-minus"
:class="{'is-disabled': minDisabled}"
v-repeat-click="decrease"
>
</span>
minDisabled
minDisabled
是一个计算属性,用来控制是否禁用该按钮。
computed: {
minDisabled() {
return this.accSub(this.value, this.step) < this.min;
},
}
v-repeat-click
v-repeat-click
是一个指令,用来控制左键按下时不断触发事件。
directives: {
repeatClick: {
bind(el, binding, vnode) {
let interval = null; // 定时器
let startTime;
const handler = () => vnode.context[binding.expression](); // 获取表达式的内容
const clear = () => {
// 如果当前时间距离开始时间少于 100ms,执行 handler
if (new Date() - startTime < 100) {
handler();
}
// 清除计时器
clearInterval(interval);
interval = null;
};
// 绑定鼠标点击下的事件
on(el, 'mousedown', () => {
startTime = new Date(); // 更新当前时间
once(document, 'mouseup', clear); // 给鼠标抬起绑定一次性事件 clear
interval = setInterval(handler, 100); // 开始定时器
});
}
}
},
decrease
控制数值减少。
methods: {
decrease() {
if (this.minDisabled) return; // 如果减少按钮不可以用,直接返回
const value = this.value || 0; // 默认为0
if (this.accSub(value, this.step) < this.min || this.disabled) return; // 如果减少后小于最小值,或者当前input不可用都直接返回
this.currentValue = this.accSub(value, this.step); // 否则设置新的值
},
}
其中accSub
是另一个用来做减法的方法,用来处理小数的精度问题。
methods: {
// arg1 当前值
// arg2 步进值
accSub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split('.')[1].length; // 处理当前值小数的位数
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length; // 处理步进值小数的位数
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2)); // 为了转换成整数,好处理精度问题
n = (r1 >= r2) ? r1 : r2; // 精度取较大值
return parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n));
},
}
增加按钮
增加按钮也是一个span
,绝大多数逻辑和减少按钮一样,只是减法变成加法,在此不再进行赘述。
el-input
最后是内嵌了一个el-input
来实现输入框,并且进行了处理可以将input-number
的具名slot
的append
和prepend
传入el-input
中,这样可以更好地复用。
<el-input
v-model.number="currentValue"
@keydown.up.native="increase"
@keydown.down.native="decrease"
@blur="handleBlur"
:disabled="disabled"
:size="size"
ref="input"
>
<template slot="prepend" v-if="$slots.prepend">
<slot name="prepend"></slot>
</template>
<template slot="append" v-if="$slots.append">
<slot name="append"></slot>
</template>
</el-input>
这里只有handleBlur
没有解释过,它会设置el-input
的值。
methods: {
handleBlur() {
this.$refs.input.setCurrentValue(this.currentValue);
}
}
其他
data
初始化的时候,修正value
data() {
let value = this.value;
// 如果输入值小于最小值,则设为最小值
if (value < this.min) {
this.$emit('input', this.min);
value = this.min;
}
// 如果输入值大于最大值,则设为最大值
if (value > this.max) {
this.$emit('input', this.max);
value = this.max;
}
return {
currentValue: value
};
},
watch
watch: {
value(val) {
this.currentValue = val; // value改变的时候,currentValue也改变
},
currentValue(newVal, oldVal) {
if (newVal <= this.max && newVal >= this.min) { // 新值合法
this.$emit('change', newVal, oldVal); // 触发 change 事件
this.$emit('input', newVal); // 触发 input 事件
} else { // 不合法就回复
this.currentValue = oldVal;
}
}
},
网友评论