序言
这一篇的话 , 我直接挑InputNumber下手了,跳过了Input , 直接就拿InputNumber来讲, 感觉InputNumber比较简单,废话就不多说了,直接开始吧。
结构分析
老样子还是根据三个角度来分析,Dom、数据属性、事件。
慢慢的发现,以这样的角度来分析的话 还可以,如果有多的东西的话 可以慢慢加。
Dom
<div>
<span class="el-input-number__decrease">
<i></i>
</span>
<span class="el-input-number__increase">
<i></i>
</span>
<el-input></el-input>
</div>
<div
@dragstart.prevent
:class="[
'el-input-number',
inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
{ 'is-disabled': inputNumberDisabled },
{ 'is-without-controls': !controls },
{ 'is-controls-right': controlsAtRight },
]"
>
<span
class="el-input-number__decrease"
role="button"
v-if="controls"
v-repeat-click="decrease"
:class="{ 'is-disabled': minDisabled }"
@keydown.enter="decrease"
>
<i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
</span>
<span
class="el-input-number__increase"
role="button"
v-if="controls"
v-repeat-click="increase"
:class="{ 'is-disabled': maxDisabled }"
@keydown.enter="increase"
>
<i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
</span>
<el-input
ref="input"
:value="displayValue"
:placeholder="placeholder"
:disabled="inputNumberDisabled"
:size="inputNumberSize"
:max="max"
:min="min"
:name="name"
:label="label"
@keydown.up.native.prevent="increase"
@keydown.down.native.prevent="decrease"
@blur="handleBlur"
@focus="handleFocus"
@input="handleInput"
@change="handleInputChange"
>
</el-input>
</div>
Dom 这块的话 倒是没什么特别的结构就是正常的 增加、减少、数量的结构。
外层div (el-input-number) 属性
-
@dragstart.prevent : 禁止了div的默认拖动行为 不设置中间的数字底色可以被拖动 小细节!! 很不错
-
class :
--inputNumberSize ? 'el-input-number--' + inputNumberSize : 计数器尺寸
--is-disabled': inputNumberDisabled : 禁用
--is-without-controls : !controls : 是否使用控制按钮
--is-controls-right : controlsAtRight : 控制按钮位置
增加 递减 (el-input-number__decrease || el-input-number__increase) 两个属性基本一样
-
role="button" 之前说过的为盲人提供的访问网络的便利程序
-
v-if="controls" 是否使用控制按钮
-
v-repeat-click="decrease" 替换了原有的点击事件 使用了自定义指令(directives)来实现 知识点!
-
:class="{ 'is-disabled': minDisabled }" 禁用
-
@keydown.enter="decrease" 键盘回车事件
展示的数字
-
:value="displayValue" 绑定的value值
-
:placeholder="placeholder" 输入框默认 placeholder
-
:disabled="inputNumberDisabled" 是否禁用计数器
-
:size="inputNumberSize" 计数器尺寸
-
:max="max" 最大值
-
:min="min" 最小值
-
:name="name" 原生属性
-
:label="label" 输入框关联的label文字
-
@keydown.up.native.prevent="increase" 上键
-
@keydown.down.native.prevent="decrease" 下键
-
@blur="handleBlur" 失焦
-
@focus="handleFocus" 聚焦
数据属性
props data
props: {
step: { // 计数器步长 每次di
type: Number,
default: 1,
},
stepStrictly: { // 只能输入 step 的倍数 严格步数
type: Boolean,
default: false,
},
max: { //设置计数器允许的最大值
type: Number,
default: Infinity,
},
min: { //设置计数器允许的最小值
type: Number,
default: -Infinity,
},
value: {}, //绑定值
disabled: Boolean, //是否禁用计数器
size: String, //计数器尺寸
controls: { //是否使用控制按钮
type: Boolean,
default: true,
},
controlsPosition: { //控制按钮位置
type: String,
default: "",
},
name: String, //原生属性
label: String, //输入框关联的label文字
placeholder: String, //输入框默认 placeholder
precision: { //数值精度
type: Number,
validator(val) {
return val >= 0 && val === parseInt(val, 10);
},
},
},
data() {
return {
currentValue: 0, // 当前的值
userInput: null, // 上次的值
};
},
watch
watch: {
value: {
//确认是否以当前的初始值执行handler的函数。
immediate: true,
handler(value) {
//Number() 函数把对象的值转换为数字。
let newVal = value === undefined ? value : Number(value);
if (newVal !== undefined) {
if (isNaN(newVal)) {
return;
}
if (this.precision !== undefined) {
//如果数值精度存在,将数字按精度转换
newVal = this.toPrecision(newVal, this.precision);
}
}
if (newVal >= this.max) newVal = this.max;
if (newVal <= this.min) newVal = this.min;
this.currentValue = newVal;
this.$emit('input', newVal);
}
}
},
事件
//按精度转换数值
toPrecision(num, precision) {
if (precision === undefined) precision = this.numPrecision;
//toFixed() 方法可把 Number 四舍五入为指定小数位数的数字,返回字符串;parseFloat()函数可解析一个字符串,并返回一个浮点数。
return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
},
//获取value的小数位数
getPrecision(value) {
if (value === undefined) return 0;
const valueString = value.toString();
const dotPosition = valueString.indexOf(".");
let precision = 0;
if (dotPosition !== -1) {
precision = valueString.length - dotPosition - 1;
}
return precision;
},
//返回value增加step后的值
_increase(val, step) {
if (typeof val !== "number" && val !== undefined) return this.currentValue;
const precisionFactor = Math.pow(10, this.numPrecision);
return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
},
//返回value减去step后的值
_decrease(val, step) {
if (typeof val !== "number" && val !== undefined) return this.currentValue;
const precisionFactor = Math.pow(10, this.numPrecision);
return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
},
// 点击增加按钮
increase() {
if (this.inputNumberDisabled || this.maxDisabled) return;
const value = this.value || 0;
const newVal = this._increase(value, this.step);
this.setCurrentValue(newVal);
},
// 点击递减按钮
decrease() {
if (this.inputNumberDisabled || this.minDisabled) return;
const value = this.value || 0;
const newVal = this._decrease(value, this.step);
this.setCurrentValue(newVal);
},
// 中间input 失焦事件
handleBlur(event) {
this.$emit("blur", event);
},
// 中间input 聚焦事件
handleFocus(event) {
this.$emit("focus", event);
},
// 用来设置 中间所显示的数字的值
setCurrentValue(newVal) {
const oldVal = this.currentValue;
if (typeof newVal === "number" && this.precision !== undefined) {
newVal = this.toPrecision(newVal, this.precision);
}
if (newVal >= this.max) newVal = this.max;
if (newVal <= this.min) newVal = this.min;
if (oldVal === newVal) return;
this.userInput = null;
this.$emit("input", newVal);
this.$emit("change", newVal, oldVal);
this.currentValue = newVal;
},
// 中间input事件
handleInput(value) {
this.userInput = value;
},
handleInputChange(value) {
const newVal = value === "" ? undefined : Number(value);
if (!isNaN(newVal) || value === "") {
this.setCurrentValue(newVal);
}
this.userInput = null;
},
select() {
this.$refs.input.select();
},
招聘广告
言重式招聘 寻人!!!寻志同道合之人、寻竭忠尽智之人、寻深思远虑之人、寻勤恳至诚之人
浙江大华技术股份有限公司-软研-智慧城市产品研发部招聘高级前端!!!!!
欢迎大家来聊,有意向可发送简历到chen_zhen@dahuatech.com
尽量宽恕别人,而决不要原谅自己。——————无奖竞猜