<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結構javascript
<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代碼以下:css
.el-input-number .el-input__inner { -webkit-appearance: none; padding-left: 50px; padding-right: 50px; text-align: center; }
這個inputNumber源碼還算簡單,多看幾遍就懂了html
<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>
<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>