<style lang="scss">
.zee-scheme-form {
  &__flex-wrap {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
  }
  &__group-title {
    font-weight: bold;
    padding: 15px 5px;
    border-bottom: 1px solid #d9d9d9;
    margin-bottom: 15px;
  }
  &__group-content {
    margin: 15px 0px;
  }
  &__footer {
    padding-top: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
</style>

<template>
  <el-form
    ref="form"
    class="zee-scheme-form"
    :size="size"
    :class="formClass"
    :model="formModel"
    :label-width="labelWidth"
    :label-position="labelPosition || labelWidth ? 'right' : 'top'"
    v-bind="formProps"
  >
    <scheme-form-render
      :title-class="titleClass"
      :content-class="contentClass"
      :item-class="itemClass"
      :col-class="colClass"
      :group-class="groupClass"
      :list="formList"
      :value="model"
      :model="model"
      :span="span"
      :type="type"
      :params="params"
      @item-change="onItemChange"
      @form-item-change="onFormItemChange"
      @item-update="onItemUpdate"
    >
      <slot v-for="key in slotKeys" :name="key" :slot="key"></slot>
    </scheme-form-render>
    <div v-if="$scopedSlots.footer" class="zee-scheme-form__footer">
      <slot name="footer" :size="size" :validate="validate" :reset="reset" :model="model"></slot>
    </div>
  </el-form>
</template>

<script>
import SchemeFormRender from './scheme-form-render';
import { cloneDeep, set } from '../utils';

export default {
  name: 'SchemeForm',
  components: { SchemeFormRender },
  props: {
    value: Object,
    list: Array,
    formClass: String,
    titleClass: String,
    contentClass: String,
    itemClass: String,
    colClass: [String, Function],
    groupClass: String,
    labelWidth: String,
    labelPosition: String,
    type: String,
    size: {
      type: String,
      default: 'small',
    },
    span: {
      type: Number,
      default: 24,
    },
    formProps: {
      type: Object,
      default: () => ({}),
    },
    params: Object,
  },
  data() {
    return {
      model: {},
      formModel: {},
      formList: [],
    };
  },
  computed: {
    slotKeys() {
      return Object.keys(this.$scopedSlots);
    },
  },
  watch: {
    value: {
      handler(val = {}) {
        this.model = val;
        this.setFormModel(val);
      },
      immediate: true,
    },
    list: {
      handler(val) {
        // 深度克隆传入的列表，避免原始值被修改
        const newList = cloneDeep(this.list);
        // 生成列表值的全路径key，即列表项为对象时，对象内的key与上一级的key合并作为全路径key
        const generateFullKey = (list, parentKey) => {
          list.forEach(item => {
            if (item.group && item.list) {
              if (item.group.key) {
                item.fullKey = `${parentKey ? `${parentKey}-${item.group.key}` : item.group.key}`;
              } else {
                item.fullKey = parentKey || item.key;
              }
              generateFullKey(item.list, item.fullKey);
            } else {
              item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`;
            }
          });
        };
        generateFullKey(newList);
        this.formList = newList;
      },
      immediate: true,
    },
  },
  methods: {
    handleInput(value) {
      console.log(value);
    },
    /**
     * @description 表单项值变化
     * @param {Object} item 表单项值对象
     */
    onItemChange(item) {
      this.$emit('input', { ...this.model, ...item });
    },
    /**
     * @description 表单相校验值变化
     * @param {Object} item 表单项校验值对象
     */
    onFormItemChange(item) {
      this.formModel = { ...this.formModel, ...item };
    },
    /**
     * @description 校验表单
     */
    validate(cb) {
      return new Promise(resolve => {
        this.$refs.form.validate(valid => {
          cb && cb(valid);
          this.$emit('validate', valid, this.model);
          return resolve(valid);
        });
      });
    },
    /**
     * @description 重置表单
     */
    reset() {
      this.$refs.form.clearValidate();
      this.$emit('reset');
    },
    /**
     * @description 根据表单值设置表单校验值
     * @param {Object} value 表单值
     */
    setFormModel(value) {
      let formModel = {};
      // 递归深度解析表单值，将表单值扁平化为一层的对象并设置为表单校验值对象
      const setFormModelValue = (list, parentKey) => {
        list.forEach(item => {
          item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`;
          if (item.value instanceof Object) {
            setFormModelValue(
              Object.keys(item.value).map(key => ({ key, value: item.value[key] })),
              item.fullKey,
            );
          } else {
            formModel[item.fullKey] = item.value;
          }
        });
      };
      setFormModelValue(Object.keys(value).map(key => ({ key, value: value[key] })));
      this.formModel = formModel;
    },
    /**
     * @description 手动更新某一表单项的值
     * @param {Object} param 需要更新的参数对象或者对象数组 { name => 表单项key,可嵌套; value => 更新的值 }
     * @example { name: 'a.b.c', value: 123 }
     * @example { name: 'd.0.e', value: ['f'] }
     */
    onItemUpdate(param) {
      this.$nextTick(() => {
        const newModel = cloneDeep(this.model);
        Object.entries(param).forEach(entry => {
          set(newModel, entry[0], entry[1]);
        });
        this.$emit('input', newModel);
      });
    },
  },
};
</script>
