Element UI 源码改造 —— 自定义数字输入框的实现

2021/5/13 14:55:47

本文主要是介绍Element UI 源码改造 —— 自定义数字输入框的实现,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

需求描述

实现方案

完整代码范例

1. 找到 Element UI源码中的el-input-number对应的文件

2. 新建文件 newElNumberInput.vue

3. 在目标文件中引入使用

4. 最终效果

友情提示 

需求描述

 表单中需要一个数字输入框,功能与 el-input-number 基本一样,但有以下区别:

1. 按上下方向键时,不改变输入的内容

2. 输入的值超出最大值或小于最小值时,保留输入的值,而不是当失去焦点时自动替换为最大或最小值

3. 向el-input一样,可以配置可清空按钮

实现方案

复制一份Element UI源码中的el-input-number对应的文件,改造成满足需求的组件。

完整代码范例

1. 找到 Element UI源码中的el-input-number对应的文件

node_modules/element-ui/packages/input-number/src/input-number.vue

2. 新建文件 newElNumberInput.vue

将源码input-number.vue 拷贝到 newElNumberInput.vue中,并按需求对源码进行修改,最终代码如下:

<template>
    <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"
                :clearable = 'clearable'
                :value="displayValue"
                :placeholder="placeholder"
                :disabled="inputNumberDisabled"
                :size="inputNumberSize"
                :max="max"
                :min="min"
                :name="name"
                :label="label"
                @blur="handleBlur"
                @focus="handleFocus"
                @input="handleInput"
                @change="handleInputChange">
        </el-input>
    </div>
</template>
<script>
    import ElInput from 'element-ui/packages/input';
    import Focus from 'element-ui/src/mixins/focus';
    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: {
            clearable:Boolean,
            step: {
                type: Number,
                default: 1
            },
            stepStrictly: {
                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,
            placeholder: String,
            precision: {
                type: Number,
                validator(val) {
                    return val >= 0 && val === parseInt(val, 10);
                }
            }
        },
        data() {
            return {
                currentValue: 0,
                userInput: null
            };
        },
        watch: {
            value: {
                immediate: true,
                handler(value) {
                    let newVal = value === undefined ? value : Number(value);
                    if (newVal !== undefined) {
                        if (isNaN(newVal)) {
                            return;
                        }
                        if (this.stepStrictly) {
                            const stepPrecision = this.getPrecision(this.step);
                            const precisionFactor = Math.pow(10, stepPrecision);
                            newVal = Math.round(newVal / this.step) * precisionFactor * this.step / precisionFactor;
                        }
                        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.userInput = null;
                    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() {
                const {value, step, getPrecision, precision} = this;
                const stepPrecision = getPrecision(step);
                if (precision !== undefined) {
                    if (stepPrecision > precision) {
                        console.warn('[Element Warn][InputNumber]precision should not be less than the decimal places of step');
                    }
                    return precision;
                } else {
                    return Math.max(getPrecision(value), stepPrecision);
                }
            },
            controlsAtRight() {
                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;
            },
            displayValue() {
                if (this.userInput !== null) {
                    return this.userInput;
                }
                let currentValue = this.currentValue;
                if (typeof currentValue === 'number') {
                    if (this.stepStrictly) {
                        const stepPrecision = this.getPrecision(this.step);
                        const precisionFactor = Math.pow(10, stepPrecision);
                        currentValue = Math.round(currentValue / this.step) * precisionFactor * this.step / precisionFactor;
                    }
                    if (this.precision !== undefined) {
                        currentValue = currentValue.toFixed(this.precision);
                    }
                }
                return currentValue;
            }
        },
        methods: {
            toPrecision(num, precision) {
                if (precision === undefined) precision = this.numPrecision;
                return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
            },
            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;
            },
            _increase(val, step) {
                if (typeof val !== 'number' && val !== undefined) return this.currentValue;
                const precisionFactor = Math.pow(10, this.numPrecision);
                // Solve the accuracy problem of JS decimal calculation by converting the value to integer.
                return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
            },
            _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);
            },
            handleBlur(event) {
                this.$emit('blur', event);
            },
            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;
            },
            handleInput(value) {
                this.userInput = value;
            },
            handleInputChange(value) {
                const newVal = value === '' ? undefined : Number(value);
                if (!isNaN(newVal) || value === '') {
                    this.setCurrentValue(newVal);
                } else {
                    this.setCurrentValue(undefined)
                }
                this.userInput = null;
            },
            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>

3. 在目标文件中引入使用

<template>
    <div style="padding: 30px">
        <el-form ref="formRef" :model="formData" size="mini" label-width="100px">
            <el-form-item label="分数" prop='score'
                          :rules="[
                           { required: true, message: '不能为空', trigger: 'blur' },
                           {type: 'number', min:0,max:99,message: '请输入0-99之间的数字', trigger: 'blur' }
                            ]"
            >
                <NewElNumberInput clearable :controls="false" :max="99" v-model.number="formData.score"/>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" @click="submit">提交</el-button>
            </el-form-item>
        </el-form>
    </div>
</template>
<script>
    import NewElNumberInput from './newElNumberInput'

    export default {
        components: {NewElNumberInput},
        data() {
            return {
                formData: {}
            }
        },
        methods: {
            submit() {
                this.$refs.formRef.validate((valid) => {
                    if (valid) {
                        alert('通过表单校验!');
                    } else {
                        console.log('表单校验失败!');
                        return false;
                    }
                });
            }
        }
    }
</script>
<style scoped>
</style>

4. 最终效果

友情提示

若有其他通过改造 Element UI 源码即可快速实现的功能,都可以参考本例实现。



这篇关于Element UI 源码改造 —— 自定义数字输入框的实现的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程