index.vue 4.19 KB
<template>
  <z-form ref="form" v-model="model" v-bind="schema.props" v-on="schema.props">
    <template v-for="(item, index) in schema.items">
      <template v-if="item.is">
        <z-form-item v-bind="keywordFilter(item)" :key="index">
          <slot
            v-if="$scopedSlots[item.prop]"
            :name="item.prop"
            :value="get(model, item.prop)"
            :onInput="value => onComponentInput({ value, item })"
            :props="{ value: get(model, item.prop) }"
            :listeners="{ input: value => onComponentInput({ value, item }) }"
            v-bind="slotProps"
          >
          </slot>
          <item-render v-else :item="item" :value="get(model, item.prop)" :onInput="value => onComponentInput({ value, item })"></item-render>
          <slot :name="`label-${item.prop}`" slot="label" v-bind="slotProps"></slot>
          <slot :name="`error-${item.prop}`" slot="error" v-bind="slotProps"></slot>
        </z-form-item>
      </template>
      <template v-else>
        <z-form-item v-bind="keywordFilter(item)" :key="index" :value="get(model, item.prop)">
          <slot
            :name="item.prop"
            :value="get(model, item.prop)"
            :onInput="value => onComponentInput({ value, item })"
            :props="{ value: get(model, item.prop) }"
            :listeners="{ input: value => onComponentInput({ value, item }) }"
            v-bind="slotProps"
          ></slot>
        </z-form-item>
      </template>
    </template>
    <slot name="footer" v-bind="slotProps"></slot>
  </z-form>
</template>

<script>
import { cloneDeep, get, set } from '../utils';

export default {
  name: 'SchemaForm',
  components: {
    ItemRender: {
      functional: true,
      render(h, context) {
        const props = context.props;
        const item = props.item || {};
        let content = [];
        if (item.render && typeof item.render === 'function') {
          content = context.props.render;
        }
        if (item.children) {
          if (Array.isArray(item.children)) {
            if (item.children.length > 0) {
              content = item.children.map(i => h('item-render', { props: { item: i } }));
            }
          } else {
            content = [item.children];
          }
        }
        let _props = item.props || {};
        if (props.value) {
          _props = { ..._props, value: props.value };
        }
        let _on = item.on || {};
        if (props.onInput) {
          _on = { ..._on, input: props.onInput };
        }
        return h(item.is, { attrs: item.attrs, props: _props, on: _on }, content);
      },
    },
  },
  props: {
    value: {
      type: Object,
      default() {
        return {};
      },
    },
    schema: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      model: this.value,
      originData: {},
    };
  },
  computed: {
    schemaProps() {
      return this.schema.props;
    },
    slotProps() {
      return {
        submit: this.onSubmit,
        cancel: this.onCancel,
        reset: this.onReset,
      };
    },
  },
  created() {
    const { originData, ...other } = this._data;
    this.originData = cloneDeep(other);
  },
  watch: {
    value(val = {}) {
      this.model = val;
    },
    model: {
      handler(val) {
        this.$emit('input', val);
      },
      deep: true,
    },
  },
  methods: {
    get,
    validate(callback) {
      return this.$refs.form.validate(callback);
    },
    validateField(props, callback) {
      return this.$refs.form.validateField(props, callback);
    },
    resetFields() {
      this.$refs.form.resetFields();
    },
    clearValidate(props) {
      return this.$refs.form.clearValidate(props);
    },
    keywordFilter(val = {}) {
      const { children, is, ...other } = val;
      return other;
    },
    onComponentInput({ value, item }) {
      set(this.model, item.prop, value);
    },
    onSubmit() {
      this.$refs.form.validate(valid => {
        if (valid) {
          this.$emit('submit', this.model);
        }
      });
    },
    onCancel() {
      this.$emit('cancel');
    },
    onReset() {
      this.model = cloneDeep(this.originData).model;
      this.$refs.form.resetFields();
      this.$emit('reset');
    },
  },
};
</script>