<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-table-option"
               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="'selectTable'+ currentConfig.name">
      <el-option :value="currentValue" :label="currentConfig.multiple ? currentValue[0] : currentValue">
        <section @click="selectOption">
          <mu-table v-model="config.options" :config="currentConfig.optionsConfig"
                    :extension="(table, value)=>{ this.onTableData = value.onHandleData;
                     this.setCurrentRow = value.setCurrentRow;
                     this.setSelection = value.setSelection;
                     this.clearSelection = value.clearSelection
                     this.clearCurrentRow = value.clearCurrentRow}"></mu-table>
        </section>
      </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,
    remote?: boolean,
    remoteMethod?: Function,
    filterMethod?: Function,
    options?: any,
    optionsConfig?: any,
    visibleChange?: Function
  }

  @Component({
    name: 'mcSelectTable'
  })
  export default class extends Vue {
    @Prop({ default: () => [] }) private value!: any;
    @Prop({ default: () => [] }) private config!: InputConfig;
    @Prop() private extension?: (value: any) => void;
    initLoading: boolean = false;
    private currentValue: any = this.config.multiple ? this.value || [] : this.value;
    private selectedLabel: any = this.config.multiple ? [] : '';
    private select: any = [];
    private setSelection: Function | undefined;
    private setCurrentRow: Function | undefined;
    private clearSelection: Function | undefined;
    private clearCurrentRow: Function | undefined;
    private onTableData: Function | undefined;
    private defaultOptions: any = [];
    private defaultConfig: any = {
      name: "selectTable",
      size: 'medium',
      placeholder: '请选择',
      options: [],
      optionsConfig: {
        "size": "small",
        'height': 150,
        "props": {
          label: 'label',
          value: 'value'
        }
      }
    };

    // 合并默认数据
    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.rowKey = result.optionsConfig.props.value;
      if (this.config.multiple) {
        this.multiSelect(result);
      } else {
        result.optionsConfig.currentChange = this.currentChange;
        result.optionsConfig.highlightCurrentRow = true;
      }
      return result;
    }

    // 多选操作
    multiSelect(result) {
      result.optionsConfig.showSelection = true;
      result.optionsConfig.selection = [];
      result.optionsConfig.onSelect = (isChecked: boolean, row: any) => {
        const { label, value } = this.config.optionsConfig.props;
        if (isChecked) {
          this.select.push({
            [label]: row[label],
            [value]: row[value]
          });
          this.value.push(row[value]);
          this.selectedLabel.push(row[label]);
          this.$emit('on-change', this.value);
        } else {
          const index = this.select.findIndex((item: any) => item[value] === row[value]);
          if (index < 0) return;
          this.select.splice(index, 1);
          this.value.splice(index, 1);
          this.selectedLabel.splice(index, 1);
          this.$emit('on-change', this.value);
        }
      };
      result.optionsConfig.onSelectAll = (selection: any) => {
        if (!selection.length) return this.$emit('input', []);
        const { label, value } = this.config.optionsConfig.props;
        this.select = selection;
        this.selectedLabel = selection.map((item: any) => item[label]);
        this.$emit('input', selection.map((item: any) => item[value]));
        this.$emit('on-change', this.value);
      };
    }

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

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

    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.onTableData && this.onTableData('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: (currentValue: any) => {
          this.setValue(currentValue);
          this.$emit('input', currentValue);
        }
      });
      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) {
      const { label, value } = this.currentConfig.optionsConfig.props;
      if (this.config.multiple) {
        // 为空处理
        if (!currentValue || currentValue.length === 0) {
          this.selectedLabel = [];
          return this.clearSelection();
        }
        this.select = this.config.options.filter((item: any) => (currentValue || []).includes(item[value]));
        this.setSelection(this.select, true);
        this.selectedLabel = this.select.map((item: any) => item[label]);
      } else {
        // 为空处理
        if (!currentValue) {
          this.selectedLabel = "";
          return this.setCurrentRow();
        }
        const currentRow = this.config.options.find((item: any) => currentValue === item[value]);
        if (currentRow) {
          this.setCurrentRow(currentRow);
          this.selectedLabel = currentRow[label];
        }
      }
    }

    /**
     * 过滤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 值
     */
    handleChange(value: any) {
      this.$emit('input', value);
    }

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

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

    /**
     * 选择事件
     * @author tanxj
     * @param any event 事件
     */
    selectOption(event) {
      event.preventDefault();
      event.stopPropagation();
    }

    // change事件
    @Emit('on-change')
    currentChange(data: any) {
      if (!data) return;
      (this.$refs['selectTable' + this.currentConfig.name] as any).blur();
      const { label, value } = this.currentConfig.optionsConfig.props;
      this.selectedLabel = data[label];
      this.$emit('input', data[value]);
    }


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

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

