<script>
import { findSysCode, getDictList } from 'eoss-ui/src/config/api.js';
import { debounce } from 'throttle-debounce';
import store from 'eoss-ui/src/utils/store';
import util from 'eoss-ui/src/utils/util';
export default {
  name: 'EsSelect',
  componentName: 'EsSelect',
  inheritAttrs: false,
  provide() {
    return {
      esSelect: this
    };
  },
  inject: {
    esForm: {
      default: ''
    }
  },
  props: {
    service: String,
    value: [String, Array, Object, Number],
    method: {
      type: String,
      default: 'get'
    },
    param: {
      type: Object,
      default() {
        return {};
      }
    },
    // 显示隐藏组件
    display: {
      type: Boolean,
      default: true
    },
    // 	是否不渲染组件
    hide: {
      type: Boolean,
      default: false
    },
    valueType: {
      type: String,
      default: 'string',
      validator: function (value) {
        return ['string', 'object'].includes(value);
      }
    },
    // 选择项的值字段
    valueKey: {
      type: String,
      default: 'value'
    },
    // 指定选择项显示值的键名，data元素是对象类型时必填
    labelKey: {
      type: String,
      default: 'name'
    },
    data: {
      type: Array,
      default() {
        return [];
      }
    },
    // 远程搜索
    remote: Boolean,
    // 远程搜索的参数名
    remoteKey: {
      type: String,
      default: 'searchKey'
    },
    // 远程搜索的方法
    remoteMethod: Function,
    // 选择项远程地址
    url: String,
    // 选择项获取代码表数据(优先级大于url)
    sysCode: String,
    filtrate: [String, Object, Array],
    filtrateKey: {
      type: String,
      default: 'pid'
    },
    readonly: Boolean,
    separator: {
      type: String,
      default: '-'
    },
    symbol: {
      type: String,
      default: ','
    },
    format: { type: Boolean, default: true },
    plain: Boolean,
    where: Object,
    parseData: Function,
    disabled: Boolean,
    // 是否下拉树选择
    tree: {
      type: [Boolean, Object],
      default: false
    },
    // 父级是否可选
    parentCheck: {
      type: Boolean,
      default: true
    },
    // 父级可选时父级是否作为单独的值
    parentAsValue: {
      type: Boolean,
      default: true
    },
    group: Boolean,
    multiple: Boolean,
    defaultValue: Boolean,
    onExpand: Function,
    onLoadsub: Function,
    subParamKey: Object,
    lazy: Boolean,
    //是否请求代码表
    ajax: {
      type: Boolean,
      default: true
    },
    isNoParamRequest: {
      type: Boolean,
      default: true
    },
    dataKey: String
  },
  data() {
    return {
      loading: false,
      options: '',
      valKey: this.valueKey,
      label: this.labelKey,
      hidden: false,
      canceled: false
    };
  },
  computed: {
    isObject() {
      if (this.valueType === 'object') {
        return true;
      }
      if (
        util.isObject(this.value) ||
        (this.value && this.value[0] && util.isObject(this.value[0]))
      ) {
        return true;
      }
      return false;
    },
    isRemote() {
      return new Boolean(this.remote);
    },
    results() {
      if (this.options.length) {
        return this.options;
      }
      if (this.filtrate) {
        if (Array.isArray(this.filtrate) && this.filtrate.length) {
          let ary = [];
          this.filtrate.forEach((items) => {
            let val = typeof items === 'string' ? items : items[this.valKey];
            ary = ary.concat(
              this.data.filter((item) => {
                return item[this.filtrateKey] == val;
              })
            );
          });
          return ary;
        } else {
          let val =
            typeof this.filtrate === 'string'
              ? this.filtrate
              : this.filtrate[this.valKey];
          return this.data.filter((item) => {
            return item[this.filtrateKey] == val;
          });
        }
      }
      return this.data;
    },
    models: {
      get() {
        return this.value;
      },
      set(val) {
        return val;
      }
    },
    findCode() {
      if (this.service) {
        return `/${this.service}${getDictList}`;
      }
      return findSysCode;
    }
  },
  watch: {
    sysCode: {
      immediate: true,
      handler(val, old) {
        if (val && val !== old) {
          this.label = this.service ? 'label' : 'shortName';
          this.valKey = this.service ? 'value' : 'cciValue';
          const options = store.get(val);
          if (options) {
            this.options = JSON.parse(JSON.stringify(options));
          } else {
            this.getData(val);
          }
        }
      }
    },
    url: {
      immediate: true,
      handler(val, old) {
        if (val && old !== val && !this.sysCode) {
          this.getData(this.sysCode);
        }
      }
    },
    param: {
      deep: true,
      handler(val, old) {
        if (this.url && JSON.stringify(val) !== JSON.stringify(old)) {
          this.options = [];
          this.getData(this.sysCode);
        }
      }
    }
  },
  beforeCreate() {
    this.getData = debounce(500, (sysCode, param, reload) => {
      this.getDatas(sysCode, param, reload);
    });
  },
  created() {
    this.bindEventBus();
  },
  mounted() {},
  methods: {
    createOption({ filtrate, data, readonly = false, h }) {
      let filte =
        filtrate && util.isObject(filtrate) ? filtrate[this.valKey] : filtrate;
      let eles = [];
      data.forEach((item) => {
        if (this.models === undefined && item.selected) {
          this.$emit(
            'input',
            this.valueType === 'object' ? item : item[this.valKey]
          );
        }
        if (
          (Object.prototype.hasOwnProperty.call(item, 'children') ||
            Object.prototype.hasOwnProperty.call(item, 'options')) &&
          (item.children || item.options)
        ) {
          const children = item.children || item.options;
          if (readonly) {
            eles.push(
              this.createOption({ filtrate, data: children, readonly, h })
            );
          } else {
            eles.push(
              h(
                'el-option-group',
                { attrs: { label: item[this.label] || item.label } },
                this.createOption({ filtrate, data: children, readonly, h })
              )
            );
          }
        } else {
          if (readonly) {
            if (item[this.valKey] === filte) {
              eles.push(item[this.label] || item.label);
            }
          } else {
            if (
              filte === null ||
              filte === undefined ||
              filte === '' ||
              filte === item[this.filtrateKey]
            ) {
              if (typeof item === 'string') {
                eles.push(
                  h('el-option', {
                    attrs: {
                      label: item,
                      value: item
                    }
                  })
                );
              } else {
                let content = [];
                const labelContent = item[this.label] || item.label;
                //判断它是否是html字符串
                if (
                  typeof labelContent === 'string' &&
                  /<[^>]+>/.test(labelContent)
                ) {
                  // 如果是 HTML 字符串，创建对应的 VNode
                  content = [
                    h('div', { domProps: { innerHTML: labelContent } })
                  ];
                } else {
                  content = [labelContent];
                }
                if (item.render) {
                  content = [item.render(h, item)];
                }
                if (item.tips) {
                  let attrs =
                    typeof item.tips == 'string'
                      ? { content: item.tips }
                      : item.tips;
                  content.push(
                    h('es-tips', {
                      attrs: attrs
                    })
                  );
                }

                eles.push(
                  h(
                    'el-option',
                    {
                      attrs: {
                        label: item[this.label] || item.label,
                        value: this.isObject ? item : item[this.valKey],
                        disabled: item.disabled
                      },
                      style: item.style
                    },
                    content
                  )
                );
              }
            }
          }
        }
      });
      return eles;
    },
    getDatas(sysCode, param, reload) {
      if (
        !reload &&
        (!this.ajax ||
          (!this.isNoParamRequest && Object.keys(this.param).length == 0))
      ) {
        return false;
      }
      let params = {};
      if (this.results.length === 0 || reload) {
        params = util.extend(
          {},
          this.param,
          sysCode ? { sysAppCode: sysCode, code: sysCode } : {},
          param ? param : {}
        );
      } else {
        return false;
      }
      if (!sysCode && !this.url) {
        return false;
      }
      this.loading = true;
      util
        .ajax({
          url: sysCode ? this.findCode : this.url,
          method: this.method,
          params: params,
          format: this.format,
          data: params
        })
        .then((res) => {
          this.loading = false;
          this.canceled = false;
          if (res.rCode === 0) {
            if (this.parseData) {
              this.options = this.parseData(
                JSON.parse(JSON.stringify(res.results))
              );
            } else {
              this.options = JSON.parse(JSON.stringify(res.results));
            }
            if (sysCode || this.dataKey) {
              this.$emit('optionSuccess', this.options);
              const bus = this.bus || this.$root.Bus;
              bus &&
                bus.$emit(
                  sysCode || this.dataKey,
                  JSON.parse(JSON.stringify(this.options))
                );
              store.set(sysCode, JSON.parse(JSON.stringify(this.options)));
            }
          } else {
            this.$message.error(res.msg);
          }
        })
        .catch((err) => {
          this.loading = false;
          if (err.message) {
            if (err.message !== 'canceled') {
              this.$message.error(err.message);
            }
            if (err.message === 'canceled' && sysCode) {
              this.canceled = true;
            }
          }
        });
    },
    search(query) {
      if (query && query !== '') {
        if (this.url && this.url !== '') {
          this.loading = true;
          let params = {};
          params[this.remoteKey] = query;
          params = util.extend({}, this.param, params);
          util
            .ajax({
              url: this.url,
              method: this.method,
              params: params,
              data: params
            })
            .then((res) => {
              this.loading = false;
              if (res.rCode === 0) {
                this.options = JSON.parse(JSON.stringify(res.results));
              }
            })
            .catch((err) => {
              this.loading = false;
              if (err.message && err.message !== 'canceled') {
                this.$message.error(err.message);
              }
            });
        } else {
          this.options = [];
        }
      } else {
        this.options = this.data;
      }
    },
    renderd(doms, h, type) {
      if (typeof this.$attrs[type] === 'boolean') {
        if (this.$attrs.render) {
          if (typeof this.$attrs.render == 'string') {
            try {
              doms.push(
                h(
                  'template',
                  { slot: type },
                  util.toFunction(this.$attrs.render)(h, this)
                )
              );
            } catch (error) {
              throw error;
            }
          } else {
            doms.push(
              h('template', { slot: type }, this.$attrs.render(h, this))
            );
          }
        }
      } else if (typeof this.$attrs[type] === 'object') {
        doms.push(
          h(
            'es-button',
            {
              slot: type,
              class: ['el-primeval', this.$attrs[type].class],
              props: {
                ...this.$attrs[type].props,
                size: this.$attrs[type].size
                  ? this.$attrs[type].size
                  : type === 'prefix' || type === 'suffix'
                  ? 'small'
                  : '',
                type: 'primary'
              },
              domProps: this.$attrs[type].icon
                ? { innerHTML: this.$attrs[type].icon }
                : {},
              on: {
                click: (e) => {
                  this.$attrs[type].click &&
                    this.$attrs[type].click(this, this.model);
                  this.$attrs[type].event &&
                    this.$attrs[type].event(this, this.model);
                }
              },
              ref: 'selectBtn'
            },
            [this.$attrs[type].text]
          )
        );
      } else {
        doms.push(h('template', { slot: type }, [this.$attrs[type]]));
      }
    },
    getLabel(value, data) {
      if ((value || value === 0) && data) {
        if (util.isObject(data)) {
          if (value === data[this.valKey]) {
            return data[this.label];
          } else if (value === data[this.label]) {
            return typeof value === 'string' ? value : String(value);
          } else if (
            Object.prototype.hasOwnProperty.call(data, 'children') &&
            data.children &&
            data.children.length
          ) {
            let val = this.getLabel(value, data.children);
            if (val) {
              return val;
            }
          }
        } else {
          if (
            typeof data === 'string' ||
            (!data.length && !this.sysCode && !this.url)
          ) {
            return value;
          }
          if (Array.isArray(data) && data.length) {
            let tempLabel = '';
            for (let i = 0; i < data.length; i++) {
              let val = this.getLabel(value, data[i]);
              if (val) {
                tempLabel = val;
                return val;
              }
            }
            if (!tempLabel) {
              return value;
            }
          }
        }
      }
    },
    reload() {
      this.getData(...arguments);
    },
    handleExpand(data, node) {
      if (this.onExpand) {
        this.onExpand(data, node);
      } else {
        if (this.lazy) {
          this.loadsub(data, node);
        }
        this.$emit('node-expand', data, node);
      }
    },
    //请求子节点数据
    loadsub(data, node) {
      if (data.state !== '' && (!data.children || data.children.length === 0)) {
        let param = {};
        if (this.subParamKey) {
          for (let i in this.subParamKey) {
            param[i] = data[this.subParamKey[i]];
          }
        } else {
          param[this.valKey] = data[this.valKey];
        }
        let params = util.extend({}, this.param, this.where, param);
        if (this.parseParams) {
          params = this.parseParams({ data, params, node });
        }
        if (this.onLoadsub) {
          this.onLoadsub({ data, params, node })
            .then((res) => {
              this.loading = false;
              this.$set(data, 'children', JSON.parse(JSON.stringify(res)));
              if (this.dataKey && this.esForm) {
                this.esForm.setSysCodes(
                  this.dataKey,
                  JSON.parse(JSON.stringify(this.options))
                );
              }
            })
            .catch((e) => {
              this.loading = false;
            });
        } else {
          util
            .ajax({
              method: this.method,
              url: this.url,
              data: params,
              params: params
            })
            .then((res) => {
              if (res.rCode === 0) {
                this.loading = false;
                let results = JSON.parse(JSON.stringify(res.results));
                if (Array.isArray(results)) {
                  if (data.id === results[0].id) {
                    this.$set(data, 'children', results[0].children);
                  } else {
                    this.$set(data, 'children', results);
                  }
                } else if (data.id === results.id) {
                  this.$set(data, 'children', results.children);
                } else {
                  console.error('tree数据格式错误');
                }
                if (this.dataKey && this.esForm) {
                  this.esForm.setSysCodes(
                    this.dataKey,
                    JSON.parse(JSON.stringify(this.options))
                  );
                }
              }
            })
            .catch((err) => {
              this.loading = false;
              if (err.message && err.message !== 'canceled') {
                this.$message.error(err.message);
              }
            });
        }
      }
    },
    toggleMenu() {
      this.$refs.elSelect.toggleMenu();
    },
    setOptions(val) {
      this.options = val;
    },
    bindEventBus() {
      const bus = this.bus || this.$root.Bus;
      bus && bus.$on(this.sysCode || this.dataKey, this.setOptions);
    },
    unbindEventBus() {
      const bus = this.bus || this.$root.Bus;
      bus && bus.$off(this.sysCode || this.dataKey, this.setOptions);
    }
  },
  beforeDestroy() {
    this.unbindEventBus();
  },
  render(h) {
    if (!this.hide) {
      if (this.readonly) {
        let dom = [];
        let models =
          this.models || this.models == 0
            ? JSON.parse(JSON.stringify(this.models))
            : '';
        if (typeof models === 'string' && models.includes(',')) {
          models = models.split(',');
        }
        if (Array.isArray(models)) {
          dom = models.map((item) => {
            if (util.isObject(item)) {
              if (!item[this.label] && !item.label) {
                const label = this.getLabel(this.value[this.valKey], data);
                return label !== null ? label : this.value[this.valKey];
              }
              return item[this.label] || item.label;
            }
            const label = this.getLabel(item, this.results);
            return label !== null ? label : item;
          });
        } else if (util.isObject(models)) {
          if (!models[this.label] && !models.label) {
            const label = this.getLabel(models[this.valKey], this.results);
            dom = [label !== null ? label : models[this.valKey]];
          } else {
            dom = [models[this.label] || models.label];
          }
        } else {
          const label = this.getLabel(models, this.results);
          dom = [label !== null ? label : models];
        }
        console.log('dom', dom);
        return h(
          'div',
          {
            class: [
              {
                'es-plain': this.plain,
                'es-textarea__inner el-textarea__inner': this.multiple,
                'el-input__inner es-input__inner': !this.multiple
              }
            ],
            domProps: {
              innerHTML: dom.join(this.symbol)
            }
          },
          []
        );
      }
      let doms = [];
      let cls = [];
      if (this.results.length && !this.tree) {
        if (this.filtrate && Array.isArray(this.filtrate)) {
          doms = this.filtrate.map((item) => {
            return h(
              'el-option-group',
              {
                attrs: {
                  label: item[this.label] || item.label,
                  key: item[this.valKey]
                }
              },
              this.createOption({ filtrate: item, data: this.results, h })
            );
          });
        } else {
          doms = this.createOption({
            filtrate: this.filtrate,
            data: this.results,
            h
          });
        }
      }
      let { prefix, suffix, prepend, append, ...attrs } = this.$attrs;
      if (this.$slots.prefix) {
        doms.push(h('template', { slot: 'prefix' }, this.$slots.prefix));
      } else if (prefix) {
        this.renderd(doms, h, 'prefix');
        cls.push('es-select-button-prefix');
      }
      if (this.$slots.suffix) {
        doms.push(h('template', { slot: 'suffix' }, this.$slots.suffix));
      } else if (suffix) {
        this.renderd(doms, h, 'suffix');
        cls.push('es-select-button-suffix');
      }
      if (this.$slots.prepend) {
        doms.push(h('template', { slot: 'prepend' }, this.$slots.prepend));
      } else if (prepend) {
        this.renderd(doms, h, 'prepend');
      }
      if (this.$slots.append) {
        doms.push(h('template', { slot: 'append' }, this.$slots.append));
      } else if (append) {
        this.renderd(doms, h, 'append');
      }
      if (!this.models && this.results.length && this.defaultValue) {
        if (this.valueType === 'object') {
          this.models = this.results[0];
          //this.$emit('input', this.results[0]);
        } else {
          this.models = this.results[0][this.valKey];
          //this.$emit('input', this.results[0][this.valKey]);
        }
        this.$emit('change', this.results[0], 0);
      }
      return h(
        'el-select',
        {
          class: cls,
          ref: 'elSelect',
          directives: [
            {
              name: 'show',
              value: this.display
            }
          ],
          props: {
            ...attrs,
            multiple: this.multiple,
            valueType: this.valueType,
            valueKey: this.valKey,
            labelKey: this.label,
            loading: this.loading,
            disabled: this.disabled,
            tree:
              typeof this.tree === 'boolean'
                ? this.tree
                : { ...this.tree, lazy: this.lazy },
            data: this.results,
            // 父级是否可选
            parentCheck: this.parentCheck,
            // 父级可选时父级是否作为单独的值
            parentAsValue: this.parentAsValue,
            value: this.models
          },
          attrs: {
            remoteMethod: !this.remoteMethod ? this.search : this.remoteMethod,
            remote: this.remote
          },
          on: {
            ...this.$listeners,
            'node-expand': this.handleExpand
          }
        },
        this.$slots.default ? this.$slots.default : doms
      );
    } else {
      return '';
    }
  }
};
</script>
