ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-05-13 14:31:40  阅读:300  来源: 互联网

标签:return 自定义 precision value Element newVal step 源码 const


目录

需求描述

实现方案

完整代码范例

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 源码即可快速实现的功能,都可以参考本例实现。

标签:return,自定义,precision,value,Element,newVal,step,源码,const
来源: https://blog.csdn.net/weixin_41192489/article/details/116748305

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有