开发背景是一个表单的展示字段是根据配置读取然后动态展示的,例如:
字段名称,字段类型,字段长度,组别,是否展示,验证规则等等内容
{
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
}
当新增,编辑时展示对应的弹窗,数据处理等。
先展示一下效果: 新增:
编辑:
具体实现
<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()异步获取表单数据,再对应处理特殊的字段即可。 详情展示也是类似的逻辑,只是对应的值直接渲染即可,不用去渲染表单。