index.vue 4.38 KB
<style>
.el-input .el-input__inner, .el-textarea__inner {
  border-radius: 0;
}
</style>

<style>
.eagle-form__flex-wrap {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}
.eagle-form__group-title {
  font-weight: bold;
  padding: 15px 5px;
  border-bottom: 1px solid #d9d9d9;
  margin-bottom: 30px;
}
.eagle-form__group-content {
  margin: 15px 0px;
}
</style>

<template>
  <el-form ref="form" size="mini" :class="formClass" :model="formModel" :label-width="labelWidth" :label-position="labelPosition || labelWidth ? 'right' : 'top'">
    {{ formModel }}
    <form-render :title-class="titleClass" :content-class="contentClass" :item-class="itemClass" :list="formList" :value="model"
      :model="model" :span="span" :type="type"
      @item-change="onItemChange" @form-item-change="onFormItemChange" @item-update="onItemUpdate"
    ></form-render>
    {{ list }}
  </el-form>
</template>

<script>
import FormRender from './form-render';
import { cloneDeep, set } from './util';

export default {
  name: 'FormNew',
  components: { FormRender },
  props: {
    value: Object,
    list: Array,
    formClass: String,
    titleClass: String,
    contentClass: String,
    itemClass: String,
    labelWidth: String,
    labelPosition: String,
    type: String,
    span: {
      type: Number,
      default: 24
    }
  },
  data() {
    return {
      model: {},
      formModel: {},
      formList: []
    }
  },
  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 = item.key;
              }
              generateFullKey(item.list, item.fullKey);
            } else {
              item.fullKey = `${parentKey ? `${parentKey}-${item.key}` : item.key}`;
            }
          });
        };
        generateFullKey(newList);
        this.formList = newList;
      },
      immediate: true
    }
  },
  methods: {
    /**
     * @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() {
      this.$refs.form.validate(valid => {
        this.$emit("validate", valid, this.model);
      });
    },
    /**
     * @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>