<template>
  <div class="inputs ht-component">
    <el-select
      style="width: 100%;"
      @click.native="clickNative"
      v-show="inputWriteable"
      v-validate="inputValidate"
      :name="inputName"
      v-model="inputVal"
      :loading="loading"
      :size="size"
      :clearable="clearable"
      :disabled="readonly || disabled"
      :placeholder="disabled?'':(placeholder?placeholder:'请选择')"
      :filterable="filterable"
      :allow-create="allowCreate"
      :multiple="multiple"
      :remote="remote"
      :remote-method="handleRemoteMethod"
      :collapse-tags="collapseTags"
      v-if="inputWriteable"
      @change="change"
      @visible-change="visibleChange"
      @remove-tag="removeTag"
      @clear="handleClear"
      @blur="handleBlur"
      @focus="handleFocus"
    >
      <slot
        v-if="
          optionsAry && optionsAry.constructor == Array && optionsAry.length > 0
        "
        :options="optionsAry"
        :prop-key="propKey"
        :prop-value="propValue"
      >
        <el-option
          v-for="item in optionsAry"
          :key="item[propKey]"
          :label="item[propValue]"
          :value="item[propKey]"
          :disabled="!!item.disabled"
        ></el-option>
      </slot>
    </el-select>
    <ht-field-tail
      v-if="permission != 'n'"
      :writeable.sync="writeable"
      :fieldName="inputName"
      :readonly="!inputWriteable"
      :inputValue="inputVal"
      :tag-format-value="tagFormatValue"
    />
  </div>
</template>
<script>
import utils from '@/utils.js'
import service from '@/service.js'
import HtFieldTail from '@/components/HtFieldTail.vue'
import { setTimeout } from 'timers'
import deepmerge from 'deepmerge'

export default {
  name: 'ht-select',
  props: {
    validate: [String, Object],
    value: [String, Number, Boolean, Array],
    name: String,
    permission: {
      type: String,
      default: 'w',
      validator: function(value) {
        return ['b', 'w', 'r', 'n'].indexOf(value) !== -1
      }
    },
    size: {
      type: String,
      default: 'small'
    },
    options: Array,
    props: {
      type: Object,
      default: function() {
        return {
          key: 'key',
          value: 'value'
        }
      }
    },
    disabled: {
      type: Boolean,
      default: false
    },
    readonly: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    clearable: {
      type: Boolean,
      default: false
    },
    filterable: {
      type: Boolean,
      default: false
    },
    allowCreate: {
      type: Boolean,
      default: false
    },
    linkage: [Object, Array],
    placeholder: String,
    remoteMethod: Function,
    remote: {
      type: Boolean,
      default: false
    },
    collapseTags: {
      type: Boolean,
      default: false
    }
  },
  components: {
    HtFieldTail
  },
  data() {
    return {
      optionsAry: [],
      tagFormatValue: [],
      remoteMethodFn: null,
      inputName: null,
      writeable: true,
      isRemote: false,
      loading: false,
      oldValue: this.value,
      remoteLoaded: false
    }
  },
  watch: {
    options: {
      handler: function(newVal, oldValue) {
        if (newVal && newVal.constructor === Array) {
          // 在加载了数据时，标记远程方法调用状态为已调用
          this.remoteLoaded = true
          this.optionsAry = [...newVal]
          // 异步加载数据 下拉框不会回显 需要加载完options 重新赋值
          if (
            (!oldValue || oldValue.length == 0) &&
            !utils.isEmpty(this.oldValue)
          ) {
            this.$emit('input', this.oldValue)
          }
        }
      },
      immediate: true
    }
  },
  computed: {
    inputVal: {
      get() {
        if (
          utils.isEmpty(this.value) ||
          !this.optionsAry ||
          this.optionsAry.length == 0
        ) {
          return utils.isEmpty(this.value) ? '' : this.value
        }
        if (this.value.constructor == String) {
          const ary = this.value === '' ? [] : this.value.split(',')
          if (this.inputWriteable) {
            if (this.multiple) {
              return ary
            } else {
              return this.value
            }
          } else {
            // 只读 多选
            if (this.multiple) {
              const ary = this.value === '' ? [] : this.value.split(',')
              let optionVals = []
              let tempAry = deepmerge([], ary, { clone: true })
              ary.forEach(m => {
                this.optionsAry.find(opt => {
                  if (opt[this.propKey] === m) {
                    optionVals.push(opt[this.propValue])
                    tempAry.remove(m)
                  }
                  if (opt.children && opt.children.length > 0) {
                    opt.children.find(childOpt => {
                      if (childOpt[this.propKey] === m) {
                        optionVals.push(childOpt[this.propValue])
                        tempAry.remove(m)
                      }
                    })
                  }
                })
              })
              optionVals = optionVals.concat(tempAry)
              this.tagFormatValue = optionVals
              return ary
            }
            if (this.optionsAry && this.optionsAry.length > 0) {
              let optionVals = []
              let tempAry = deepmerge([], ary, { clone: true })
              ary.forEach(m => {
                this.optionsAry.find(opt => {
                  if (opt[this.propKey] === m) {
                    optionVals.push(opt[this.propValue])
                    tempAry.remove(m)
                  }
                  if (opt.children && opt.children.length > 0) {
                    opt.children.find(childOpt => {
                      if (childOpt[this.propKey] === m) {
                        optionVals.push(childOpt[this.propValue])
                        tempAry.remove(m)
                      }
                    })
                  }
                })
              })
              optionVals = optionVals.concat(tempAry)
              this.tagFormatValue = optionVals
              return this.value
            }
          }
        }
        return this.value
      },
      set(val) {
        let result = ''
        if (this.multiple) {
          if (val && val.constructor === Array && val.length > 0) {
            if (val[0].constructor === Number) {
              throw '多选不支持数字类型'
            }
            result = val.join(',')
          }
        } else {
          result = val
        }
        this.$emit('input', result)
      }
    },
    inputWriteable: function() {
      return this.writeable
        ? utils.getWriteable(this.permission)
        : this.writeable
    },
    inputValidate: function() {
      return utils.addRequiredOrNot(this.permission, this.validate, this)
    },
    propKey: function() {
      if (this.props && this.props.key) {
        return this.props.key
      }
      return 'key'
    },
    propValue: function() {
      if (this.props && this.props.value) {
        return this.props.value
      }
      return 'value'
    }
  },
  mounted() {
    // 字段联动规则
    this.mountedLinkage()
    if (this.value) {
      this.handleRemoteMethod()
    }
  },
  created() {
    this.inputName = this.name ? this.name : utils.getName()
    this.remoteMethodFn = this.remoteMethod
    this.isRemote =
      (this.remoteMethodFn && this.remoteMethodFn.constructor === Function) ||
      !!(
        this.$vnode.data.directives &&
        this.$vnode.data.directives.find(m => m.name == 'query')
      )
    this.$validator = this.$root.$validator
  },
  methods: {
    mountedLinkage() {
      if (this.linkage) {
        const exp = this.linkage
        // 如果要联动的字段权限是只读“r”,则把联动配置里面要联动的字段权限“w”改为只读“r”
        if (exp && exp.length > 0) {
          const frmInst = utils.getOnlineFormInstance(this)
          // 表单所有字段的初始权限
          if (frmInst && frmInst.permission) {
            exp.forEach(item => {
              let effect = item.effect
              effect.forEach(effectItem => {
                let initPermission = utils.getValueByPath(
                  frmInst,
                  effectItem.target
                )
                if (
                  effectItem.target &&
                  effectItem.target.indexOf('permission') != -1 &&
                  effectItem.value != 'n' &&
                  initPermission == 'r'
                ) {
                  effectItem.value = initPermission
                }
              })
            })
          }
        }
        setTimeout(() => {
          service.linkageHandler(this, exp)
        }, 0)
      }
    },
    clickNative: function() {
      // 在远程方法未调用时才触发远程调用
      if (!this.remoteLoaded) {
        this.handleRemoteMethod()
      }
    },
    handleRemoteMethod: function(query) {
      if (this.isRemote) {
        this.loading = true
        var result = this.remoteMethodFn(query)
        if (result && result.finally) {
          result.finally(() => {
            this.loading = false
          })
        } else {
          setTimeout(() => {
            this.loading = false
          }, 1000)
        }
      }
    },
    change: function(data) {
      let selectObj = this.optionsAry.find(opt => opt[this.propKey] === data)
      this.$emit('change', data, selectObj)
    },
    clearValue: function() {
      this.oldValue = ''
      this.inputVal = ''
    },
    visibleChange: function(m) {
      this.$emit('visible-change', m)
    },
    removeTag: function(tag) {
      this.$emit('remove-tag', tag)
    },
    handleClear: function() {
      this.$emit('clear')
    },
    handleBlur: function(event) {
      this.$emit('blur', event)
    },
    handleFocus: function(event) {
      this.$emit('focus', event)
    }
  }
}
</script>
<style lang="scss" scoped>
div[aria-invalid="true"] >>> .el-input__inner,
div[aria-invalid="true"] >>> .el-input__inner:focus {
  border-color: #f56c6c;
}
</style>
