表单 schema 配置化

发布于:2025-03-16 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、前沿

基于 Ant Design Vue 组件库实现了表单的配置化生成,通过 schema 配置化的方式实现表单的动态渲染、数据绑定和更新等功能,而提交按钮及获取数据逻辑由使用方自行提供。通过 schema 对象来定义表单的结构和属性,modelData 对象存储表单数据。主要组件包括 z-form-generatorformGeneratorGroup 以及各种具体的表单字段组件(如 field-datefield-radio 等)。

二、具体实现

1. schema 配置
  • 结构定义schema 对象包含 containers(表单结构的 JSON 数据)、colNum(最多展示列数)和 isViewType(表单是否为只读)等属性。containers 中可以包含多个组件配置,每个组件配置又有 typelabelmodel 等属性。
schema: {
  containers: [],
  colNum: 2,
  isViewType: false
}
  • 字段配置:不同类型的字段组件(如日期、单选、多选等)根据 schema 中的 type 属性来确定使用哪个具体的组件进行渲染。例如,type: 'date' 会使用 field-date 组件。
2. 组件渲染
  • formGeneratorGroup 组件:根据 schema 中的 type 属性动态渲染不同的表单字段组件。通过 getFieldType 方法 type 获取组件名称,并使用 JSX 进行渲染。
// formGeneratorGroup.js
const { getFieldType, field, colNum } = this
const comArr = ['area', 'checkbox', 'date', 'file', 'input', 'radio', 'search', 'select', 'upload', 'modal', 'cascader', 'list']
let isCom = _.includes(comArr, field.type)
const component = isCom ? getFieldType(field) : null
const { type, ...restProps } = field
return (
  <a-col>
    {
      component && <component
        schema={restProps}
        modelData={this.modelData}
        isViewType={this.isViewType}
        on-model-updated={this.onModelUpdated}
        on-error-updated={this.onErrorUpdated}
        on-schema-updated={this.onSchemaUpdated}
      />
    }
  </a-col>
)
  • 具体字段组件:每个字段组件(如 field-datefield-radio 等)根据 schema 中的属性进行渲染,并处理相应的事件(如 onChange)。
  getFieldType ({ type } = {}) {
    if (!type) {
      throw new Error('请确认组件类型')
    }
    return `Field-${type}`
  },
3. 组件化设计

采用组件化的设计思想,将不同类型的表单组件封装成独立的组件,如 FieldRadioFieldCheckboxFieldFile 等。每个组件都接收 schemamodelData 作为 props,通过 render 函数动态渲染表单元素。

// field-radio.vue
export default {
  name: 'FieldRadio',
  props: {
    schema: {
      type: Object,
      default: () => ({}),
      require: true,
    },
    modelData: {
      type: Object,
      default: () => ({}),
    },
  },
  methods: {
    changeHandle (e) {
      this.value = e.target.value
    },
  },
  render () {
    const { label, model, list, optionName, ...restSchema } = this.schema
    const { keyName: key, valueName: val } = {
      keyName: 'key',
      valueName: 'val',
      ...optionName,
    }
    return <a-form-model-item
      label={label}
      prop={model}
    >
      <a-radio-group
        value={this.value}
        prop={{ ...restSchema }}
        onChange={this.changeHandle}
      >
        <a-row>
          {
            list && list.map(el =>
              <a-col span={12}>
                <a-radio value={el[key]}>
                  {el[val]}
                </a-radio>
              </a-col>
            )
          }
        </a-row>
      </a-radio-group>
    </a-form-model-item>
  },
}
4. 数据绑定与更新

通过 modelData 对象实现表单数据的绑定。在组件中,通过 props 属性将 model 绑定到表单元素上,然后通过 $emit 触发自定义事件,将数据更新传递给父组件。

// field-radio.vue
methods: {
  changeHandle (e) {
    this.value = e.target.value
    this.$emit('model-updated', this.schema, this.value)
  },
}
5. 校验规则初始化

通过 initRules 方法初始化表单的校验规则。根据 schema 中的 required 属性和组件类型,为每个字段添加相应的校验规则。

// formGenerator.vue
initRules () {
  const { containers, rules } = this.cloneSchema
  // 初始化规则校验对象
  let initRules = { ...rules }
  // 平铺所有容器的fields
  const components = containers.reduce((acc, container) => {
    acc.push(...container.components);
    return acc;
  }, []);
  // 统一处理字段规则
  components.forEach(field => {
    const { code, type, required, inputType, regular, errorMessage, precision } = field;

    // 初始化当前字段的校验规则
    if (!initRules[code]) initRules[code] = [];
    // 兼容 required 历史数据
    if (required) {
      const requiredRule = type === 'list'
        ? { required: true, message: '该列表不得为空,请为列表新增数据' }
        : { required: true, message: '该项不得为空', trigger: type === 'date' ? 'change' : 'blur' };
      initRules[code].unshift(requiredRule);
    }

	// 如果是input类型组件,处理其校验规则
    if (type === 'input') {
      let ruleItem = initRules[code];

      // 处理内建的输入框类型校验
      if (inputType && ['phone', 'intNum', 'number', 'email', 'idCardNo', 'bussNo', 'bankNo'].includes(inputType)) {
		const { reg, message } = inputType === 'number' ? RulesReg[inputType][idx] : RulesReg[inputType]
        addValidationRule.call(this, ruleItem, reg, message)
      }

      // 处理自定义正则校验
      if (regular) {
        const reg = new RegExp(regular);
        const message = errorMessage;
        addValidationRule.call(this, ruleItem, reg, message)
      }

      initRules[code] = [...ruleItem];
    }
  })
 
  // 更新规则
  this.rules = initRules;
}

addValidationRule(ruleItem, reg, message) {
  ruleItem.push({
    validator: (rule, value, cb) => this.valitaRule(reg, value, cb, message),
    trigger: 'blur',
  });
}

三、特点

  1. 配置化设计:通过 schema 配置化的方式,实现了表单的动态生成和定制,提高了代码的可维护性和可扩展性。
  2. 组件化开发:将不同类型的表单组件封装成独立的组件(如 field-datefield-radio 等),提高了代码的复用性和可测试性。
  3. 动态渲染:根据 schema 中的 type 属性动态渲染不同的表单组件,实现了表单的灵活定制。
  4. 规则校验:支持多种类型的规则校验,包括必填项校验、输入框校验等,确保了用户输入数据的合法性。

网站公告

今日签到

点亮在社区的每一天
去签到