<template>
  <el-form class="mu-form-container" :model="value" :disabled="config.disabled" :name="config.name" :ref="config.name"
           :class="{'is-disabled':config.disabled}" v-mu-loading="isLoading"
           onkeydown='if(event.keyCode==13) return false;'>
    <slot name="header"></slot>
    <mu-row v-for="(rowItem, index) in getFields" :key="index">
      <mu-col :config="{span: field.column}" v-for="(field, fIndex) in rowItem" :key="fIndex" v-show="!field.isHide">
        <slot v-if="field.configDialog && field.configDialog.slot" :name="field.configDialog.slot"></slot>
        <slot :name="field.name" v-if="field.type === 'slot'"></slot>
        <component v-else-if="field.type === 'custom'" :is="getCurrentComp(field)" :title="field"></component>
        <Field v-else :config="field" :key="fIndex" v-model="value[field.key]" :currentValue="value"
               @on-change="onDataUpdate"></Field>
      </mu-col>
    </mu-row>
    <slot></slot>
  </el-form>
</template>
<script lang="ts">
  import { Component, Vue, Prop, Watch } from 'vue-property-decorator';
  import { Config } from './types';
  import Field from './field'
  import { SystemApi } from '../../api';
  // import { PageModule } from '@/store/modules';
  import util from '../../common/utils/util';
  import { SourceConfig } from '../../common/model';

  interface iProps {
    config: Config,
    extension?: (form: any, value: any) => void,
    value?: any
  }

  interface FormConfig {
    layout?: string,
    name?: string,
    column?: number,
    disabled?: boolean,
    items?: any,
    children?: any,
    sourceConfigs?: Array<SourceConfig>
  }

  @Component({
    components: { Field }
  })
  export default class MuForm extends Vue {
    @Prop({ default: () => [] }) private value: any;
    @Prop({ default: () => [] }) private config: FormConfig;
    @Prop({ default: Function }) private extension?: (form: any, value: any) => void;
    defaultValue = JSON.parse(JSON.stringify(this.value || {}));
    extensionInput: Array<string> = ['select', 'selectTable', 'selectAdd', 'selectTree', 'radio', 'checkbox', 'cascader'];
    isLoading: boolean = false;
    defaultConfig: any = {
      name: util.generateUUID(),
      column: 12
    };

    created() {
      Object.keys(this.defaultConfig).forEach((key: string) => {
        if (this.config[key] === undefined) return this.config[key] = this.defaultConfig[key];
      });
    }

    // 检测值
    @Watch("value")
    onValueChanged(val: any) {
      this.defaultValue = JSON.parse(JSON.stringify(this.value || []));
    }

    /**
     * 获取输入域元素
     */
    get getFields() {
      const { column, items } = this.config;
      let currentColumn = 0;
      const result = [];
      (items || []).forEach((item: any, index: number) => {
        if (this.value[item.key] === undefined)
          this.$set(this.value, item.key, item.multiple || item.type === 'checkbox' ? [] : '');
        if (item.selectKey && this.value[item.selectKey] === undefined) this.$set(this.value, item.selectKey, '');
        item.lang = {};
        // 处理表单布局
        item.column = item.column || column || 12;
        // 处理extension
        if (this.extensionInput.includes(item.type)) {
          if (item.extension && !item.defalutExtension) item.defalutExtension = item.extension;
          item.extension = (value: any) => {
            item.handleFormItem = value;
            item.defalutExtension && item.defalutExtension(value);
            // 处理格式
            this.$nextTick(() => {
              (this.$refs[this.config.name] as any).clearValidate();
            });
          };
        }
        currentColumn = currentColumn + (item.column);
        if ((index === 0) || (currentColumn > 12)) {
          result.push([item]);
          currentColumn = item.column;
        } else {
          result[result.length - 1].push(item);
        }
      });
      // 处理格式
      this.$nextTick(() => {
        (this.$refs[this.config.name] as any).clearValidate();
      });
      return result;
    }

    mounted() {
      const params = {
        onHandleSource: this.onHandleData,
        onShowEdit: async (isEdit: boolean, callback?: Function) => {
          if (this.config.children && this.config.children.length)
            this.editAllForm(isEdit);
          this.config.disabled = !isEdit;
          if (!isEdit) {
            await this.$emit('input', JSON.parse(JSON.stringify(this.defaultValue)));
          } else {
            this.defaultValue = JSON.parse(JSON.stringify(this.value || {}));
          }
          (this.$refs[this.config.name] as any).clearValidate();
          callback && callback();
        },
        setDisabled: (options: any) => {
          Object.keys(options).forEach((key: string) => {
            (this.config.items || []).forEach((item: any, index: number) => {
              if (item.key === key) {
                this.$set(this.config.items[index], 'disabled', options[key]);
              }
            });
          });
        },
        setHide: (options: any) => {
          Object.keys(options).forEach((key: string) => {
            (this.config.items || []).forEach((item: any, index: number) => {
              if (item.key === key) this.$set(this.config.items[index], 'isHide', options[key]);
            });
          });
        },
        resetFields: (data: any) => (this.$refs[this.config.name] as any).resetFields(data),
        validateField: (data: any) => (this.$refs[this.config.name] as any).validateField(data),
        clearValidate: (data: any) => (this.$refs[this.config.name] as any).clearValidate(data),
        loadFormItems: this.loadFormItem
      };
      this.extension && this.extension(this.$refs[this.config.name], params);
    }

    /**
     * 处理表单数据，增删改查
     * @author tanxj
     * @param type load/add/update/delete
     * @param params 数据参数
     * @param callback 回调
     */
    onHandleData(type: string, params: any, callback?: Function) {
      const currentSource = (this.config.sourceConfigs || []).find((item: any) => item.type === type);
      switch (type) {
        case "load":
          return this.handleLoad(currentSource, params, callback);
        case "add":
          return this.submitSource('add', currentSource, callback);
        case "update":
          return this.submitSource('update', currentSource, callback);
        case "delete":
          return this.submitDelete(currentSource, callback);
        default:
          return;
      }
    }

    /**
     * 加载数据
     * @author tanxj
     * @param source 数据源配置
     * @param params 加载数据参数
     * @param callback 回调
     */
    handleLoad(source: any, params: any, callback?: Function) {
      if (!source) return this.loadFormItem({}, callback);
      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;
          onSuccess && onSuccess(ret.data, this);
          return this.loadFormItem(ret, callback);
        }, (error: any) => {
          this.isLoading = false;
          onError && onError(error);
        });
      };
      onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...params }, next, this.value) : next({ ...api.params, ...params });
    }

    /**
     * 加载下拉数据
     * @author tanxj
     * @param data 参数数据
     * @param callback 回调
     */
    loadFormItem(data?: any, callback?: any) {
      this.config.items.forEach((item: any) => {
        if (!this.extensionInput.includes(item.type)) return;
        item.handleFormItem && item.handleFormItem.onHandleData && item.handleFormItem.onHandleData('load');
      });
      callback && callback(data);
    }

    /**
     * 处理数据源数据
     * @author tanxj
     * @param type 提交类型 add ｜ update
     * @param currentSource 数据源配置
     * @param callback 回调
     */
    submitSource(type: string, currentSource: any, callback?: Function) {
      const currentForm: any = this.$refs[this.config.name];
      const { api, onValid, onErrorValid, onBeforeSubmit, onSuccess, onError } = (currentSource || {}) as any;
      onValid && onValid(currentForm, this);
      const result: any[] = (this.config.children && this.config.children.length) ? this.handleAllForm() : [];
      const currentResult = new Promise((resolve: any, reject: any) => {
        currentForm.validate((valid: any) => {
          if (!valid) return onErrorValid && onErrorValid(valid, currentForm, this);
          resolve();
        })
      });
      Promise.all(result.concat([currentResult])).then(() => {
        if (!currentSource) return callback && callback();
        // 处理数据
        const next = (params: any) => {
          const { serviceName, funcName, config } = api;
          SystemApi.handleApi({ serviceName, funcName, params: params }, config).then((ret: any) => {
            this.defaultValue = JSON.parse(JSON.stringify(this.value || {}));
            onSuccess && onSuccess(ret.data, this);
            callback && callback(ret);
          }, (error: any) => {
            onError && onError(error);
          });
        };
        onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...this.value }, next, this) : next({ ...api.params, ...this.value });
      });
    }


    /**
     * 处理数据源数据
     * @author tanxj
     * @param currentSource 数据源配置
     * @param callback 回调
     */
    submitDelete(currentSource: any, callback?: Function) {
      const { api, onBeforeSubmit, onSuccess, onError } = (currentSource || {}) as any;
      if (!currentSource) return callback && callback();
      // 处理数据
      const next = (params: any) => {
        const { serviceName, funcName, config } = api;
        SystemApi.handleApi({ serviceName, funcName, params: params }, config).then((ret: any) => {
          onSuccess && onSuccess(ret.data, this);
          callback && callback(ret);
        }, (error: any) => {
          onError && onError(error);
        });
      };
      onBeforeSubmit ? onBeforeSubmit({ ...api.params, ...this.value }, next, this) : next({ ...api.params, ...this.value });
    }

    /**
     * 处理页面的form类型
     * @author tanxj
     */
    handleAllForm() {
      const result = [];
      this.config.children.forEach((cItem: any) => {
        const currentLog = 0;
        if (currentLog) {
          result.push(new Promise((resolve: any, reject: any) => {
            currentLog.form.validate((valid: any) => {
              if (valid) {
                if (currentLog.type === 'table') {
                  const { getAddStatus, getEditStatus, onHandleData } = currentLog.extension;
                  if (getAddStatus()) onHandleData('add');
                  if (getEditStatus()) onHandleData('update');
                }
                resolve();
              }
            });
          }));
        }
      });
      return result;
    }

    /**
     * 取消form类型
     * @author tanxj
     * @param isEdit 是否编辑
     */
    editAllForm(isEdit: boolean) {
      this.config.children.forEach((cItem: any) => {
        const currentLog = 0;
        if (currentLog.type === 'table') {
          const { getAddStatus, getEditStatus, onHandleAdd, onHandleEdit, onShowEdit } = currentLog.extension;
          if (isEdit) return onShowEdit(true);
          if (getAddStatus()) onHandleAdd('cancel');
          if (getEditStatus()) onHandleEdit('cancel');
        } else {
          const { onShowEdit } = currentLog.extension;
          onShowEdit(isEdit);
        }
      });
    }

    /**
     * 数据更新
     * @author tanxj
     * @param options 需要更新的数据对象
     */
    onDataUpdate(options: any) {
      this.value[options.key] = options.value;
    }

    /**
     * 获取当前组件
     * @author tanxj
     * @param fieldItem 当前组件对象
     */
    getCurrentComp(fieldItem: any) {
      const self = this;
      return {
        template: fieldItem.custom(fieldItem, self.value),
        props: ['title'],
        data() {
          return {
            config: fieldItem,
            form: self.value
          };
        }
      };
    }
  }
</script>
<style lang="scss" scoped>
  .mu-form-container {
    position: relative;
  }
</style>
