• 计时器组件的封装对比


    参考UI组件库:

    1.elmentUI:https://github.com/ElemeFE/element/tree/master

    2.antd;https://github.com/vueComponent/ant-design-vue/

    3.Iview;https://github.com/view-design/ViewUI

    使用demo:iView和antd的API基本一致

    https://codepen.io/share-web/pen/NWRKGOX

    https://www.iviewui.com/components/input-number

    el-inputNumber:

    <template>
    <!-- @dragstart.prevent:拖拽开始就阻止自身的默认事件 -->
      <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>
        <!-- 输入回调:鼠标事件+键盘事件:
            新需求:对displayValue的符号进行过滤替换显示
         -->
        <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>
    </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: {
          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);
            }
            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>

    a-input-number:jsx语法

    InputNumber:iView计数器

    <template>
        <div :class="wrapClasses">
            <!-- 图标 -->
            <div :class="handlerClasses">
                <a
                    @click="up"
                    :class="upClasses">
                    <span :class="innerUpClasses" @click="preventDefault"></span>
                </a>
                <a
                    @click="down"
                    :class="downClasses">
                    <span :class="innerDownClasses" @click="preventDefault"></span>
                </a>
            </div>
            <!-- 输入框 -->
            <div :class="inputWrapClasses">
                <input
                    :id="elementId"
                    :class="inputClasses"
                    :disabled="itemDisabled"
                    autocomplete="off"
                    spellcheck="false"
                    :autofocus="autofocus"
                    @focus="focus"
                    @blur="blur"
                    @keydown.stop="keyDown"
                    @input="change"
                    @mouseup="preventDefault"
                    @change="change"
                    :readonly="readonly || !editable"
                    :name="name"
                    :value="formatterValue"
                    :placeholder="placeholder">
            </div>
        </div>
    </template>
    <script>
        import { oneOf, findComponentUpward } from '../../utils/assist';
        import Emitter from '../../mixins/emitter';
        import mixinsForm from '../../mixins/form';
        const prefixCls = 'ivu-input-number';
        const iconPrefixCls = 'ivu-icon';
        function addNum (num1, num2) {
            let sq1, sq2, m;
            try {
                sq1 = num1.toString().split('.')[1].length;
            }catch (e) {
                sq1 = 0;
            }
            try {
                sq2 = num2.toString().split('.')[1].length;
            }catch (e) {
                sq2 = 0;
            }
    //        if (sq1 === 0 || sq2 === 0) {
    //            return num1 + num2;
    //        } else {
    //            m = Math.pow(10, Math.max(sq1, sq2));
    //            return (num1 * m + num2 * m) / m;
    //        }
            m = Math.pow(10, Math.max(sq1, sq2));
            return (Math.round(num1 * m) + Math.round(num2 * m)) / m;
        }
        export default {
            name: 'InputNumber',
            mixins: [ Emitter, mixinsForm ],
            props: {
                max: {
                    type: Number,
                    default: Infinity
                },
                min: {
                    type: Number,
                    default: -Infinity
                },
                step: {
                    type: Number,
                    default: 1
                },
                activeChange: {
                    type: Boolean,
                    default: true
                },
                value: {
                    type: Number,
                    default: 1
                },
                size: {
                    validator (value) {
                        return oneOf(value, ['small', 'large', 'default']);
                    },
                    default () {
                        return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
                    }
                },
                disabled: {
                    type: Boolean,
                    default: false
                },
                autofocus: {
                    type: Boolean,
                    default: false
                },
                readonly: {
                    type: Boolean,
                    default: false
                },
                editable: {
                    type: Boolean,
                    default: true
                },
                name: {
                    type: String
                },
                precision: {
                    type: Number
                },
                elementId: {
                    type: String
                },
                formatter: {
                    type: Function
                },
                parser: {
                    type: Function
                },
                placeholder: {
                    type: String,
                    default: ''
                },
            },
            data () {
                return {
                    focused: false,
                    upDisabled: false,
                    downDisabled: false,
                    currentValue: this.value
                };
            },
            computed: {
                wrapClasses () {
                    return [
                        `${prefixCls}`,
                        {
                            [`${prefixCls}-${this.size}`]: !!this.size,
                            [`${prefixCls}-disabled`]: this.itemDisabled,
                            [`${prefixCls}-focused`]: this.focused
                        }
                    ];
                },
                handlerClasses () {
                    return `${prefixCls}-handler-wrap`;
                },
                upClasses () {
                    return [
                        `${prefixCls}-handler`,
                        `${prefixCls}-handler-up`,
                        {
                            [`${prefixCls}-handler-up-disabled`]: this.upDisabled
                        }
                    ];
                },
                innerUpClasses () {
                    return `${prefixCls}-handler-up-inner ${iconPrefixCls} ${iconPrefixCls}-ios-arrow-up`;
                },
                downClasses () {
                    return [
                        `${prefixCls}-handler`,
                        `${prefixCls}-handler-down`,
                        {
                            [`${prefixCls}-handler-down-disabled`]: this.downDisabled
                        }
                    ];
                },
                innerDownClasses () {
                    return `${prefixCls}-handler-down-inner ${iconPrefixCls} ${iconPrefixCls}-ios-arrow-down`;
                },
                inputWrapClasses () {
                    return `${prefixCls}-input-wrap`;
                },
                inputClasses () {
                    return `${prefixCls}-input`;
                },
                precisionValue () {
                    // can not display 1.0
                    if(!this.currentValue) return this.currentValue;
                    return this.precision ? this.currentValue.toFixed(this.precision) : this.currentValue;
                },
                formatterValue () {
                    if (this.formatter && this.precisionValue !== null) {
                        return this.formatter(this.precisionValue);
                    } else {
                        return this.precisionValue;
                    }
                }
            },
            methods: {
                preventDefault (e) {
                    e.preventDefault();
                },
                up (e) {
                    const targetVal = Number(e.target.value);
                    if (this.upDisabled && isNaN(targetVal)) {
                        return false;
                    }
                    this.changeStep('up', e);
                },
                down (e) {
                    const targetVal = Number(e.target.value);
                    if (this.downDisabled && isNaN(targetVal)) {
                        return false;
                    }
                    this.changeStep('down', e);
                },
                changeStep (type, e) {
                    if (this.itemDisabled || this.readonly) {
                        return false;
                    }
                    const targetVal = Number(e.target.value);
                    let val = Number(this.currentValue);
                    const step = Number(this.step);
                    if (isNaN(val)) {
                        return false;
                    }
                    // input a number, and key up or down
                    if (!isNaN(targetVal)) {
                        if (type === 'up') {
                            if (addNum(targetVal, step) <= this.max) {
                                val = targetVal;
                            } else {
                                return false;
                            }
                        } else if (type === 'down') {
                            if (addNum(targetVal, -step) >= this.min) {
                                val = targetVal;
                            } else {
                                return false;
                            }
                        }
                    }
                    if (type === 'up') {
                        val = addNum(val, step);
                    } else if (type === 'down') {
                        val = addNum(val, -step);
                    }
                    this.setValue(val);
                },
                setValue (val) {
                    // 如果 step 是小数,且没有设置 precision,是有问题的
                    if (val && !isNaN(this.precision)) val = Number(Number(val).toFixed(this.precision));
                    const {min, max} = this;
                    if (val!==null) {
                        if (val > max) {
                            val = max;
                        } else if (val < min) {
                            val = min;
                        }
                    }
                    this.$nextTick(() => {
                        this.currentValue = val;
                        this.$emit('input', val);
                        this.$emit('on-change', val);
                        this.dispatch('FormItem', 'on-form-change', val);
                    });
                },
                focus (event) {
                    this.focused = true;
                    this.$emit('on-focus', event);
                },
                blur () {
                    this.focused = false;
                    this.$emit('on-blur');
                    if (!findComponentUpward(this, ['DatePicker', 'TimePicker', 'Cascader', 'Search'])) {
                        this.dispatch('FormItem', 'on-form-blur', this.currentValue);
                    }
                },
                keyDown (e) {
                    if (e.keyCode === 38) {
                        e.preventDefault();
                        this.up(e);
                    } else if (e.keyCode === 40) {
                        e.preventDefault();
                        this.down(e);
                    }
                },
                change (event) {
                    if (event.type == 'change' && this.activeChange) return;
                    if (event.type == 'input' && !this.activeChange) return;
                    let val = event.target.value.trim();
                    if (this.parser) {
                        val = this.parser(val);
                    }
                    const isEmptyString = val.length === 0;
                    if(isEmptyString){
                        this.setValue(null);
                        return;
                    }
                    if (event.type == 'input' && val.match(/^-?.?$|.$/)) return; // prevent fire early if decimal. If no more input the change event will fire later
                    val = Number(val);
                    if (!isNaN(val)) {
                        this.currentValue = val;
                        this.setValue(val);
                    } else {
                        event.target.value = this.currentValue;
                    }
                },
                changeVal (val) {
                    val = Number(val);
                    if (!isNaN(val)) {
                        const step = this.step;
                        this.upDisabled = val + step > this.max;
                        this.downDisabled = val - step < this.min;
                    } else {
                        this.upDisabled = true;
                        this.downDisabled = true;
                    }
                }
            },
            mounted () {
                this.changeVal(this.currentValue);
            },
            watch: {
                value (val) {
                    this.currentValue = val;
                },
                currentValue (val) {
                    this.changeVal(val);
                },
                min () {
                    this.changeVal(this.currentValue);
                },
                max () {
                    this.changeVal(this.currentValue);
                }
            }
        };
    </script>

    -待续-

  • 相关阅读:
    WP8.1通过StreamSocket连接C++服务器
    WP10通过StreamSocket连接C++服务器
    二维背包(两个限制条件)
    dp(多重背包)
    dp(完全背包)
    dfs(迷宫)
    bfs迷宫
    蚁人cp数
    二分(老死不相往来)
    前缀和(狼和野牛)
  • 原文地址:https://www.cnblogs.com/wheatCatcher/p/14033707.html
Copyright © 2020-2023  润新知