<template>
  <div class="inputs ht-component" v-express>
    <el-input
      ref="elInput"
      :size="size"
      :style="inputWidth"
      v-if="inputWriteable && type != 'number'"
      v-validate="inputValidate"
      :name="inputName"
      :placeholder="disabled?'':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="disabled?'':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: false }
        )
        endScope.$watch(
          'inputVal',
          function(newVal, oldVal) {
            if (newVal !== oldVal) {
              handleDateCalc(startScope.value, endScope.value)
            }
          },
          { immediate: false }
        )
      } 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>
