动态表单 DynamicView
DynamicView 动态表单
在某些特殊场景下,视图容器不能被枚举,需要动态的被加载。例如合同模板生成场景,需要数据中勾选了特定模块,才会出现特定的数据容器。
这种场景下可使用动态表单 DynamicView 布局,此布局会在页面渲染时通过指定的 LogicFlow/LogicFunction 加载动态的 DSL 及元数据,将子元素挂在到该布局下。
动态表单返回的 DSL 中的容器均是通过 lookupFrom 与静态的容器关联。从而实现数据回填及连带提交。
DynamicView API
IDynamicViewProps
| 参数 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| logicFunc | string | 获取动态表单DSL的func key | - |
| logicFunction | string | 获取动态表单DSL的func key,等价于logicFunc | - |
| logicFlow | string | 获取动态表单DSL的flow key | - |
| dataParams | string | 获取动态表单 func/flow 的入参。在页面打开时解析,不会收集依赖。可以不需要表达式格式#{expression}。一般可以用来接收pageRecord。例如 dataParams=”{ id: pageRecord.id }“ | - |
| dynamicLookupFrom | string | 动态表单内的容器 lookupFrom 关联的父容器key,目的是提交时父容器可以带上动态表单内的数据 | - |
| show | boolean | 是否显示, 表达式只在页面打开执行一次,且只能读取env, pageRecord或者固定值 | - |
主要开发步骤:
- 写含动态视图的XML
- 主容器模型继承 DynamicView, 用于接收或回显动态数据
- 定义接口响应动态视图模型数据
动态视图XML
<View type="Form" forModel="item_MyUser" title="用户编辑" >
<Form key="user" model="item_MyUser" dataCondition="id = ?" dataParams="[3]"> <Fields> <Field name="name"/> <Field name="age"/> </Fields>
<Actions> <Action type="Submit" label="提交" after="Refresh" layout="Footer" logicFunction="item_DynamicSubmitFunc"/> </Actions> </Form>
<!-- 加载动态表单生成dsl的元数据 logicFlow 或者 logicFunc 二选一 dynamicLookupFrom 取主容器key dataParams 可选,可传入pageRecord(上一个页面传过来的上下文),或者env dataParams 用法和其他数据容器类似,不需要写成表达式格式,会作为后端func/flow的入参 --> <DynamicView logicFunc="item_DynamicViewFunc" dynamicLookupFrom="user" dataParams="{id: pageRecord.id}" />
</View>主容器模型继承 DynamicView
主容器需继承 DynamicModel , 这样会拥有动态数据 默认提交和回显 能力。
@Data@Model(name = "用户", mainField = "name")@EqualsAndHashCode(callSuper = true)// 需要继承 DynamicModel, 拥有 dynamicFields 字段, 用于提交和回显数据public class MyUser extends DynamicModel<Long> {
@Field(name = "名称") private String name;
@Field(name = "年龄") private Integer age;
}定义接口响应动态视图模型数据
需要先简单介绍下动态模型数据,以下为他们与 dsl 的对应关系。
| 模型 | 对应XML | 作用 |
|---|---|---|
| DynamicView | 数据容器,如<Form/>/<TableForm/> | 对数据容器的抽象,可以封装 <Fields/> |
| DynamicViewGroup | 对应 <GroupField/> | 对 <GroupField/> 的抽象,不需要时使用默认组便可 |
| DynamicViewField | 对应到 <Field/> | 对 <Field/> 的抽象,一般要定义视图项的名称和类型 |
使用常规逻辑流或逻辑函数便可, 参数看 标签上的 dataParams ,可空, 响应值必须为 List<DynamicView>,表示包含多个数据容器。
@FunctionImplpublic class DynamicViewFuncImpl implements DynamicViewFunc {
/** * 响应动态模型描述数据, list 表示包含多个容器 */ @Override public List<DynamicView> execute() { List<DynamicView> list = new ArrayList<>(); // 添加一个 Form 数据容器 DynamicView form = generateForm(); list.add(form); // 添加一个 TableForm 数据容器 DynamicView tableForm = generateTableForm(); list.add(tableForm); return list; }
/** * 响应 Form */ @NotNull private DynamicView generateForm() { DynamicView form = new DynamicView(); form.setTitle("学生"); form.setModel("student"); form.setDataContainer(DataContainer.Form);
DynamicViewGroup g1 = new DynamicViewGroup(); g1.setTitle("小分组"); g1.setSingleColumn(false); form.setGroups(Collections.singletonList(g1));
// 字符串类型 DynamicViewField f1 = new DynamicViewField(); f1.setName("name"); f1.setLabel(名称); f1.setType(FieldType.Text); f1.setTypeMeta(TextType.DEFAULT.get());
// 数字类型 DynamicViewField f2 = new DynamicViewField(); f2.setName("age"); f2.setLabel("年龄"); f2.setType(FieldType.Int); f2.setTypeMeta(IntType.DEFAULT.get());
g1.setFields(Arrays.asList(f1, f2)); return form; }
/** * 响应 TableForm */ @NotNull private DynamicView generateTableForm() { DynamicView tableForm = new DynamicView(); tableForm.setTitle("班级"); tableForm.setModel("class"); tableForm.setDataContainer(DataContainer.TableForm); DynamicViewGroup defaultGroup = new DynamicViewGroup(); tableForm.setDefaultGroup(defaultGroup); DynamicViewField f1 = generateField("name", "名称", 0); DynamicViewField f2 = generateField("teacher", "老师", 1); defaultGroup.setFields(Arrays.asList(f1, f2)); return tableForm; }
@NotNull private DynamicViewField generateField(String name, String label, int order) { DynamicViewField dynamicViewField = new DynamicViewField(); dynamicViewField.setName(name); dynamicViewField.setLabel(label); dynamicViewField.setType(FieldType.Text); dynamicViewField.setTypeMeta(TextType.DEFAULT.get()); dynamicViewField.setOrder(order); return dynamicViewField; }
}以上实现便能渲染出动态模型页面了, 与常规页面区别不大。
数据提交和回显
动态数据提交
我们可以通过以下方式对将要 保存到数据库的动态数据 进行预处理。
@FunctionImplpublic class DynamicViewSumitFuncImpl implements DynamicViewSumitFunc { @Override public MyUser execute(MyUser myUser) { Map<String, Object> dynamicFields = myUser.getDynamicFields(); return myUser; }}动态数据回显
我们也可以使用 postFunc 的方式对将要 回显的动态数据 进行后置处理。
@FunctionImplpublic class DynamicViewLoadPostFuncImpl implements DynamicViewLoadPostFunc { @Override public MyUser execute(MyUser myUser) {
Map<String, Object> dynamicFields = myUser.getDynamicFields(); // do something..
return myUser; }}一些特殊动态项的定义
图片类型
DynamicViewField dynamicViewField = new DynamicViewField();dynamicViewField.setName("picture");dynamicViewField.setLabel("图片");dynamicViewField.setType(FieldType.Image);ImageType imageType = ImageType.DEFAULT.get();
// 如果需要调整格式的话,可以设置下imageType.setAllowedTypes(Lists .newArrayList(AllowedTypes.getTypes(AllowedTypes.IMAGE)));dynamicViewField.setTypeMeta(imageType);return dynamicViewField;枚举类型
- 定义枚举类型
DynamicViewField f5 = new DynamicViewField();f5.setName("type");f5.setLabel("类型");f5.setType(FieldType.MultiDictionary);DictionaryType dictionaryTypeMeta = new DictionaryType("custom_dict", DictType.String);dictionaryTypeMeta.setOptions(Arrays.asList( new DictItem("a", "第一个字母"), new DictItem("b", "第二个字母"), new DictItem("c", "第三个字母")));f5.setTypeMeta(dictionaryTypeMeta);- 使用对应元数据的枚举类型, 这样子表单项可以省略,其值从 ms 拿
DynamicViewField f5 = new DynamicViewField();f5.setName("type");f5.setLabel("类型");f5.setType(FieldType.MultiDictionary);DictionaryType dictionaryTypeMeta = new DictionaryType("base_Unit", DictType.String);f5.setTypeMeta(dictionaryTypeMeta);所有类型参考
| 类型 | 含义 |
|---|---|
| TextType | 文本类型 |
| NumberType | 数字类型 |
| IntType | 整数类型 |
| FloatType | 小数类型 |
| CurrencyType | 金钱类型 |
| PercentType | 百分比类型 |
| TimeType | 时间类型 |
| DateTimeType | 日期时间类型 |
| BooleanType | 布尔类型 |
| ImageType | 图片类型 |
| AttachmentType | 附件类型 |
| JsonType | json类型 |
| DictionaryType | 字典类型 |
| EnumType | 枚举类型 |
| AddressType | 地址类型 |