Skip to main content

Command Palette

Search for a command to run...

vue2 inputNumber组件

Updated
•3 min read•View as Markdown

<!-- input-number/index.vue -->
<template>
<div class="container">
  <r-input
    v-model="inputValue"
    center
    :disabled="disabled"
    @change="handleChange"
    >
    <template v-slot:prepend>
      <div @click="handleClick('decrease')" class="decrease" :class="{is_decrease_disbaled: decreaseDisabled }">-</div>
    </template>
    <template v-slot:append>
      <div @click="handleClick('increase')" class="increase"
        :class="{ is_increase_disbaled: increaseDisabled }">+</div>
    </template>
  </r-input>
</div>

</template>
<script>

export default {
  name: 'input-number',
  data(){
    return {
      // msg: this.value,
    }
  },
  props: {
    value: {
      type: [Number, String],
      default: 0,
      validator: (value) => {
        return typeof(+value) === 'number'
      }
    },
    step: {
      type: Number,
      default: 1,
    },
    max: {
      type: Number,
    },
    min: {
      type: Number
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    // 精度
    precision: {
      type: Number,
    }
  },
  computed: {
    inputValue: {
      get(){
        return this.value
      },
      set(value){
        const _this = this
        // 也可以用if/else,但是这种写法更清爽
        const rules = [
          {validate(val){ return !_this.checkIsNumber(val) }, res: this.inputValue},
          {validate(val){ return val > _this.max}, res: _this.max},
          {validate(val){ return val < _this.min}, res: _this.min},
          {validate(val){ return true}, res: value},
        ]
        const _value = rules.find(item => item.validate(value)).res
        this.precision ?
          this.$emit('input', Number(_value).toFixed(this.precision))
          : this.$emit('input', _value)
        console.log('_value', _value);
        console.log('inputValue', this.inputValue);
      }
    },
    decreaseDisabled(){ return this.disabled || this.inputValue <= this.min },
    increaseDisabled() { return this.disabled || this.inputValue >= this.max },
  },
  methods: {
    handleClick(type) {
      if(type === 'decrease') {
        if(this.disabled) return
        this.inputValue = Number(this.inputValue) - this.step
      } else {
        if (this.disabled) return
        this.inputValue = Number(this.inputValue) +  this.step
      }
    },
    checkIsNumber(val) {
      // isNaN(NaN) true
      return !isNaN(val) && Object.prototype.toString.call(val) === '[object Number]'
    },
    handleChange(){
      console.log('forceUpdate')
      console.log(this.inputValue);
      // 为什么没有更新数据?为什么要用强制刷新?
      // 这里不知道,为什么只有自己没更新
      // 这里可能是需要改进的
      this.$forceUpdate()
    }
  }
}
</script>
<style lang="scss">
.container {
  width: 300px;
  margin: 0 auto;
}
.decrease,.increase {
  width: 30px;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}
.is_decrease_disbaled {
  cursor: not-allowed;
}
.is_increase_disbaled {
  cursor: not-allowed;
}
</style>
<!-- inputNumber.vue -->
<template>
  <div>
    <input-number :value="initValue" @input="handle" :max="10" :min="1" disabled :precision="2"></input-number>
    <input-number v-model="initValue" :precision="2" :step="0.2"></input-number>
    {{ initValue }}
  </div>
</template>
<script>
export default {
  data (){
    return {
      initValue: 6,
    }
  },
  methods: {
    handle(value) {
      console.log(value);
      this.initValue = value
    },
  }
}
</script>
<!-- /components/input/index.vue -->
<template>
<div class="input-wrapper">
  <template v-if="type === 'text'">
    <div class="inline-block"  :class="styleClass">
      <div class="prepend" v-if="$slots.prepend" :class="equalHeight">
        <slot name="prepend"></slot>
      </div>
      <div class="wrapper">
        <!-- v-on 把所有的事情集中在一起 -->
        <!-- v-bind 一种写法,v-bind="$attrs",把父组件的所有属性继承过来 -->
        <input 
          :value="inputValue" 
          v-on="MyListeners"
          v-bind="$attrs" 
          class="r-text"
          :class="className"
          :disabled="disabled" 
          />
        <span v-if="showClear"><r-icon name="qingkong" @click.native="clearContent"></r-icon></span>
      </div>
      <div class="append" v-if="$slots.append" :class="equalHeight">
        <slot name="append"></slot>
      </div>
    </div>

  </template>
  <template v-else>
    <textarea 
      :value="inputValue" 
      v-on="MyListeners"
      v-bind="$attrs" 
      class="r-textarea"
      :class="className"
      :disabled="disabled"></textarea>
  </template>
</div>
</template>
<script>
export default {
  name: 'r-input',
  props: {
    value: {
      type: [String, Number],
      default: ''
    },
    type: {
      type: String,
      default: 'text',
      validator(type) {
        return ['text', 'textarea'].indexOf(type) > -1
      }
    },
    size: {
      type: String,
      default: '',
      validator (value) {
        return ['', 'small', 'medium'].includes(value)
      }
    },
    clearable: {
      type: Boolean,
      default: false,
    },
    center: {
      type: Boolean,
      default: false
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  computed: {
    inputValue: {
      get: function() {
        return this.value
      },
      set (value) {
        console.log(1111);
        // 这句代码需要的
        console.log('input', value);
        this.$emit('input', value)
      }
    },
    // 重写input事件
    MyListeners() {
      return Object.assign(this.$listeners, {
        input: event => this.$emit("input", event.target.value)
      });
    },
    className () {
      return {
        ['r-input--' + this.size]: true,
        'is-center': this.center
      }
    },
    showClear() {
      return this.clearable && this.inputValue !== ''
    },
    styleClass() {
      return {
        'has-append': this.$slots.append,
        'has-prepend': this.$slots.prepend
      }
    },
    equalHeight() {
      return {
        ['r-input--' + this.size]: true,
      }
    }
  },
  methods: {
    clearContent() {
      this.inputValue = ''
    }
  },
  mounted() {
  }
}
</script>
<style lang="scss" scoped>
@import './style.scss'
</style>

More from this blog

EddieQiao's blog

84 posts

coder, work in suzhou