<script>
import { findSysCode, getDictList } from 'eoss-ui/src/config/api.js';
import { debounce } from 'throttle-debounce';
import util from 'eoss-ui/src/utils/util';
export default {
  name: 'EsSelectGanged',
  componentName: 'EsSelectGanged',
  inheritAttrs: false,
  provide() {
    return {
      ganged: this
    };
  },
  inject: {
    esForm: {
      default: ''
    }
  },
  props: {
    service: String,
    method: {
      type: String,
      default: 'get'
    },
    //接口请求携带的参数
    param: {
      type: [Object, Array],
      default() {
        return {};
      }
    },
    // 显示隐藏组件
    display: {
      type: Boolean,
      default: true
    },
    // 	是否不渲染组件
    hide: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    valueType: {
      type: String,
      default: 'object',
      validator: function (value) {
        return ['string', 'object'].includes(value);
      }
    },
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    // 选择项远程地址
    url: {
      type: String,
      default: ''
    },
    // 开启联动选择
    ganged: {
      type: Number,
      default: 0
    },
    //是否多选
    multiple: [Boolean, Array],
    // 选择项的值字段
    value: {
      type: [String, Array],
      default: ''
    },
    filtrate: [String, Number],
    filtrateKey: {
      type: String,
      default: 'pid'
    },
    // 请求参数
    queryKey: [Object, Array, String],
    //选择项的值字段
    valueKey: {
      type: String,
      default: 'value'
    },
    labelKey: {
      type: String,
      default: 'name'
    },
    sysCode: String,
    integrality: {
      type: Boolean,
      default: false
    },
    separator: {
      type: String,
      default: '/'
    },
    symbol: {
      type: String,
      default: '，'
    },
    readonly: Boolean,
    plain: Boolean,
    parseData: Function,
    group: Boolean,
    hasSub: {
      type: String,
      default: 'hasSub'
    },
    defaultValue: Boolean,
    dataKey: String
  },
  data() {
    return {
      lists: [],
      options: null,
      disabled: [],
      values: [],
      filtrates: [this.filtrate],
      wheres: [{}],
      first: true,
      height: undefined,
      gangedNum: null,
      canceled: false
    };
  },
  computed: {
    getGanged: {
      get() {
        if (this.ganged > 0) {
          return this.ganged;
        } else if (this.results.length > 0 && Array.isArray(this.results[0])) {
          return this.results.length;
        } else {
          return 1;
        }
      },
      set(val) {
        return val;
      }
    },
    multiples: {
      get() {
        if (!this.gangedNum) {
          if (this.ganged && this.value.length <= this.getGanged) {
            this.gangedNum = this.getGanged;
          } else {
            let val = this.value.length
              ? this.value[this.value.length - 1]
              : null;
            if (
              val &&
              Object.prototype.hasOwnProperty.call(val, 'hasSub') &&
              val.hasSub === false
            ) {
              this.gangedNum = this.value.length;
            } else {
              this.gangedNum = this.value.length + 1;
            }
          }
        }
        let multiples = [];
        if (this.multiple === undefined && this.value.length) {
          multiples = this.value.map((item) => {
            return Array.isArray(item);
          });
        } else if (Array.isArray(this.multiple) && this.multiple.length) {
          if (this.multiple.length < this.gangedNum) {
            for (let i = 0; i < this.gangedNum; i++) {
              multiples.push(
                this.multiple[i] == undefined
                  ? multiples[i - 1]
                  : this.multiple[i]
              );
            }
          } else {
            multiples = this.multiple;
          }
        } else {
          for (let i = 0; i < this.gangedNum; i++) {
            multiples.push(this.multiple);
          }
        }
        return multiples;
      },
      set(val) {
        return val;
      }
    },
    results() {
      if (this.integrality) {
        return this.lists;
      }
      return this.data;
    },
    findCode() {
      if (this.service) {
        return `/${this.service}${getDictList}`;
      }
      return findSysCode;
    }
  },
  watch: {
    value: {
      immediate: true,
      deep: true,
      handler(val) {
        if (this.first) {
          this.getValue();
          if (val && (util.isObject(val) || val.length)) {
            this.getChildren(this.results, this.values);
          }
        }
      }
    },
    // 监听级联层数，便于动态修改级联数据
    ganged: {
      deep: true,
      handler(val, oldVal) {
        if (val !== oldVal) {
          this.gangedNum = val;
          // 当级联数增加时，重新执行handeChange，以便最后一层数据从倒数第二层的位置开始找
          if (val > oldVal) {
            let value = this.values[oldVal - 1];
            this.handeChange(value, oldVal - 1);
          } else {
            let value = this.values[val - 1];
            this.handeChange(value, val - 1);
          }
        }
      }
    }
  },
  beforeCreate() {
    this.getData = debounce(500, () => {
      this.getDatas();
    });
  },
  created() {
    this.getData();
  },
  mounted() {},
  methods: {
    getDatas() {
      if (!this.integrality || !this.url) {
        return false;
      }
      util
        .ajax({
          url: this.sysCode ? this.findCode : this.url,
          method: this.method,
          params: this.param,
          data: this.param
        })
        .then((res) => {
          this.canceled = false;
          if (res.rCode === 0) {
            this.lists = res.results;
            this.getChildren(this.results, this.values);
            if (sysCode || this.dataKey) {
              this.esForm &&
                this.esForm.setSysCodes(
                  sysCode || this.dataKey,
                  JSON.parse(JSON.stringify(this.lists))
                );
              store.set(sysCode, JSON.parse(JSON.stringify(this.lists)));
            }
          }
        })
        .catch((err) => {
          if (err.message) {
            if (err.message !== 'canceled') {
              this.$message.error(err.message);
            }
            if (err.message === 'canceled' && sysCode) {
              this.canceled = true;
            }
          }
        });
    },
    getChildren(data, values) {
      if (!this.gangedNum) {
        if (
          (this.ganged && this.value.length < this.getGanged) ||
          Array.isArray(this.results[0])
        ) {
          this.gangedNum = this.getGanged;
        } else {
          let val = this.value.length
            ? this.value[this.value.length - 1]
            : null;
          if (
            val &&
            Object.prototype.hasOwnProperty.call(val, 'hasSub') &&
            val.hasSub === false
          ) {
            this.gangedNum = this.value.length;
          } else {
            this.gangedNum = this.value.length + 1;
          }
        }
      }
      for (let i = 0; i < values.length; i++) {
        if (i > 0) {
          if (
            (values[i] || values[i] == 0) &&
            (util.isObject(values[i]) || values[i].length)
          ) {
            this.$set(this.disabled, i, false);
          }
        }
        if (i < values.length - 1) {
          if (
            values[i] &&
            Object.prototype.hasOwnProperty.call(values[i], 'multiple')
          ) {
            this.$set(this.multiples, i + 1, Boolean(this.values[i].multiple));
          }
          if (i + 1 <= values.length) {
            this.$set(
              this.filtrates,
              i + 1,
              this.multiples[i + 1]
                ? Array.isArray(values[i])
                  ? values[i]
                  : [values[i]]
                : values[i]
            );
          }

          if (this.url && i + 1 <= values.length) {
            let params = {};
            if (this.queryKey !== undefined) {
              if (typeof this.queryKey === 'string') {
                if (Array.isArray(values[i])) {
                  params[this.queryKey] = values[i].map((item) => {
                    return item[this.queryKey];
                  });
                } else {
                  params[this.queryKey] = values[i][this.queryKey];
                }
              } else {
                for (let x in this.queryKey) {
                  if (Array.isArray(values[i])) {
                    params[x] = values[i].map((item) => {
                      return item[this.queryKey[x]];
                    });
                  } else {
                    params[x] = values[i][this.queryKey[x]];
                  }
                }
              }
            }
            this.$set(this.wheres, i + 1, params);
          }
        }
      }
      if (values.length && values.length < this.gangedNum) {
        this.$set(this.disabled, values.length, false);
      }
    },
    getValue() {
      if (!this.gangedNum) {
        if (
          (this.ganged && this.value.length <= this.getGanged) ||
          Array.isArray(this.results[0])
        ) {
          this.gangedNum = this.getGanged;
        } else {
          let val = this.value.length
            ? this.value[this.value.length - 1]
            : null;
          if (
            val &&
            Object.prototype.hasOwnProperty.call(val, 'hasSub') &&
            val.hasSub === false
          ) {
            this.gangedNum = this.value.length;
          } else {
            this.gangedNum = this.value.length + 1;
          }
        }
      }
      if (
        !this.ganged ||
        (this.value.length < this.gangedNum - 1 && this.gangedNum > 0)
      ) {
        let val = this.value.length
          ? JSON.parse(JSON.stringify(this.value))
          : [];
        let disabled = [];
        for (let i = 0; i < this.gangedNum; i++) {
          if (i > this.value.length - 1) {
            val.push(this.multiples[i] ? [] : '');
          }
          disabled.push(i > this.value.length);
        }
        this.disabled = disabled;
        this.values = val;
      } else {
        let disabled = [];
        if (this.multiple) {
          this.values = this.value.map((item, index) => {
            return this.multiples[index]
              ? Array.isArray(item)
                ? item
                : [item]
              : item;
          });
          for (let i = 0; i < this.gangedNum; i++) {
            disabled.push(
              this.values[i] && this.values[i].length ? false : i > 0
            );
          }
        } else {
          for (let i = 0; i < this.gangedNum; i++) {
            if (Array.isArray(this.value[i])) {
              this.$set(this.multiples, i, true);
              disabled.push(this.value[i].length ? false : i > 0);
            } else {
              this.$set(
                this.multiples,
                i,
                util.isObject(this.value[i]) ? this.value[i].multiple : false
              );
              disabled.push(this.value[i] ? false : i > 0);
            }
          }
          this.values = JSON.parse(JSON.stringify(this.value));
        }
        this.disabled = disabled;
      }
    },
    handeChange(val, key) {
      if (!this.gangedNum) {
        if (this.ganged && this.value.length < this.getGanged) {
          this.gangedNum = this.getGanged;
        } else {
          let val = this.value.length
            ? this.value[this.value.length - 1]
            : null;
          if (
            val &&
            Object.prototype.hasOwnProperty.call(val, 'hasSub') &&
            val.hasSub === false
          ) {
            this.gangedNum = this.value.length;
          } else {
            this.gangedNum = this.value.length + 1;
          }
        }
      }
      if (key === this.gangedNum - 1 && val[this.hasSub]) {
        this.gangedNum += 1;
      } else if (val[this.hasSub] === false) {
        this.gangedNum = key + 1;
      }
      this.first = false;
      this.parentData = null;
      if (this.url && key + 1 < this.gangedNum) {
        this.parentData = val;
        let params = {};
        if (this.queryKey !== undefined) {
          if (typeof this.queryKey === 'string') {
            params[this.queryKey] = Array.isArray(val)
              ? val.map((item) => {
                  return item[this.queryKey];
                })
              : val[this.queryKey];
          } else {
            for (let i in this.queryKey) {
              params[i] = Array.isArray(val)
                ? val.map((item) => {
                    return item[this.queryKey[i]];
                  })
                : val[this.queryKey[i]];
            }
          }
        } else {
          params.id = Array.isArray(val)
            ? val.map((item) => {
                return item[this.valueKey];
              })
            : val[this.valueKey];
        }
        this.$set(this.wheres, key + 1, params);
      }
      this.$set(this.values, key, val);
      if (key + 1 < this.gangedNum) {
      }
      for (let i = key + 1; i < this.gangedNum; i++) {
        if (this.multiples[i - 1]) {
          let newVal = [];
          this.values[i - 1].forEach((element) => {
            this.values[i].forEach((itemVal) => {
              if (element[this.valueKey] === itemVal[this.filtrateKey]) {
                newVal.push(itemVal);
              }
            });
          });
          this.$set(this.values, i, newVal);
        }
        if (
          this.values[i - 1] &&
          (util.isObject(this.values[i - 1]) || this.values[i - 1].length)
        ) {
          this.$set(this.disabled, i, false);
        } else {
          this.$set(this.disabled, i, true);
        }
        this.$set(this.filtrates, i, this.values[i - 1]);
        if (!this.multiple) {
          if (
            util.isObject(val) &&
            Object.prototype.hasOwnProperty.call(val, 'multiple')
          ) {
            this.$set(this.multiples, i, Boolean(val.multiple));
          }
        }
        if (!this.multiples[i - 1] || val.length == 0) {
          this.$set(this.values, i, this.multiples[i] ? [] : '');
        }
      }
      if (key + 1 === this.gangedNum) {
        this.$emit('input', this.values);
      }
      this.$emit('change', { value: val, index: key, data: this.values });
    },
    getLabel(res, index) {
      let results = this.results;
      if (Array.isArray(this.results[0])) {
        results = this.results[index];
      }
      for (let i = 0; i < results.length; i++) {
        let item = results[i];
        if (item[this.valueKey] == res) {
          return item;
        }
        if (
          Object.prototype.hasOwnProperty.call(item, 'children') &&
          item.children &&
          item.children.length
        ) {
          return this.getLabel(item.children, res);
        }
      }
    }
  },
  render(h) {
    if (!this.hide) {
      if (this.readonly) {
        let value = [];
        if (this.value) {
          if (this.group) {
            let path = {};
            let values = [];
            this.value.forEach((item, index) => {
              if (Array.isArray(item)) {
                item.forEach((ele) => {
                  let obj = ele;
                  if (!ele[this.filtrateKey]) {
                    obj = this.getLabel(ele[this.valueKey] || ele, index);
                  }
                  if (path[obj[this.filtrateKey]]) {
                    path[obj[this.valueKey]] =
                      path[obj[this.filtrateKey]] +
                      this.separator +
                      (obj[this.labelKey] || obj.label);
                    values.push(path[obj[this.valueKey]]);
                  } else {
                    path[obj[this.valueKey]] = obj[this.labelKey] || obj.label;
                  }
                });
              } else {
                let obj = item;
                if (!item[this.filtrateKey]) {
                  obj = this.getLabel(item[this.valueKey] || item, index);
                }
                if (path[obj[this.filtrateKey]]) {
                  path[obj[this.valueKey]] =
                    path[obj[this.filtrateKey]] +
                    this.separator +
                    (obj[this.labelKey] || obj.label);
                  values.push(path[obj[this.valueKey]]);
                } else {
                  path[obj[this.valueKey]] = obj[this.labelKey] || obj.label;
                }
              }
            });
            values.forEach((item, index) => {
              let flag = true;
              for (let i = index + 1; i < values.length; i++) {
                if (values[i].indexOf(item) > -1) {
                  flag = false;
                  break;
                }
              }
              if (flag) {
                value.push(item);
              }
            });
          } else {
            value = [
              this.value
                .map((item, indexs) => {
                  if (Array.isArray(item)) {
                    return item
                      .map((ele, index) => {
                        return util.isObject(ele)
                          ? ele[this.labelKey] ||
                              ele.label ||
                              this.getLabel(
                                ele[this.valueKey] || ele.label,
                                index
                              )
                          : this.getLabel(ele, index) || ele;
                      })
                      .join(this.symbol);
                  }
                  return util.isObject(item)
                    ? item[this.labelKey] ||
                        item.label ||
                        this.getLabel(item[this.valueKey] || item.label, indexs)
                    : this.getLabel(item, indexs) || item;
                })
                .join(this.separator)
            ];
          }
        }
        return h('es-select', {
          attrs: {
            ...this.$attrs,
            separator: this.separator,
            readonly: true,
            multiple: value.length > 1,
            value: value
          }
        });
      }
      if (!this.gangedNum) {
        if (this.ganged && this.value.length < this.getGanged) {
          this.gangedNum = this.getGanged;
        } else {
          let val = this.value.length
            ? this.value[this.value.length - 1]
            : null;
          if (
            val &&
            Object.prototype.hasOwnProperty.call(val, 'hasSub') &&
            val.hasSub === false
          ) {
            this.gangedNum = this.value.length;
          } else {
            this.gangedNum = this.value.length + 1;
          }
        }
      }
      let doms = [];
      for (let i = 0; i < this.gangedNum; i++) {
        doms.push(
          h('es-select', {
            attrs: {
              ...this.$attrs,
              data: this.results[i]
                ? Array.isArray(this.results[0])
                  ? this.results[i]
                  : this.results
                : [],
              sysCode: this.integrality ? undefined : this.sysCode,
              url: this.integrality ? undefined : this.url,
              wait: this.ganged ? i <= this.value.length : true,
              param: Array.isArray(this.param)
                ? { ...this.param[i], ...this.wheres[i] }
                : { ...this.param, ...this.wheres[i] },
              disabled: this.disabled[i],
              multiple: this.multiples[i],
              height: this.height,
              value: this.values[i],
              valueType: this.valueType,
              valueKey: this.valueKey,
              labelKey: this.labelKey,
              filtrateKey: this.filtrateKey,
              filtrate: this.url
                ? this.integrality
                  ? this.filtrates[i]
                  : undefined
                : this.filtrates[i],
              placeholder: this.placeholder,
              parseData: this.parseData,
              defaultValue: i === 0 ? this.defaultValue : false
            },
            on: {
              change: (val) => {
                this.handeChange(val, i);
              },
              resetInputHeight: (val) => {
                this.height = val;
              }
            }
          })
        );
      }
      return h(
        'div',
        {
          class: 'es-select-ganged',
          directives: [
            {
              name: 'show',
              value: this.display
            }
          ]
        },
        doms
      );
    } else {
      return '';
    }
  }
};
</script>
