element-ui inputNumber、Card 、Breadcrumb组件源码分析整理笔记(三)
程序员文章站
2022-07-02 13:58:15
inputNumber组件 解析: (1)先看下html结构 左边的减号和右边的加号是通过绝对定位,设置在input左右的padding位置的,input的css代码如下: 这个inputNumber源码还算简单,多看几遍就懂了 Card 组件 Breadcrumb组件 ......
inputnumber组件
<template> <!--@dragstart.prevent禁止input中数字的拖动--> <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="currentinputvalue" :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" @change="handleinputchange"> </el-input> </div> </template> <script> import elinput from 'element-ui/packages/input'; import focus from 'element-ui/src/mixins/focus'; //repeatclick,用来控制左键按下时不断触发事件 import repeatclick from 'element-ui/src/directives/repeat-click'; export default { name: 'elinputnumber', mixins: [focus('input')], inject: { elform: { default: '' }, elformitem: { default: '' } }, directives: { repeatclick: repeatclick }, components: { elinput }, props: { step: { //计数器步长 type: number, default: 1 }, 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 }; }, 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); } } }, computed: { // 返回当前减号是否被禁用 mindisabled() { // 当前值-计数器步长<最小值时,减号被禁用,不能再继续减 return this._decrease(this.value, this.step) < this.min; }, maxdisabled() { return this._increase(this.value, this.step) > this.max; }, //返回数值的精度 numprecision() { // precision 的值必须是一个非负整数,并且不能小于 step 的小数位数。 const { value, step, getprecision, precision } = this; const stepprecision = getprecision(step); if (precision !== undefined) { //如果step 的小数位数大于数值精度时,控制台输出警告并返回数值精度 if (stepprecision > precision) { console.warn('[element warn][inputnumber]precision should not be less than the decimal places of step'); } return precision; } else { //如果step 的小数位数小于数值精度时,再比较数值的精度和step的精度,取最大值 return math.max(getprecision(value), stepprecision); } }, // 控制按钮的位置 controlsatright() { // 当控制按钮存在,并且控制按钮的位置为right时,此处通过添加is-controls-right类来改变控制按钮的位置,使控制按钮在右边显示。 return this.controls && this.controlsposition === 'right'; }, _elformitemsize() { return (this.elformitem || {}).elformitemsize; }, //计数器的大小 inputnumbersize() { return this.size || this._elformitemsize || (this.$element || {}).size; }, // 是否禁用计数器 inputnumberdisabled() { return this.disabled || (this.elform || {}).disabled; }, currentinputvalue() { const currentvalue = this.currentvalue; if (typeof currentvalue === 'number' && this.precision !== undefined) { return currentvalue.tofixed(this.precision); } else { return currentvalue; } } }, methods: { //按精度转换数值 toprecision(num, precision) { if (precision === undefined) precision = this.numprecision; //tofixed() 方法可把 number 四舍五入为指定小数位数的数字,返回字符串;parsefloat()函数可解析一个字符串,并返回一个浮点数。 return parsefloat(parsefloat(number(num).tofixed(precision))); }, //获取value的小数位数 getprecision(value) { if (value === undefined) return 0; const valuestring = value.tostring(); const dotposition = valuestring.indexof('.'); let precision = 0; if (dotposition !== -1) { //valuestring.length减去小数点前面的位数,剩下的就是小数点后面的位数 precision = valuestring.length - dotposition - 1; } return precision; }, _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; //math.pow()计算10的this.numprecision次方 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); }, handleblur(event) { this.$emit('blur', event); this.$refs.input.setcurrentvalue(this.currentinputvalue); }, 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) { //改变input的当前值 this.$refs.input.setcurrentvalue(this.currentinputvalue); return; } this.$emit('input', newval); this.$emit('change', newval, oldval); this.currentvalue = newval; }, handleinputchange(value) { const newval = value === '' ? undefined : number(value); if (!isnan(newval) || value === '') { this.setcurrentvalue(newval); } }, select() { this.$refs.input.select(); } }, mounted() { let innerinput = this.$refs.input.$refs.input; innerinput.setattribute('role', 'spinbutton'); innerinput.setattribute('aria-valuemax', this.max); innerinput.setattribute('aria-valuemin', this.min); innerinput.setattribute('aria-valuenow', this.currentvalue); innerinput.setattribute('aria-disabled', this.inputnumberdisabled); }, updated() { if (!this.$refs || !this.$refs.input) return; const innerinput = this.$refs.input.$refs.input; innerinput.setattribute('aria-valuenow', this.currentvalue); } }; </script>
解析:
(1)先看下html结构
<div class="el-input-number"> <!--左边的减号--> <span class="el-input-number__decrease"> <i class="el-icon-minus"></i> </span> <!--右边的加号--> <span class="el-input-number__increase"> <i class="el-icon-plus"></i> </span> <!--中间的输入框--> <el-input ref="input"></el-input> </div>
左边的减号和右边的加号是通过绝对定位,设置在input左右的padding位置的,input的css代码如下:
.el-input-number .el-input__inner { -webkit-appearance: none; padding-left: 50px; padding-right: 50px; text-align: center; }
这个inputnumber源码还算简单,多看几遍就懂了
card 组件
<template> <div class="el-card" :class="shadow ? 'is-' + shadow + '-shadow' : 'is-always-shadow'"> <!--头部:设置 header,也可以通过 slot#header 传入 dom--> <div class="el-card__header" v-if="$slots.header || header"> <slot name="header">{{ header }}</slot> </div> <!--内容部分--> <div class="el-card__body" :style="bodystyle"> <slot></slot> </div> </div> </template> <script> export default { name: 'elcard', props: { header: {}, //设置 header,也可以通过 slot#header 传入dom bodystyle: {}, //设置 body 的样式 shadow: { //设置阴影显示时机 type: string } } }; </script>
breadcrumb组件
<template> <span class="el-breadcrumb__item"> <span :class="['el-breadcrumb__inner', to ? 'is-link' : '']" ref="link" role="link"> <!--插入文字--> <slot></slot> </span> <!--图标分隔符--> <i v-if="separatorclass" class="el-breadcrumb__separator" :class="separatorclass"></i> <!--分隔符--> <span v-else class="el-breadcrumb__separator" role="presentation">{{separator}}</span> </span> </template> <script> export default { name: 'elbreadcrumbitem', props: { to: {}, //路由跳转对象,同 vue-router 的 to replace: boolean //在使用 to 进行路由跳转时,启用 replace 将不会向 history 添加新记录 }, data() { return { separator: '', separatorclass: '' }; }, inject: ['elbreadcrumb'], mounted() { //获取父组件的separator this.separator = this.elbreadcrumb.separator; //获取父组件的separatorclass this.separatorclass = this.elbreadcrumb.separatorclass; const link = this.$refs.link; link.setattribute('role', 'link'); //添加点击事件 link.addeventlistener('click', _ => { const { to, $router } = this; if (!to || !$router) return; //根据replace的值确定是replace还是push,replace 将不会向 history 添加新记录 this.replace ? $router.replace(to) : $router.push(to); }); } }; </script>
推荐阅读
-
element-ui Tag、Dialog组件源码分析整理笔记(五)
-
element-ui button组件 radio组件源码分析整理笔记(一)
-
element-ui Upload 上传组件源码分析整理笔记(十四)
-
element-ui input组件源码分析整理笔记(六)
-
element-ui Steps步骤条组件源码分析整理笔记(九)
-
element-ui switch组件源码分析整理笔记(二)
-
element-ui inputNumber、Card 、Breadcrumb组件源码分析整理笔记(三)
-
element-ui Rate组件源码分析整理笔记(十三)
-
element-ui Message组件源码分析整理笔记(八)
-
element-ui Tag、Dialog组件源码分析整理笔记(五)