<template>
  <div
    :class="[
      'en-input-number',
      inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
      { 'is-disabled': inputNumberDisabled },
      { 'is-without-controls': !controls },
      { 'is-controls-right': controlsAtRight }
    ]"
    @dragstart.prevent
  >
    <!-- <span
      v-if="controls"
      v-repeat-click="decrease"
      class="el-input-number__decrease"
      role="button"
      :class="{ 'is-disabled': minDisabled }"
      @keydown.enter="decrease"
    >
      <i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
    </span>
    <span
      v-if="controls"
      v-repeat-click="increase"
      class="el-input-number__increase"
      role="button"
      :class="{ 'is-disabled': maxDisabled }"
      @keydown.enter="increase"
    >
      <i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
    </span> -->
    <el-input
      ref="input"
      :value="displayValue"
      :placeholder="placeholder"
      :disabled="inputNumberDisabled"
      :size="inputNumberSize"
      :max="max"
      :min="min"
      :name="name"
      :label="label"
      v-bind="$attrs"
      @keydown.up.native.prevent="increase"
      @keydown.down.native.prevent="decrease"
      @blur="handleBlur"
      @focus="handleFocus"
      @input="handleInput"
      @change="handleInputChange"
    >
      <slot slot="right" name="button"></slot>
      <slot slot="prepend" name="prepend"> </slot>
      <slot slot="append" name="append"> </slot>
      <slot slot="suffix" name="suffix"> </slot>
      <slot slot="prefix" name="prefix"> </slot>
    </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: "EnInputNumber",
  inject: {
    elForm: { default: "" },
    elFormItem: { default: "" }
  },
  directives: { repeatClick: RepeatClick },
  components: { ElInput },
  mixins: [Focus("input")],
  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: false
    },
    controlsPosition: {
      type: String,
      default: ""
    },
    name: String,
    label: String,
    placeholder: String,
    precision: {
      type: Number,
      validator(val) {
        return val >= 0 && val === parseInt(val, 10);
      }
    },
    // 支持千分位展示
    thousandFormat: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      currentValue: "",
      userInput: null
    };
  },
  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;
        return this.commafy(this.userInput);
      }
      const currentValue = this.currentValue;
      if (typeof currentValue === "number" && this.precision !== undefined) {
        // return currentValue.toFixed(this.precision);
        return this.commafy(currentValue.toFixed(this.precision));
      } else {
        return this.commafy(currentValue);
      }
    }
  },
  watch: {
    value: {
      immediate: true,
      deep: true,
      handler(value) {
        let newVal = value === undefined ? value : Number(value);
        // 空值不转了
        if (value === "") {
          this.currentValue = "";
          this.$emit("input", value);
          return;
        }
        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.userInput = null;
        this.$emit("input", newVal);
      }
    }
  },
  mounted() {
    const 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);
  },
  methods: {
    toPrecision(num, precision) {
      if (precision === undefined) {
        precision = this.numPrecision;
      }
      return parseFloat(Number(num).toFixed(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.currentValue = newVal;
      this.$emit("input", newVal);
      this.$emit("change", newVal, oldVal);
    },
    handleInput(value) {
      this.userInput = this.delcommafy(value);
      // this.userInput = value;
    },
    handleInputChange(value) {
      const newVal = value === "" ? undefined : Number(this.delcommafy(value));
      if (!isNaN(newVal) || value === "") {
        this.setCurrentValue(newVal);
      }
      this.userInput = null;
    },
    select() {
      this.$refs.input.select();
    },
    /**
     * 数字格式转换成千分位
     *@param{Object}num
     */
    commafy(num) {
      if (!this.thousandFormat) {
        return num;
      }
      if ((num + "").trim() === "") {
        return "";
      }
      if (isNaN(num)) {
        return "";
      }
      num = num + "";
      if (/^.*\..*$/.test(num)) {
        const pointIndex = num.lastIndexOf(".");
        let intPart = num.substring(0, pointIndex);
        const pointPart = num.substring(pointIndex + 1, num.length);
        intPart = intPart + "";
        const re = /(-?\d+)(\d{3})/;
        while (re.test(intPart)) {
          intPart = intPart.replace(re, "$1,$2");
        }
        num = intPart + "." + pointPart;
      } else {
        num = num + "";
        const re = /(-?\d+)(\d{3})/;
        while (re.test(num)) {
          num = num.replace(re, "$1,$2");
        }
      }
      return num;
    },
    /**
     * 去除千分位
     *@param{Object}num
     */
    delcommafy(num) {
      if (!this.thousandFormat) {
        return num;
      }
      if ((num + "").trim() === "") {
        return "";
      }
      num = num.replace(/,/gi, "");
      return num;
    }
  }
};
</script>

<style lang="scss" scoped>
.en-input-number {
  display: inline-table;
  .el-input /deep/ .el-input-inner {
    text-align: left;
  }
}
</style>
