<template>
  <div class="inputs" v-express>
    <el-input
      ref="elInput"
      :size="size"
      :style="inputWidth"
      v-if="inputWriteable && type!='number'"
      v-validate="inputValidate"
      :name="inputName"
      :placeholder="placeholder"
      :clearable="clearable"
      :show-password="showPassword"
      :disabled="disabled"
      :readonly="readonly"
      v-model="inputVal"
      :model-expression="modelExpression"
      :type="type"
      :rows="rowsVal"
      :cols="colsVal"
      :min="min"
      :max="max"
      :autosize="autosize"
      :tabindex="tabindex"
      :prefix-icon="prefixIcon"
      :suffix-icon="suffixIcon"
      :minlength="minlength"
      :maxlength="maxlength"
      :show-word-limit="showWordLimit"
      @blur="handleBlur"
      @focus="handleFocus"
      @change="handleChange"
      @input="handleInput"
      @clear="handleClear"
    >
      <slot name="prefix" slot="prefix"></slot>
      <slot name="suffix" slot="suffix"></slot>
      <slot name="prepend" slot="prepend"></slot>
      <slot name="append" slot="append"></slot>
    </el-input>
    <el-input-number
      :controls-position="controlsPosition"
      :size="size"
      :style="inputWidth"
      v-if="inputWriteable && type=='number'"
      v-validate="inputValidate"
      :name="inputName"
      :placeholder="placeholder"
      :clearable="clearable"
      :disabled="disabled"
      :readonly="readonly"
      v-model="inputVal"
      :min="min"
      :max="max"
      :step="step"
      :step-strictly="stepStrictly"
      :precision="precision"
      :type="type"
      @blur="handleBlur"
      @focus="handleFocus"
      @change="handleChange"
    ></el-input-number>
    <ht-field-tail :writeable.sync="writeable" :fieldName="inputName" :readonly="!inputWriteable" :inputValue="inputVal">
      <slot name="append" slot="append"></slot>
    </ht-field-tail>
  </div>
</template>
<script>
import utils from "@/utils.js";
import FormMath from "@/math.js";
import HtFieldTail from "@/components/HtFieldTail.vue";

export default {
  name: "ht-input",
  props: {
    validate: [String, Object],
    value: [String, Number],
    format: [String, Number],
    name: String,
    size: {
      type: String,
      default: "small",
    },
    controlsPosition: {
      type: String,
      default: "right",
    },
    width: [String, Number],
    permission: {
      type: String,
      default: "w",
      validator: function (value) {
        return ["b", "w", "r", "n"].indexOf(value) !== -1;
      },
    },
    clearable: {
      type: Boolean,
      default: true,
    },
    showPassword: {
      type: Boolean,
      default: false,
    },
    mathExp: String,
    type: {
      type: String,
      default: "text",
    },
    dateCalcExp: Object,
    placeholder: {
      type: String,
      default: "请输入内容",
    },
    readonly: {
      type: Boolean,
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    min: Number,
    max: Number,
    step: {
      type: Number,
      default: 1,
    },
    stepStrictly: {
      type: Boolean,
      default: false,
    },
    precision: Number,
    cols: {
      type: Number,
      default: 60,
    },
    rows: {
      type: Number,
      default: 2,
    },
    autosize: {
      type: [Boolean, Object],
      default: false,
    },
    prefixIcon: String,
    suffixIcon: String,
    tabindex: String,
    modelExpression: {
      type: String,
      default: "",
    },
    minlength: {
      type: Number,
    },
    maxlength: {
      type: Number,
    },
    showWordLimit: {
      type: Boolean,
      default: false,
    },
    readonlyFormat: {
      type: Function,
    },
  },
  components: {
    HtFieldTail,
  },
  data() {
    return {
      unwatchAry: [],
      inputName: null,
      writeable: true,
      newValidate: null,
    };
  },
  computed: {
    inputVal: {
      get() {
        if (!utils.isEmpty(this.format) && !this.inputWriteable) {
          return this.format;
        }
        if (this.readonlyFormat && !this.inputWriteable) {
          let resultVal = this.readonlyFormat(this.value);
          if (!utils.isEmpty(resultVal)) {
            return resultVal;
          }
        }
        if (utils.isEmpty(this.value)) {
          return "";
        }
        return this.value;
      },
      set(val) {
        this.$emit("input", val);
      },
    },
    colsVal: function () {
      return this.type == "textarea" ? this.cols : null;
    },
    rowsVal: function () {
      return this.type == "textarea" ? this.rows : null;
    },
    inputWriteable: function () {
      return this.writeable ? utils.getWriteable(this.permission):this.writeable;
    },
    inputValidate: function () {
      if (this.newValidate) {
        return this.newValidate;
      }
      return utils.addRequiredOrNot(this.permission, this.validate, this);
    },
    inputWidth: function () {
      return this.width
        ? typeof this.width == "string"
          ? { width: this.width }
          : { width: `${this.width}px` }
        : null;
    },
  },
  mounted() {
    const _me = this;
    const exp = this.mathExp;
    const formVm = utils.getOnlineFormInstance(_me);

    // 数学运算表达式
    if (exp) {
      let elAttr = this.$vnode.data.model.expression;
      if (this.modelExpression) {
        elAttr = this.modelExpression;
      }
      const fields = FormMath.parseFuncexpField(exp);
      formVm.$on(elAttr, function (args) {
        if (args.hasOwnProperty("index")) {
          const { subScopeEl, index } = utils.getSubScopeElAndIndex(_me.$el);
          if (subScopeEl != null && index === null) {
            throw "要计算子表行数据的输入框不在包含data-subname属性的元素中.";
          }
          if (index == args.index) {
            _me.$emit("input", args.result);
          }
        } else {
          _me.$emit("input", args.result);
        }
      });
      fields.forEach((ele) => {
        if (formVm.watchMap.has(ele)) {
          let ary = formVm.watchMap.get(ele);
          ary.push({ target: elAttr, exp });
          formVm.watchMap.set(ele, ary.unique("target"));
        } else {
          formVm.watchMap.set(ele, [{ target: elAttr, exp }]);
        }
      });
    }
    // 日期计算表达式
    if (this.dateCalcExp) {
      // 计算日期间隔
      let handleDateCalc = function (startVal, endVal) {
        _me.$emit(
          "input",
          utils.dateCalc(startVal, endVal, _me.dateCalcExp.diffType)
        );
      };
      // 获取是否在子表中每一行进行日期计算
      const { subScopeEl, index } = utils.getSubScopeElAndIndex(_me.$el);
      if (subScopeEl) {
        const startScope = utils.getSubInputScopeByModelExpression(
          subScopeEl,
          this.dateCalcExp.start
        );
        const endScope = utils.getSubInputScopeByModelExpression(
          subScopeEl,
          this.dateCalcExp.end
        );
        // 对于子表中某行的日期计算，需要找到日期控件对应的vm实例
        startScope.$watch(
          "inputVal",
          function (newVal, oldVal) {
            if (newVal !== oldVal) {
              handleDateCalc(startScope.value, endScope.value);
            }
          },
          { immediate: true }
        );
        endScope.$watch(
          "inputVal",
          function (newVal, oldVal) {
            if (newVal !== oldVal) {
              handleDateCalc(startScope.value, endScope.value);
            }
          },
          { immediate: true }
        );
      } else {
        let cb = function (newVal, oldVal) {
          if (newVal !== oldVal) {
            let startVal = utils.getValueByPath(
              formVm,
              _me.dateCalcExp.start,
              index
            );
            let endVal = utils.getValueByPath(
              formVm,
              _me.dateCalcExp.end,
              index
            );
            handleDateCalc(startVal, endVal);
          }
        };
        // 主表中的日期计算，在表单上通过v-model的表达式来监控值变更
        formVm.$watch(this.dateCalcExp.start, cb, { immediate: true });
        formVm.$watch(this.dateCalcExp.end, cb, { immediate: true });
      }
    }
  },
  beforeDestroy() {
    let that = this;
    let watcher = that._watchers.find((m) => m.expression == "inputVal");
    if (watcher && watcher.cb) {
      // 单行文本销毁时，触发一次change事件，更新计算公式
      watcher.cb("0", that.inputVal, true);
    }
  },
  destroyed() {
    this.unwatchAry &&
      this.unwatchAry.forEach((unwatch) => {
        unwatch.call();
      });
  },
  created() {
    this.inputName = this.name ? this.name : utils.getName();
    this.$validator = this.$root.$validator;
  },
  methods: {
    handleBlur(event) {
      this.$emit("blur", event);
    },
    handleFocus(event) {
      this.$emit("focus", event);
    },
    handleChange(val) {
      this.$emit("change", val);
    },
    handleInput(val) {
      this.$emit("input", val);
    },
    handleClear() {
      this.$emit("clear");
    },
  },
};
</script>
<style lang="scss" scoped>
div[aria-invalid="true"] >>> .el-input__inner,
div[aria-invalid="true"] >>> .el-input__inner:focus {
  border-color: #f56c6c;
}

div[aria-invalid="true"] >>> .el-textarea__inner,
div[aria-invalid="true"] >>> .el-textarea__inner:focus {
  border-color: #f56c6c;
}
</style>