可配置化表单(Antd-UI)

112 阅读2分钟

开发背景是一个表单的展示字段是根据配置读取然后动态展示的,例如:

字段名称,字段类型,字段长度,组别,是否展示,验证规则等等内容

{
fields:[
{
    createable:true,//是否可新增
    defaultValue:'',//默认值,
    groupKey:'details',//组key
    labelNameCn:'',//中文展示
    labelNameEn:'',//英文展示
    length:255,//长度
    name:'type',//属性名称
    nillabel:''//是否必填
    picklistValues:[]//类型为‘PICKLIST’会有值
    referenceTo:[],//关联对象
    relationship:'',
    relationshipName:'',
    showWhenDetail:'',//详情时是否展示
    showWhenUpdate:'',//新增和编辑是否展示
    updateable:true,//是否可更新
    visible:true//是否展示
}
],
object:''//对应对象key
}

当新增,编辑时展示对应的弹窗,数据处理等。

先展示一下效果: 新增:

1697013729847.png 显示字段,是否必填,默认值等;

编辑:

1697013992491.png

具体实现

<Modal>
   <div className={styles['configModalBox']}>
        <FormComp
          config={config}
          formData={formData}
          form={form}
          currentType={currentType}
        />
      </div>
 </Modal>
//读取配置
...
let config = CompObj.TASK_DETAIL;
// 特殊处理一些字段
setConfig(config);
form.resetFields();
if(编辑){
 setCurrentType('edit');
 fetchTaskDetail({ id: showModal.id, componentId }).then(res => {
      setFormData(res.data);
 });
}
else{
  setCurrentType('add');
}
//FormComp

 //模板
  return (
    <div>
      <div className={styles['form-box']}>
        <Form form={form}>{useMemo(()=> <RenderForm/>,[infoList,formData])}</Form>
      </div>
    </div>
  );

//读取配置数据
useEffect(() => {
    if (!config) return;
    let arr = [];
    let data = (config.groups || []).map(group => {
      let children = (config.fields || []).filter(
        field =>
          field.groupKey == group.groupKey &&
          field.showWhenEdit &&
          field.visible,
      );
      arr.push(true);
      return {
        ...group,
        children: children,
      };
    });
    setInfoList(data);
  }, [config]);


//RenderForm 方法
//根据不同的状态渲染不同的组

  const RenderForm = (props) => {
    if(currentType=='edit'&&(!infoList|| !infoList.length||!formData || JSON.stringify(formData)=='{}')) return null;
    if(currentType=='add'&&(!infoList || !infoList.length)) return null;
    if (isInit) {
      setTimeout(() => {
        setIsInit(false);
      }, 800);
    }
    return infoList.map((vo, index) => {
      return (
        <div key={vo.groupKey} className={styles['form-container']}>
          {vo.children.length ? (
            <div className={styles['panel']}>
              {isCN ? vo.labelNameCn : vo.labelNameEn}
            </div>
          ) : null}
          {vo.children.map(item => {
            if (
              item.showWhenEdit &&
              item.visible &&
              (currentType == 'add' ? item.createable : item.updateable)
            ) {
              return <>{RenderFormItem(item, isInit)}</>;
            }
          })}
        </div>
      );
    });
  };
  
//RenderFormItem 渲染每个组的内容
 const RenderFormItem = (config, _isInit) => {
  let key = config.name;
  let val = '';
  //特殊字段类型处理 此处略
  const formProps = {
      key,
      label: isCN ? config.labelNameCn : config.labelNameEn,
      name: key,
    };
  if (!config.nillable) {
      formProps.rules = [
        {
          required: true,
        },
      ];
    }
    
   //特殊字段类型错误规则设置,此处略
   
   
   //开始每个字段的赋值
   if(currentType == 'add'){
   //以下处理下拉类型的默认值
       if (config.defaultValue && type != 'PICKLIST') {
              setFieldValue(key, config.defaultValue);
            } else if (type == 'PICKLIST') {
              // 下拉默认值
              const findIndex = (config.picklistValues || []).findIndex(
                ele => ele.defaultValue == true,
              );
              if (findIndex != -1) {
                const defaultValue = config.picklistValues[findIndex].value;
                setFieldValue(key, defaultValue);
       }
   
}else{
  // 编辑初始赋值
      if (!formData) return;
      val = _isInit ? _.get(formData, key) : form.getFieldValue(key);
      if (type == 'DATE' || type == 'TIMESTAMP') {
        const time = transLocalTime(val);
        val = val ? moment(time) : null;
      }
      setFieldValue(key, val);
}
//以下就根据字段类型渲染不同的表单字段,下面展示两种类型示例
switch (type) {
      case 'STRING': {
        return (
          <FormItem {...formProps}>
            <Input
              placeholder={t('form:inputPlaceholder')}
              maxLength={config.length}
              disabled={disabledStatus}
            />
          </FormItem>
        );
      }
   case 'PICKLIST': {
        return (
          <FormItem {...formProps}>
            <Select
              allowClear
              placeholder={t('form:selectPlaceholder')}
              disabled={disabledStatus}
              onChange={(val, option) => onChange(val, option, config)}
            >
              {config.picklistValues.map(one => (
                <Option key={one.value} value={one.value}>
                  {isCN ? one.labelNameCn : one.labelNameEn}
                </Option>
              ))}
            </Select>
          </FormItem>
        );
      }
      default:
        {
          console.log('default-formProps:', config, formProps, type);
        }
        break;
    }

}

大概流程就是如此,在提交数据时直接获取form.validateFields()异步获取表单数据,再对应处理特殊的字段即可。 详情展示也是类似的逻辑,只是对应的值直接渲染即可,不用去渲染表单。