input-number
組件是用來(lái)輸入數(shù)字的,也不是很復(fù)雜恢准。
最外層
最外層是一個(gè)div.el-input-number
,上面有一些動(dòng)態(tài)的class
。
<div class="el-input-number"
:class="[
size ? 'el-input-number--' + size : '',
{ 'is-disabled': disabled },
{ 'is-without-controls': !controls}
]"
>
</div>
size
size
是一個(gè)prop
寞缝,用來(lái)控制其大小的。
props: {
size: String,
}
disabled
disabled
也是prop
仰泻,用來(lái)控制是否禁用荆陆。
props: {
disabled: Boolean,
}
controls
controls
也是prop
,用來(lái)控制是否有控制器集侯。
props: {
controls: {
type: Boolean,
default: true
}
}
減少按鈕
減少按鈕是一個(gè)span
被啼。
<span
v-if="controls"
class="el-input-number__decrease el-icon-minus"
:class="{'is-disabled': minDisabled}"
v-repeat-click="decrease"
>
</span>
minDisabled
minDisabled
是一個(gè)計(jì)算屬性,用來(lái)控制是否禁用該按鈕棠枉。
computed: {
minDisabled() {
return this.accSub(this.value, this.step) < this.min;
},
}
v-repeat-click
v-repeat-click
是一個(gè)指令浓体,用來(lái)控制左鍵按下時(shí)不斷觸發(fā)事件。
directives: {
repeatClick: {
bind(el, binding, vnode) {
let interval = null; // 定時(shí)器
let startTime;
const handler = () => vnode.context[binding.expression](); // 獲取表達(dá)式的內(nèi)容
const clear = () => {
// 如果當(dāng)前時(shí)間距離開始時(shí)間少于 100ms辈讶,執(zhí)行 handler
if (new Date() - startTime < 100) {
handler();
}
// 清除計(jì)時(shí)器
clearInterval(interval);
interval = null;
};
// 綁定鼠標(biāo)點(diǎn)擊下的事件
on(el, 'mousedown', () => {
startTime = new Date(); // 更新當(dāng)前時(shí)間
once(document, 'mouseup', clear); // 給鼠標(biāo)抬起綁定一次性事件 clear
interval = setInterval(handler, 100); // 開始定時(shí)器
});
}
}
},
decrease
控制數(shù)值減少命浴。
methods: {
decrease() {
if (this.minDisabled) return; // 如果減少按鈕不可以用,直接返回
const value = this.value || 0; // 默認(rèn)為0
if (this.accSub(value, this.step) < this.min || this.disabled) return; // 如果減少后小于最小值贱除,或者當(dāng)前input不可用都直接返回
this.currentValue = this.accSub(value, this.step); // 否則設(shè)置新的值
},
}
其中accSub
是另一個(gè)用來(lái)做減法的方法咳促,用來(lái)處理小數(shù)的精度問題。
methods: {
// arg1 當(dāng)前值
// arg2 步進(jìn)值
accSub(arg1, arg2) {
var r1, r2, m, n;
try {
r1 = arg1.toString().split('.')[1].length; // 處理當(dāng)前值小數(shù)的位數(shù)
} catch (e) {
r1 = 0;
}
try {
r2 = arg2.toString().split('.')[1].length; // 處理步進(jìn)值小數(shù)的位數(shù)
} catch (e) {
r2 = 0;
}
m = Math.pow(10, Math.max(r1, r2)); // 為了轉(zhuǎn)換成整數(shù)勘伺,好處理精度問題
n = (r1 >= r2) ? r1 : r2; // 精度取較大值
return parseFloat(((arg1 * m - arg2 * m) / m).toFixed(n));
},
}
增加按鈕
增加按鈕也是一個(gè)span
跪腹,絕大多數(shù)邏輯和減少按鈕一樣,只是減法變成加法飞醉,在此不再進(jìn)行贅述冲茸。
el-input
最后是內(nèi)嵌了一個(gè)el-input
來(lái)實(shí)現(xiàn)輸入框,并且進(jìn)行了處理可以將input-number
的具名slot
的append
和prepend
傳入el-input
中缅帘,這樣可以更好地復(fù)用轴术。
<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
沒有解釋過(guò),它會(huì)設(shè)置el-input
的值钦无。
methods: {
handleBlur() {
this.$refs.input.setCurrentValue(this.currentValue);
}
}
其他
data
初始化的時(shí)候逗栽,修正value
data() {
let value = this.value;
// 如果輸入值小于最小值,則設(shè)為最小值
if (value < this.min) {
this.$emit('input', this.min);
value = this.min;
}
// 如果輸入值大于最大值失暂,則設(shè)為最大值
if (value > this.max) {
this.$emit('input', this.max);
value = this.max;
}
return {
currentValue: value
};
},
watch
watch: {
value(val) {
this.currentValue = val; // value改變的時(shí)候彼宠,currentValue也改變
},
currentValue(newVal, oldVal) {
if (newVal <= this.max && newVal >= this.min) { // 新值合法
this.$emit('change', newVal, oldVal); // 觸發(fā) change 事件
this.$emit('input', newVal); // 觸發(fā) input 事件
} else { // 不合法就回復(fù)
this.currentValue = oldVal;
}
}
},