<template>
  <mu-form-item :label="currentConfig.label" :prop="currentConfig.key" :inline="currentConfig.inline"
                :tip="currentConfig.tip" :validators="currentConfig.validators" trigger="change">
    <el-cascader class="mu-cascader" :class="'mu-'+ currentConfig.size"
                 popper-class="mu-cascader-options"
                 v-model="value"
                 :show-all-levels="currentConfig.showAll"
                 :change-on-select="currentConfig.changeOnSelect"
                 :options="currentConfig.options"
                 :disabled="currentConfig.disabled"
                 :readonly="currentConfig.readonly"
                 :filterable="currentConfig.filterable"
                 :filter-method="currentConfig.filterMethod"
                 :clearable="currentConfig.clearable"
                 :separator="currentConfig.separator"
                 :props="currentConfig.optionsConfig.props"
                 :placeholder="currentConfig.placeholder"
                 @visible-change="handleShowChange"
                 @expand-change="handleExpandChange"
                 @change="handleChange"
                 @focus="handleFocus"
                 @blur="handleBlur">
    </el-cascader>
  </mu-form-item>
</template>

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

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

  interface InputConfig {
    name?: string,
    size?: Size,
    placeholder?: string
    label?: string,
    key?: string,
    inline?: boolean,
    tip?: string,
    validators?: any,
    readonly?: boolean,
    clearable?: boolean,
    filterable?: boolean,
    filterMethod?: Function,
    disabled?: boolean,
    showAll?: boolean,
    separator?: string,
    options?: any,
    optionsConfig?: {
      props?: {
        expandTrigger?: boolean,
        lazy?: boolean,
        label?: string,
        value?: string,
        children?: string,
        disabled?: string,
        checkStrictly?: boolean
        multiple?: boolean,
        lazyLoad?: Function
      },
      optionsLoad?: Function,
      sourceConfigs?: any
    },
    visibleChange?: Function,
    expandChange?: Function
  }

  @Component({
    name: 'mcInput'
  })
  export default class extends Vue {
    @Prop({ default: () => [] }) private value!: any;
    @Prop({ default: () => [] }) private config!: InputConfig;
    @Prop() private extension?: (value: any) => void;
    private currentValue: any = JSON.parse(JSON.stringify(this.value)) || [];
    private isLoading: boolean = false;
    private defaultConfig: any = {
      size: 'medium',
      placeholder: '请输入',
      options: [],
      showAll: true,
      changeOnSelect: false,
      filterable: true,
      optionsConfig: {
        props: {
          expandTrigger: false,
          lazy: false,
          label: "label",
          value: "value",
          children: "children",
          disabled: "disabled",
          multiple: false,
          checkStrictly: false
        }
      }
    };

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

    // 合并默认数据
    get currentConfig() {
      const result = this.config;
      Object.keys(this.defaultConfig).forEach((key: string) => {
        if (this.config[key] === undefined) result[key] = this.defaultConfig[key];
        // 处理options的配置
        if (key === 'optionsConfig') {
          Object.keys(this.defaultConfig.optionsConfig).forEach((optionKey: string) => {
            if (optionKey === 'props' && this.config.optionsConfig.props) {
              Object.keys(this.defaultConfig.optionsConfig.props).forEach((propsKey: string) => {
                if (this.config.optionsConfig.props[propsKey] === undefined)
                  result.optionsConfig.props[propsKey] = this.defaultConfig.optionsConfig.props[propsKey];
              });
              if (this.config.optionsConfig.props.lazy && !this.config.optionsConfig.props.lazyLoad) {
                result.optionsConfig.props.lazyLoad = (node, resolve) => this.handleLazyLoad(node, resolve);
              }
            }
            if (this.config.optionsConfig[optionKey] === undefined)
              result.optionsConfig[optionKey] = this.defaultConfig.optionsConfig[optionKey];
          });
          return;
        }
      });
      return result;
    }

    mounted() {
      this.extension && this.extension({
        onHandleData: (type: string, params?: any, callback?: Function) => {
          const { sourceConfigs, optionsLoad } = this.config.optionsConfig;
          const currentSource = (sourceConfigs || []).find((item: any) => item.type === type);
          switch (type) {
            case "load":
              if (!this.config.options) this.$set(this.config, 'options', []);
              if (optionsLoad) return this.config.options = optionsLoad();
              if (!currentSource) return callback && callback();
              return this.handleLoad(currentSource, params, callback);
            default:
              return;
          }
        }
      });
    }

    /**
     * 加载数据
     * @author tanxj
     * @param source 数据源配置
     * @param params 加载数据参数
     * @param callback 回调
     */
    handleLoad(source: any, params?: any, callback?: Function) {
      this.isLoading = true;
      const { api, onBeforeSubmit, onSuccess, onError } = source;
      const next = (currentParams: any) => {
        const { serviceName, funcName, config } = api;
        SystemApi.handleApi({ serviceName, funcName, params: currentParams }, config).then(async (ret: any) => {
          this.isLoading = false;
          if (onSuccess) this.config.options = onSuccess(ret.data, this.config) || ret.data;
          callback && callback(ret);
        }, (error: any) => {
          this.isLoading = false;
          onError && onError(error);
        });
      };
      onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...params }, next, this.value) : next({ ...api.params, ...params });
    }

    /**
     * 懒加载数据
     * @author tanxj
     * @param params 加载数据参数
     * @param callback 回调
     */
    handleLazyLoad(node: any, resolve: any) {
      const currentSource = (this.config.optionsConfig.sourceConfigs || []).find((item: any) => item.type === 'lazyLoad');
      if (!currentSource) return resolve([]);
      this.isLoading = true;
      const { api, onBeforeSubmit, onSuccess, onError } = currentSource;
      const next = (currentParams: any) => {
        const { serviceName, funcName, config } = api;
        SystemApi.handleApi({ serviceName, funcName, params: currentParams }, config).then(async (ret: any) => {
          this.isLoading = false;
          if (onSuccess) resolve(onSuccess(ret.data, this.config) || ret.data);
        }, (error: any) => {
          this.isLoading = false;
          onError && onError(error);
        });
      };
      onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...node }, next, this.value) : next({ ...api.params, ...node });
    }

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

    /**
     * 当展开节点发生变化时触发
     * @author tanxj
     * @param value 值
     */
    handleExpandChange(value: any) {
      this.currentConfig.expandChange && this.currentConfig.expandChange(value);
    }

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

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

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

