<template>
  <mu-form-item :label="currentConfig.label" :prop="currentConfig.key" :inline="currentConfig.inline"
                :tip="currentConfig.tip"
                :validators="currentConfig.validators" trigger="change">
    <el-select class="mu-select" :class="['mu-'+ currentConfig.size, !!currentConfig.disabled ? 'mu-disabled': '' ]"
               :popper-class="'mu-select-tree-option ' + (currentConfig.className ? currentConfig.className : '')"
               v-model="selectedLabel"
               :name="currentConfig.name"
               :multiple="currentConfig.multiple"
               :size="currentConfig.size==='large' ? 'small' : 'mini'"
               :disabled="currentConfig.disabled"
               :placeholder="currentConfig.placeholder"
               :clearable="currentConfig.clearable"
               :filterable="currentConfig.filterable"
               :remote="currentConfig.remote"
               :remote-method="currentConfig.remoteMethod"
               :filter-method="filterMethod"
               no-match-text="未搜寻到相关资料"
               @visible-change="handleShowChange"
               @clear="clearSelect"
               @remove-tag="removeTag"
               @change="handleChange"
               @focus="handleFocus"
               @blur="handleBlur"
               :ref="'selectTree'+ currentConfig.name">
      <el-option :value="currentValue" :label="currentConfig.multiple ? currentValue[0] : currentValue">
        <mu-tree v-model="config.options" :config="currentConfig.optionsConfig" :extension="(tree, value)=>{
                     this.getNode = value.getNode;
                     this.onTreeData = value.onHandleData;
                     this.setCheckedNodes = value.setCheckedNodes;
                     this.setCheckedKeys = value.setCheckedKeys;
                     this.setCurrentKey = value.setCurrentKey;
                     this.clearSelection = value.clearChecked;
                     this.getCheckedNodes =  value.getCheckedNodes;
                     this.clearCurrentNode = value.clearCurrentNode}"></mu-tree>
      </el-option>
    </el-select>
  </mu-form-item>
</template>

<script lang="ts">
  import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator';

  type Size = 'small' | 'medium' | 'large';

  interface InputConfig {
    name?: string,
    size?: Size,
    disabled?: boolean,
    placeholder?: string
    multiple?: boolean,
    label?: string,
    key?: string,
    inline?: boolean,
    tip?: string,
    validators?: any,
    clearable?: boolean,
    filterable?: boolean,
    filterMethod?: Function,
    remote?: boolean,
    remoteMethod?: Function,
    options?: any,
    optionsConfig?: any,
    visibleChange?: Function
  }

  @Component({
    name: 'mcSelectTree'
  })
  export default class extends Vue {
    @Prop({ default: () => [] }) private value!: any;
    @Prop({ default: () => [] }) private config!: InputConfig;
    @Prop() private extension?: (value: any) => void;
    private currentValue: any = this.value;
    private selectedLabel: any = this.config.multiple ? [] : '';
    private initLoading: boolean = false;
    private getNode: Function | undefined;
    private setCheckedNodes: Function | undefined;
    private setCheckedKeys: Function | undefined;
    private setCurrentKey: Function | undefined;
    private clearSelection: Function | undefined;
    private clearCurrentNode: Function | undefined;
    private getCheckedNodes: Function | undefined;
    private onTreeData: Function | undefined;
    private select: any = [];
    private defaultOptions: any = [];
    private defaultConfig: any = {
      size: 'medium',
      placeholder: '请选择',
      options: [],
      optionsConfig: {
        props: {
          label: 'label',
          value: 'value',
          disabled: 'disabled'
        }
      }
    };

    // 检测值
    @Watch("config.options")
    onOptionsChanged(val: any) {
      this.initOption();
    }

    // 检测值
    @Watch("value")
    onValueChanged(val: any) {
      this.currentValue = val;
     this.setValue(this.currentValue);
    }

    // 合并默认数据
    get currentConfig() {
      const result = this.config;
      Object.keys(this.defaultConfig).forEach((key: string) => {
        if (key === 'options' && !this.config.options) {
          this.$set(this.config, 'options', []);
        }
        if (key === 'optionsConfig') {
          if (!this.config.optionsConfig) this.config.optionsConfig = {};
          Object.keys(this.defaultConfig.optionsConfig).forEach((optionKey: string) => {
            if (this.config.optionsConfig[optionKey] === undefined)
              result.optionsConfig[optionKey] = this.defaultConfig.optionsConfig[optionKey];
          });
        } else if (this.config[key] === undefined) result[key] = this.defaultConfig[key];
      });
      result.optionsConfig.nodeKey = result.optionsConfig.props.value;
      result.optionsConfig.defaultExpandAll = true;
      if (this.config.multiple) {
        this.multiSelect(result);
      } else {
        result.optionsConfig.currentChange = this.currentChange;
        result.optionsConfig.highlightCurrent = true;
      }
      return result;
    }

    mounted() {
      this.extension && this.extension({
        onHandleData: (type: string, params?: any, callback?: Function) => {
          switch (type) {
            case "load":
              if (this.config.optionsConfig.optionsLoad) {
                this.config.options = this.config.optionsConfig.optionsLoad();
                this.setValue(this.value);
                callback && callback(this.config.options);
                return;
              }
              this.onTreeData && this.onTreeData('load', params, (ret: any, result: any) => {
                if (result) {
                  this.config.options = result;
                  this.setValue(this.value);
                  callback && callback(this.config.options);
                }
              });
              return;
            default:
              return;
          }
        },
        setValue: (value: any) => {
          this.setValue(value);
          this.$emit('input', value);
        }
      });
      this.initOption();
    }

    /**
     * 初始化options值
     */
    initOption() {
      if (this.initLoading || !this.config.options || this.config.options.length === 0) return;
      this.initLoading = true;
      if (this.value) this.setValue(this.value);
    }

    /**
     * 设置值
     * @author tanxj
     * @param currentValue 值
     */
    setValue(currentValue: any) {
      if (this.config.multiple) {
        // 为空处理
        if (!currentValue || currentValue.length === 0) {
          this.selectedLabel = [];
          return this.clearSelection();
        }
        if (!currentValue) return this.setCheckedKeys([]);
        this.setCheckedKeys(currentValue);
      } else {
        // 为空处理
        if (!currentValue) {
          this.selectedLabel = "";
          return this.setCurrentKey();
        }
        this.$nextTick(() => {
          this.setCurrentKey(currentValue);
          const currentNode = this.getNode(currentValue);
          if (currentNode && currentNode.data) {
            this.selectedLabel = currentNode.data[this.currentConfig.optionsConfig.props.label];
          }
        });
      }
    }

    // 多选操作
    multiSelect(result: any) {
      result.optionsConfig.showCheckbox = true;
      result.optionsConfig.checkChange = (isChecked: boolean, node: any) => {
        const { label, value } = this.currentConfig.optionsConfig.props;
        if (isChecked) {
          if (!node[this.currentConfig.optionsConfig.props.children || 'children']) {
            this.select.push({
              [label]: node[label],
              [value]: node[value]
            });
            this.value.push(node[value]);
            this.selectedLabel.push(node[label]);
          }
        } else {
          const index = this.select.findIndex((item: any) => item[value] === node[value]);
          if (index < 0) return;
          this.select.splice(index, 1);
          this.value.splice(index, 1);
          this.selectedLabel.splice(index, 1);
        }
      };
    }


    /**
     * 去除多选tag值
     * @author tanxj
     * @param tagItem 多选的tag值
     */
    removeTag(tagItem: string) {
      this.setCheckedNodes(this.getCheckedNodes().filter((item: any) =>
        (this.currentValue || []).includes(item[this.currentConfig.optionsConfig.props.value])), false);
    }

    /**
     * 清除下拉选中值
     * @author tanxj
     * @param any event 事件
     */
    clearSelect() {
      this.config.multiple ? this.clearSelection() : this.setCurrentKey();
    }

    /**
     * 过滤options的数据
     * @author tanxj
     * @param value 值
     */
    filterMethod(value: any) {
      if (this.currentConfig.filterMethod) return this.currentConfig.filterMethod(value, this.config.options);
      if (this.defaultOptions.length === 0) this.defaultOptions = JSON.parse(JSON.stringify(this.config.options));
      this.config.options = this.defaultOptions.filter((item: any) =>
        item[this.currentConfig.optionsConfig.props.label].indexOf(value) > -1);
      this.$forceUpdate();
    }

    /**
     * 下拉框出现/隐藏时触发
     * @author tanxj
     * @param value 值
     */
    handleShowChange(value: any) {
      this.currentConfig.visibleChange && this.currentConfig.visibleChange(value);
    }

    /**
     * 输入框值改变
     * @author tanxj
     * @param value 值
     */
    @Emit('on-change')
    currentChange(value: any) {
      (this.$refs['selectTree' + this.currentConfig.name] as any).blur();
      this.selectedLabel = value[this.currentConfig.optionsConfig.props.label];
      this.$emit('input', value[this.currentConfig.optionsConfig.props.value]);
    }

    /**
     * 输入框值改变
     * @author tanxj
     * @param value 值
     */
    @Emit('on-change')
    handleChange(value: any) {
      this.$emit('input', value);
    }

    /**
     * 失去焦点事件
     * @author tanxj
     * @param any event 事件
     */
    handleBlur(event: any) {
      this.$emit('on-blur', event.target.value, event, this.config.key);
    }

    /**
     * 使input 获取焦点
     * @author tanxj
     * @param any event 事件
     */
    handleFocus(event: any) {
      this.$emit('on-focus', event);
    }
  }
</script>

