From a76685bbd6402742cbac88907158fde75e192329 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Thu, 16 Jan 2025 10:46:02 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0formily=20json=20schema?= =?UTF-8?q?=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../build/JenkinsBaseBuildVariables.java | 235 ++++++----- .../formily/FormilyComponentProps.java | 94 ++++- .../formily/FormilyEditorProps.java | 47 ++- .../annotation/formily/FormilyField.java | 112 ++++-- .../annotation/formily/FormilyForm.java | 37 +- .../annotation/formily/FormilyReaction.java | 64 ++- .../formily/FormilySchemaFactory.java | 379 ++++++++++-------- .../annotation/formily/FormilyValidator.java | 67 +++- .../formily/FormilySchemaFactoryTest.java | 308 ++++++-------- 9 files changed, 819 insertions(+), 524 deletions(-) diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/variables/build/JenkinsBaseBuildVariables.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/variables/build/JenkinsBaseBuildVariables.java index 2470c839..0210c4a3 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/variables/build/JenkinsBaseBuildVariables.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/variables/build/JenkinsBaseBuildVariables.java @@ -3,6 +3,8 @@ package com.qqchen.deploy.backend.deploy.dto.variables.build; import com.qqchen.deploy.backend.framework.annotation.formily.*; import lombok.Data; +import java.util.Map; + /** * Jenkins构建变量 */ @@ -10,105 +12,136 @@ import lombok.Data; @FormilyForm(name = "Jenkins构建配置") public class JenkinsBaseBuildVariables { - @FormilyField( - title = "绑定三方Jenkins系统", - type = "string", - component = "Select", - props = @FormilyComponentProps( - api = "/api/v1/external-system/list?type=JENKINS", - labelField = "name", - valueField = "id", - showSearch = true, - allowClear = true, - placeholder = "请选择Jenkins系统" - ), - validators = { - @FormilyValidator( - required = true, - message = "请选择Jenkins系统" - ) - } - ) - private String externalSystemId; - - @FormilyField( - title = "绑定Jenkins视图", - type = "string", - component = "Select", - props = @FormilyComponentProps( - labelField = "viewName", - valueField = "id", - showSearch = true, - allowClear = true, - placeholder = "请选择Jenkins视图" - ), - validators = { - @FormilyValidator( - required = true, - message = "请选择Jenkins视图" - ) - }, - reactions = { - @FormilyReaction( - dependencies = {"externalSystemId"}, - state = "dataSource", - value = "{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.externalSystemId).then(data => data.data)}}" - ) - } - ) - private String viewId; - - @FormilyField( - title = "绑定Jenkins任务", - type = "string", - component = "Select", - props = @FormilyComponentProps( - labelField = "jobName", - valueField = "id", - showSearch = true, - allowClear = true, - placeholder = "请选择Jenkins任务" - ), - validators = { - @FormilyValidator( - required = true, - message = "请选择Jenkins任务" - ) - }, - reactions = { - @FormilyReaction( - dependencies = {"externalSystemId", "viewId"}, - state = "dataSource", - value = "{{$fetch('/api/v1/jenkins-job/list?externalSystemId=' + $deps.externalSystemId + '&viewId=' + $deps.viewId).then(data => data.data)}}" - ) - } - ) - private String jobId; - - @FormilyField( - title = "Pipeline script", - type = "string", - component = "MonacoEditor", - props = @FormilyComponentProps( - editor = @FormilyEditorProps( - language = "groovy", - theme = "vs-dark", - minimap = false, - lineNumbers = true, - wordWrap = true, - fontSize = 14, - tabSize = 4, - automaticLayout = true, - folding = true, - placeholder = "请输入Pipeline脚本" - ) - ), - validators = { - @FormilyValidator( - required = true, - message = "请输入Pipeline脚本" - ) - } - ) - private String script; +// @FormilyField( +// title = "绑定三方Jenkins系统", +// component = "Select", +// order = 1, +// props = @FormilyComponentProps( +// labelField = "name", +// valueField = "id", +// allowClear = true, +// showSearch = true, +// placeholder = "请选择Jenkins系统" +// ), +// validators = { +// @FormilyValidator( +// required = true, +// message = "请选择Jenkins系统" +// ) +// }, +// reactions = { +// @FormilyReaction( +// state = "dataSource", +// value = "{{$fetch('/api/v1/external-system/list?type=JENKINS').then(data => data.data)}}", +// when = "{{$form.mounted && $form.values.__modalVisible}}" +// ) +// } +// ) +// private String externalSystemId; +// +// @FormilyField( +// title = "绑定Jenkins视图", +// component = "Select", +// order = 2, +// props = @FormilyComponentProps( +// labelField = "viewName", +// valueField = "id", +// allowClear = true, +// showSearch = true, +// placeholder = "请选择Jenkins视图" +// ), +// validators = { +// @FormilyValidator( +// required = true, +// message = "请选择Jenkins视图" +// ) +// }, +// reactions = { +// @FormilyReaction( +// dependencies = {"externalSystemId"}, +// state = "dataSource", +// value = "{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.externalSystemId).then(data => data.data)}}" +// ) +// } +// ) +// private String viewId; +// +// @FormilyField( +// title = "绑定Jenkins任务", +// component = "Select", +// order = 3, +// props = @FormilyComponentProps( +// labelField = "jobName", +// valueField = "id", +// allowClear = true, +// showSearch = true, +// placeholder = "请选择Jenkins任务" +// ), +// validators = { +// @FormilyValidator( +// required = true, +// message = "请选择Jenkins任务" +// ) +// }, +// reactions = { +// @FormilyReaction( +// dependencies = {"externalSystemId", "viewId"}, +// state = "dataSource", +// value = "{{$fetch('/api/v1/jenkins-job/list?externalSystemId=' + $deps.externalSystemId + '&viewId=' + $deps.viewId).then(data => data.data)}}" +// ) +// } +// ) +// private String jobId; +// +// @FormilyField( +// title = "环境变量", +// type = "map", +// order = 4, +// mapConfig = @FormilyMapConfig( +// keyTitle = "变量名", +// valueTitle = "变量值", +// keyComponent = "Select", +// valueComponent = "Input", +// keyProps = @FormilyComponentProps( +//// api = "/api/v1/env-vars/keys", +// showSearch = true, +// allowClear = true, +// placeholder = "请选择或输入变量名" +// ), +// valueProps = @FormilyComponentProps( +// placeholder = "请输入变量值" +// ), +// addText = "添加环境变量", +// allowCustomKey = true +// ) +// ) +// private Map envVars; +// +// @FormilyField( +// title = "Pipeline script", +// type = "string", +// component = "MonacoEditor", +// order = 5, +// props = @FormilyComponentProps( +// editor = @FormilyEditorProps( +// language = "groovy", +// theme = "vs-dark", +// minimap = false, +// lineNumbers = true, +// wordWrap = true, +// fontSize = 14, +// tabSize = 4, +// automaticLayout = true, +// folding = true, +// placeholder = "请输入Pipeline脚本" +// ) +// ), +// validators = { +// @FormilyValidator( +// required = true, +// message = "请输入Pipeline脚本" +// ) +// } +// ) +// private String script; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyComponentProps.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyComponentProps.java index dff47934..63e5b11e 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyComponentProps.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyComponentProps.java @@ -2,22 +2,86 @@ package com.qqchen.deploy.backend.framework.annotation.formily; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; -@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface FormilyComponentProps { - String api() default ""; // 数据源API - String labelField() default "label"; // 标签字段 - String valueField() default "value"; // 值字段 - boolean allowClear() default false; // 允许清除 - boolean showSearch() default false; // 显示搜索 - String placeholder() default ""; // 占位符 - String mode() default ""; // 模式(single/multiple等) - String[] enum_() default {}; // 枚举值 - String[] enumNames() default {}; // 枚举显示值 - - // 编辑器属性 - FormilyEditorProps editor() default @FormilyEditorProps; // 编辑器配置 + /** + * 占位符 + */ + String placeholder() default ""; + + /** + * 是否允许清除 + */ + boolean allowClear() default false; + + /** + * 是否允许搜索 + */ + boolean showSearch() default false; + + /** + * 选择模式 (single/multiple) + */ + String mode() default "single"; + + /** + * 是否启用过滤 + */ + boolean filterOption() default true; + + /** + * 标签字段名 + */ + String labelField() default "label"; + + /** + * 值字段名 + */ + String valueField() default "value"; + + /** + * 防抖时间(ms) + */ + int debounceTime() default 300; + + /** + * 枚举值 + */ + String[] enum_() default {}; + + /** + * 枚举标签 + */ + String[] enumNames() default {}; + + /** + * 编辑器配置 + */ + FormilyEditorProps editor() default @FormilyEditorProps; + + /** + * 是否加载中 + */ + boolean loading() default false; + + /** + * 是否禁用 + */ + boolean disabled() default false; + + /** + * 最大长度 + */ + int maxLength() default -1; + + /** + * 最小长度 + */ + int minLength() default -1; + + /** + * 正则表达式 + */ + String pattern() default ""; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyEditorProps.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyEditorProps.java index f2219ba2..a96481b6 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyEditorProps.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyEditorProps.java @@ -1,25 +1,22 @@ package com.qqchen.deploy.backend.framework.annotation.formily; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -@Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface FormilyEditorProps { /** - * 编辑器语言 + * 编程语言 */ - String language() default "plaintext"; + String language() default "javascript"; /** - * 编辑器主题 + * 主题 */ String theme() default "vs"; /** - * 是否启用小地图 + * 是否显示小地图 */ boolean minimap() default true; @@ -31,7 +28,7 @@ public @interface FormilyEditorProps { /** * 是否自动换行 */ - boolean wordWrap() default false; + String wordWrap() default "off"; /** * 字体大小 @@ -54,7 +51,37 @@ public @interface FormilyEditorProps { boolean folding() default true; /** - * 占位符文本 + * 是否允许滚动到最后一行之后 */ - String placeholder() default ""; + boolean scrollBeyondLastLine() default false; + + /** + * 是否启用建议 + */ + boolean suggestOnTriggerCharacters() default true; + + /** + * 是否启用代码片段建议 + */ + boolean snippetsPreventQuickSuggestions() default false; + + /** + * 垂直滚动条 + */ + String scrollbarVertical() default "auto"; + + /** + * 水平滚动条 + */ + String scrollbarHorizontal() default "auto"; + + /** + * 防抖时间(ms) + */ + int debounceTime() default 300; + + /** + * 是否只读 + */ + boolean readOnly() default false; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyField.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyField.java index 1acfe13c..083ed2ef 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyField.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyField.java @@ -8,29 +8,93 @@ import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FormilyField { - // 基础属性 - String title(); // 字段标题 - String description() default ""; // 字段描述 - String type() default "string"; // 字段类型(string/number/boolean/array/object/map) - int order() default Integer.MAX_VALUE; // 字段顺序,值越小越靠前 - - // 组件属性 - String component() default "Input"; // 组件类型 - String decorator() default "FormItem"; // 装饰器类型 - FormilyComponentProps props() default @FormilyComponentProps; // 组件属性 - FormilyDecoratorProps decoratorProps() default @FormilyDecoratorProps; // 装饰器属性 - - // 校验规则 - FormilyValidator[] validators() default {}; // 校验规则 - - // 联动配置 - FormilyReaction[] reactions() default {}; // 联动规则 - - // 展示控制 - String pattern() default "editable"; // 展示模式(editable/disabled/readOnly) - String display() default "visible"; // 显示模式(visible/hidden/none) - boolean hidden() default false; // 是否隐藏 + /** + * 字段标题 + */ + String title(); - // Map类型配置 - FormilyMapConfig mapConfig() default @FormilyMapConfig; // Map配置 + /** + * 字段描述 + */ + String description() default ""; + + /** + * 字段类型 + */ + String type() default "string"; + + /** + * 默认值 + */ + String defaultValue() default ""; + + /** + * 组件类型 + */ + String component() default "Input"; + + /** + * 装饰器类型 + */ + String decorator() default "FormItem"; + + /** + * 字段顺序 + */ + int order() default 0; + + /** + * 组件属性配置 + */ + FormilyComponentProps props() default @FormilyComponentProps; + + /** + * 装饰器属性配置 + */ + FormilyDecoratorProps decoratorProps() default @FormilyDecoratorProps; + + /** + * 验证规则 + */ + FormilyValidator[] validators() default {}; + + /** + * 联动配置 + */ + FormilyReaction[] reactions() default {}; + + /** + * Map类型配置 + */ + FormilyMapConfig mapConfig() default @FormilyMapConfig; + + /** + * 编辑模式 + */ + String pattern() default "editable"; + + /** + * 展示模式 + */ + String display() default "visible"; + + /** + * 是否隐藏 + */ + boolean hidden() default false; + + /** + * 提示信息 + */ + String tooltip() default ""; + + /** + * 是否必填 + */ + boolean required() default false; + + /** + * 栅格跨度 + */ + int gridSpan() default 1; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyForm.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyForm.java index 6b47ceaf..18927902 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyForm.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyForm.java @@ -1,13 +1,38 @@ package com.qqchen.deploy.backend.framework.annotation.formily; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) +@Documented public @interface FormilyForm { - String name(); // 表单名称 - String description() default ""; // 表单描述 + /** + * 表单名称 + */ + String name(); + + /** + * 表单描述 + */ + String description() default ""; + + /** + * Schema版本号 + */ + String version() default "2.0"; + + /** + * 是否在遇到第一个错误时立即停止验证 + */ + boolean validateFirst() default true; + + /** + * 标签列宽度 + */ + int labelCol() default 6; + + /** + * 内容列宽度 + */ + int wrapperCol() default 18; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyReaction.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyReaction.java index d972f299..847965ae 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyReaction.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyReaction.java @@ -2,12 +2,66 @@ package com.qqchen.deploy.backend.framework.annotation.formily; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -@Target({}) @Retention(RetentionPolicy.RUNTIME) public @interface FormilyReaction { - String[] dependencies() default {}; // 依赖字段 - String state() default ""; // 状态表达式,如 dataSource - String value() default ""; // 状态值,如 {{$fetch('/api/options/B?a=' + $deps.a).then(data => data.data)}} + /** + * 依赖字段 + */ + String[] dependencies() default {}; + + /** + * 触发条件 + */ + String when() default ""; + + /** + * 目标状态 + */ + String state() default ""; + + /** + * 目标值 + */ + String value() default ""; + + /** + * 执行表达式 + */ + String run() default ""; + + /** + * 是否立即执行 + */ + boolean immediate() default false; + + /** + * 是否在表单挂载时执行 + */ + boolean runOnMounted() default false; + + /** + * 防抖时间(ms) + */ + int debounceTime() default 100; + + /** + * 是否显示加载状态 + */ + boolean showLoading() default true; + + /** + * 加载状态文本 + */ + String loadingText() default "加载中..."; + + /** + * 错误处理 + */ + String onError() default ""; + + /** + * 成功处理 + */ + String onSuccess() default ""; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactory.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactory.java index 3ba74f08..084e3ea4 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactory.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactory.java @@ -38,12 +38,22 @@ public class FormilySchemaFactory { FormilyForm formDef = formClass.getAnnotation(FormilyForm.class); ObjectNode schema = objectMapper.createObjectNode(); + + // 基础属性 schema.put("type", "object"); + schema.put("version", formDef.version()); schema.put("name", formDef.name()); + schema.put("validateFirst", formDef.validateFirst()); + if (!formDef.description().isEmpty()) { schema.put("description", formDef.description()); } + // 表单布局配置 + ObjectNode formProps = schema.putObject("x-props"); + formProps.put("labelCol", formDef.labelCol()); + formProps.put("wrapperCol", formDef.wrapperCol()); + ObjectNode properties = schema.putObject("properties"); boolean hasFields = false; @@ -70,41 +80,6 @@ public class FormilySchemaFactory { return schema; } - /** - * 获取类的所有字段,包括父类的字段 - * @param clazz 要获取字段的类 - * @return 所有字段的列表 - */ - private static List getAllFields(Class clazz) { - List fields = new ArrayList<>(); - Class currentClass = clazz; - - while (currentClass != null && currentClass != Object.class) { - fields.addAll(Arrays.asList(currentClass.getDeclaredFields())); - currentClass = currentClass.getSuperclass(); - } - - return fields; - } - - /** - * 查找最近的带有FormilyForm注解的类 - * @param clazz 要查找的类 - * @return 带有FormilyForm注解的最近的类,如果没有找到则返回null - */ - private static Class findNearestFormClass(Class clazz) { - Class currentClass = clazz; - - while (currentClass != null && currentClass != Object.class) { - if (currentClass.isAnnotationPresent(FormilyForm.class)) { - return currentClass; - } - currentClass = currentClass.getSuperclass(); - } - - return null; - } - private static JsonNode generateFieldSchema(FormilyField field) { ObjectNode fieldSchema = objectMapper.createObjectNode(); @@ -114,78 +89,42 @@ public class FormilySchemaFactory { if (!field.description().isEmpty()) { fieldSchema.put("description", field.description()); } + if (!field.defaultValue().isEmpty()) { + fieldSchema.put("default", field.defaultValue()); + } + if (!field.tooltip().isEmpty()) { + fieldSchema.put("tooltip", field.tooltip()); + } // 组件属性 fieldSchema.put("x-decorator", field.decorator()); + fieldSchema.put("x-component", field.component()); + + // 装饰器属性 + ObjectNode decoratorProps = generateDecoratorProps(field); + if (decoratorProps.size() > 0) { + fieldSchema.set("x-decorator-props", decoratorProps); + } + + // 组件属性 if ("map".equals(field.type())) { - // 对于Map类型,使用ArrayItems组件 - fieldSchema.put("x-component", "ArrayItems"); - - // 生成Map配置 - ObjectNode componentProps = generateMapComponentProps(field.mapConfig()); - if (componentProps.size() > 0) { - fieldSchema.set("x-component-props", componentProps); - } - - // 生成Map项的Schema - ObjectNode items = fieldSchema.putObject("items"); - items.put("type", "object"); - items.put("x-component", "ArrayItems.Item"); - - // 生成Map项的属性Schema - ObjectNode properties = items.putObject("properties"); - - // 键的Schema - ObjectNode keySchema = properties.putObject("key"); - keySchema.put("type", "string"); - keySchema.put("title", field.mapConfig().keyTitle()); - keySchema.put("x-decorator", "FormItem"); - keySchema.put("x-component", field.mapConfig().keyComponent()); - ObjectNode keyProps = generateComponentProps(field.mapConfig().keyProps(), field.mapConfig().keyComponent()); - if (keyProps.size() > 0) { - keySchema.set("x-component-props", keyProps); - } - - // 值的Schema - ObjectNode valueSchema = properties.putObject("value"); - valueSchema.put("type", "string"); - valueSchema.put("title", field.mapConfig().valueTitle()); - valueSchema.put("x-decorator", "FormItem"); - valueSchema.put("x-component", field.mapConfig().valueComponent()); - ObjectNode valueProps = generateComponentProps(field.mapConfig().valueProps(), field.mapConfig().valueComponent()); - if (valueProps.size() > 0) { - valueSchema.set("x-component-props", valueProps); - } - - // 操作按钮 - ObjectNode operationSchema = properties.putObject("operations"); - operationSchema.put("type", "void"); - operationSchema.put("x-component", "ArrayItems.Remove"); - operationSchema.put("x-component-props", field.mapConfig().removeText()); - - // 添加按钮 - ObjectNode addition = fieldSchema.putObject("properties").putObject("addition"); - addition.put("type", "void"); - addition.put("title", field.mapConfig().addText()); - addition.put("x-component", "ArrayItems.Addition"); + generateMapSchema(fieldSchema, field.mapConfig()); } else { - fieldSchema.put("x-component", field.component()); - // 组件属性配置 ObjectNode componentProps = generateComponentProps(field.props(), field.component()); if (componentProps.size() > 0) { fieldSchema.set("x-component-props", componentProps); } } - // 装饰器属性配置 - ObjectNode decoratorProps = generateDecoratorProps(field.decoratorProps()); - if (decoratorProps.size() > 0) { - fieldSchema.set("x-decorator-props", decoratorProps); - } - - // 校验规则 - if (field.validators().length > 0) { + // 验证规则 + if (field.validators().length > 0 || field.required()) { ArrayNode validators = fieldSchema.putArray("x-validator"); + if (field.required()) { + ObjectNode requiredValidator = objectMapper.createObjectNode(); + requiredValidator.put("required", true); + requiredValidator.put("message", "此项为必填项"); + validators.add(requiredValidator); + } for (FormilyValidator validator : field.validators()) { validators.add(generateValidator(validator)); } @@ -199,7 +138,7 @@ public class FormilySchemaFactory { } } - // 展示控制 - 这些属性始终输出,因为它们有默认值 + // 展示控制 fieldSchema.put("x-pattern", field.pattern()); fieldSchema.put("x-display", field.display()); fieldSchema.put("x-hidden", field.hidden()); @@ -207,12 +146,18 @@ public class FormilySchemaFactory { return fieldSchema; } - private static ObjectNode generateMapComponentProps(FormilyMapConfig config) { + private static ObjectNode generateDecoratorProps(FormilyField field) { ObjectNode props = objectMapper.createObjectNode(); - props.put("allowAdd", config.allowAdd()); - props.put("allowRemove", config.allowRemove()); - props.put("allowCustomKey", config.allowCustomKey()); + if (field.gridSpan() > 1) { + props.put("gridSpan", field.gridSpan()); + } + if (!field.tooltip().isEmpty()) { + props.put("tooltip", field.tooltip()); + } + if (field.required()) { + props.put("asterisk", true); + } return props; } @@ -220,11 +165,19 @@ public class FormilySchemaFactory { private static ObjectNode generateComponentProps(FormilyComponentProps props, String componentType) { ObjectNode node = objectMapper.createObjectNode(); + // 通用属性 + if (!props.placeholder().isEmpty()) { + node.put("placeholder", props.placeholder()); + } + if (props.disabled()) { + node.put("disabled", true); + } + if (props.loading()) { + node.put("loading", true); + } + // Select组件属性 if ("Select".equals(componentType)) { - if (!props.api().isEmpty()) { - node.put("api", props.api()); - } if (!props.labelField().equals("label")) { node.put("labelField", props.labelField()); } @@ -237,12 +190,11 @@ public class FormilySchemaFactory { if (props.showSearch()) { node.put("showSearch", true); } - if (!props.placeholder().isEmpty()) { - node.put("placeholder", props.placeholder()); - } if (!props.mode().isEmpty()) { node.put("mode", props.mode()); } + node.put("filterOption", props.filterOption()); + if (props.enum_().length > 0) { ArrayNode enumValues = node.putArray("enum"); Arrays.stream(props.enum_()).forEach(enumValues::add); @@ -256,64 +208,88 @@ public class FormilySchemaFactory { // MonacoEditor组件属性 if ("MonacoEditor".equals(componentType)) { FormilyEditorProps editorProps = props.editor(); - if (editorProps != null) { - if (!editorProps.placeholder().isEmpty()) { - node.put("placeholder", editorProps.placeholder()); - } - - ObjectNode options = node.putObject("options"); - options.put("language", editorProps.language()); - options.put("theme", editorProps.theme()); - - ObjectNode minimap = options.putObject("minimap"); - minimap.put("enabled", editorProps.minimap()); - - options.put("lineNumbers", editorProps.lineNumbers() ? "on" : "off"); - options.put("wordWrap", editorProps.wordWrap() ? "on" : "off"); - options.put("fontSize", editorProps.fontSize()); - options.put("tabSize", editorProps.tabSize()); - options.put("automaticLayout", editorProps.automaticLayout()); - options.put("folding", editorProps.folding()); - } + ObjectNode options = node.putObject("options"); + + options.put("language", editorProps.language()); + options.put("theme", editorProps.theme()); + options.put("minimap", editorProps.minimap()); + options.put("lineNumbers", editorProps.lineNumbers()); + options.put("wordWrap", editorProps.wordWrap()); + options.put("fontSize", editorProps.fontSize()); + options.put("tabSize", editorProps.tabSize()); + options.put("automaticLayout", editorProps.automaticLayout()); + options.put("folding", editorProps.folding()); + options.put("scrollBeyondLastLine", editorProps.scrollBeyondLastLine()); + options.put("suggestOnTriggerCharacters", editorProps.suggestOnTriggerCharacters()); + options.put("snippetsPreventQuickSuggestions", editorProps.snippetsPreventQuickSuggestions()); + + ObjectNode scrollbar = options.putObject("scrollbar"); + scrollbar.put("vertical", editorProps.scrollbarVertical()); + scrollbar.put("horizontal", editorProps.scrollbarHorizontal()); + + node.put("debounceTime", editorProps.debounceTime()); + node.put("readOnly", editorProps.readOnly()); } return node; } - private static ObjectNode generateDecoratorProps(FormilyDecoratorProps props) { - ObjectNode node = objectMapper.createObjectNode(); + private static void generateMapSchema(ObjectNode fieldSchema, FormilyMapConfig config) { + fieldSchema.put("x-component", "ArrayItems"); + + // Map组件属性 + ObjectNode componentProps = fieldSchema.putObject("x-component-props"); + componentProps.put("allowAdd", config.allowAdd()); + componentProps.put("allowRemove", config.allowRemove()); + componentProps.put("allowCustomKey", config.allowCustomKey()); - if (!props.tooltip().isEmpty()) { - node.put("tooltip", props.tooltip()); - } - if (props.labelCol() != 6) { - node.put("labelCol", props.labelCol()); - } - if (props.wrapperCol() != 12) { - node.put("wrapperCol", props.wrapperCol()); - } - if (!props.labelAlign().equals("right")) { - node.put("labelAlign", props.labelAlign()); - } - if (!props.size().equals("default")) { - node.put("size", props.size()); - } - if (props.asterisk()) { - node.put("asterisk", true); - } - if (!props.bordered()) { - node.put("bordered", false); - } - if (!props.colon()) { - node.put("colon", false); + // 生成Map项的Schema + ObjectNode items = fieldSchema.putObject("items"); + items.put("type", "object"); + items.put("x-component", "ArrayItems.Item"); + + // 生成Map项的属性Schema + ObjectNode properties = items.putObject("properties"); + + // 键的Schema + ObjectNode keySchema = properties.putObject("key"); + keySchema.put("type", "string"); + keySchema.put("title", config.keyTitle()); + keySchema.put("x-decorator", "FormItem"); + keySchema.put("x-component", config.keyComponent()); + + ObjectNode keyProps = generateComponentProps(config.keyProps(), config.keyComponent()); + if (keyProps.size() > 0) { + keySchema.set("x-component-props", keyProps); } - return node; + // 值的Schema + ObjectNode valueSchema = properties.putObject("value"); + valueSchema.put("type", "string"); + valueSchema.put("title", config.valueTitle()); + valueSchema.put("x-decorator", "FormItem"); + valueSchema.put("x-component", config.valueComponent()); + + ObjectNode valueProps = generateComponentProps(config.valueProps(), config.valueComponent()); + if (valueProps.size() > 0) { + valueSchema.set("x-component-props", valueProps); + } + + // 操作按钮 + ObjectNode operationSchema = properties.putObject("operations"); + operationSchema.put("type", "void"); + operationSchema.put("x-component", "ArrayItems.Remove"); + + // 添加按钮 + ObjectNode addition = fieldSchema.putObject("properties").putObject("addition"); + addition.put("type", "void"); + addition.put("title", config.addText()); + addition.put("x-component", "ArrayItems.Addition"); } - private static ObjectNode generateValidator(FormilyValidator validator) { + private static JsonNode generateValidator(FormilyValidator validator) { ObjectNode node = objectMapper.createObjectNode(); - + if (validator.required()) { node.put("required", true); } @@ -326,33 +302,110 @@ public class FormilySchemaFactory { if (!validator.pattern().isEmpty()) { node.put("pattern", validator.pattern()); } - if (validator.min() != -1) { + if (validator.whitespace()) { + node.put("whitespace", true); + } + if (validator.min() != Double.MIN_VALUE) { node.put("min", validator.min()); } - if (validator.max() != -1) { + if (validator.max() != Double.MAX_VALUE) { node.put("max", validator.max()); } + if (validator.minLength() != -1) { + node.put("minLength", validator.minLength()); + } + if (validator.maxLength() != -1) { + node.put("maxLength", validator.maxLength()); + } + if (!validator.triggerType().isEmpty()) { + node.put("triggerType", validator.triggerType()); + } + if (!validator.validator().isEmpty()) { + node.put("validator", validator.validator()); + } + if (!validator.asyncValidator().isEmpty()) { + node.put("asyncValidator", validator.asyncValidator()); + } return node; } - private static ObjectNode generateReaction(FormilyReaction reaction) { + private static JsonNode generateReaction(FormilyReaction reaction) { ObjectNode node = objectMapper.createObjectNode(); - - // 添加依赖字段 + if (reaction.dependencies().length > 0) { - ArrayNode deps = node.putArray("dependencies"); - Arrays.stream(reaction.dependencies()).forEach(deps::add); + ArrayNode dependencies = node.putArray("dependencies"); + Arrays.stream(reaction.dependencies()).forEach(dependencies::add); + } + + if (!reaction.when().isEmpty()) { + node.put("when", reaction.when()); } - // 创建fulfill结构 ObjectNode fulfill = node.putObject("fulfill"); + + // 处理run配置 + if (!reaction.run().isEmpty()) { + fulfill.put("run", reaction.run()); + } + ObjectNode state = fulfill.putObject("state"); - - if (!reaction.state().isEmpty() && !reaction.value().isEmpty()) { + + // 处理loading状态 + if (reaction.showLoading()) { + state.put("loading", false); + } + + // 处理数据源 + if (!reaction.state().isEmpty()) { state.put(reaction.state(), reaction.value()); } + + if (!reaction.onSuccess().isEmpty()) { + fulfill.put("onSuccess", reaction.onSuccess()); + } + + if (!reaction.onError().isEmpty()) { + fulfill.put("onError", reaction.onError()); + } + + if (reaction.immediate()) { + node.put("immediate", true); + } + + if (reaction.runOnMounted()) { + node.put("runOnMounted", true); + } + + if (reaction.debounceTime() != 100) { + node.put("debounceTime", reaction.debounceTime()); + } return node; } + + private static List getAllFields(Class clazz) { + List fields = new ArrayList<>(); + Class currentClass = clazz; + + while (currentClass != null && currentClass != Object.class) { + fields.addAll(Arrays.asList(currentClass.getDeclaredFields())); + currentClass = currentClass.getSuperclass(); + } + + return fields; + } + + private static Class findNearestFormClass(Class clazz) { + Class currentClass = clazz; + + while (currentClass != null && currentClass != Object.class) { + if (currentClass.isAnnotationPresent(FormilyForm.class)) { + return currentClass; + } + currentClass = currentClass.getSuperclass(); + } + + return null; + } } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyValidator.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyValidator.java index c905a596..541aaea6 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyValidator.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyValidator.java @@ -2,15 +2,66 @@ package com.qqchen.deploy.backend.framework.annotation.formily; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -@Target({}) @Retention(RetentionPolicy.RUNTIME) public @interface FormilyValidator { - boolean required() default false; // 是否必填 - String message() default ""; // 错误信息 - String format() default ""; // 格式(email/url/phone等) - String pattern() default ""; // 正则表达式 - int min() default -1; // 最小值/长度 - int max() default -1; // 最大值/长度 + /** + * 是否必填 + */ + boolean required() default false; + + /** + * 错误提示消息 + */ + String message() default ""; + + /** + * 触发类型 + */ + String triggerType() default "onChange"; + + /** + * 格式验证 + */ + String format() default ""; + + /** + * 是否验证空白字符 + */ + boolean whitespace() default false; + + /** + * 最小值 + */ + double min() default Double.MIN_VALUE; + + /** + * 最大值 + */ + double max() default Double.MAX_VALUE; + + /** + * 最小长度 + */ + int minLength() default -1; + + /** + * 最大长度 + */ + int maxLength() default -1; + + /** + * 正则表达式 + */ + String pattern() default ""; + + /** + * 自定义验证器 + */ + String validator() default ""; + + /** + * 异步验证器 + */ + String asyncValidator() default ""; } \ No newline at end of file diff --git a/backend/src/test/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactoryTest.java b/backend/src/test/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactoryTest.java index 6dec17de..d7cf79a8 100644 --- a/backend/src/test/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactoryTest.java +++ b/backend/src/test/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilySchemaFactoryTest.java @@ -1,262 +1,186 @@ package com.qqchen.deploy.backend.framework.annotation.formily; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; -import java.util.Map; -import java.util.Iterator; -import java.util.List; -import java.util.Arrays; -import java.util.ArrayList; - import static org.junit.jupiter.api.Assertions.*; public class FormilySchemaFactoryTest { @Test - public void testGenerateSchema() { - JsonNode schema = FormilySchemaFactory.generateSchema(TestForm.class); + public void testGenerateSchema() throws Exception { + JsonNode schema = FormilySchemaFactory.generateSchema(CascadeForm.class); + + // 打印生成的Schema + ObjectMapper mapper = new ObjectMapper(); + System.out.println("Generated Schema:"); + System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema)); // 验证基本结构 assertEquals("object", schema.get("type").asText()); - assertEquals("测试表单", schema.get("name").asText()); - assertTrue(schema.has("properties")); + assertEquals("2.0", schema.get("version").asText()); + assertEquals("级联选择表单", schema.get("name").asText()); + assertTrue(schema.get("validateFirst").asBoolean()); + + // 验证表单布局 + JsonNode xProps = schema.get("x-props"); + assertEquals(6, xProps.get("labelCol").asInt()); + assertEquals(18, xProps.get("wrapperCol").asInt()); - // 验证字段顺序 - Iterator fieldNames = schema.get("properties").fieldNames(); - List actualOrder = new ArrayList<>(); - fieldNames.forEachRemaining(actualOrder::add); - - List expectedOrder = Arrays.asList("a", "b", "c", "envVars", "script"); - assertEquals(expectedOrder, actualOrder, "字段顺序应该按照order排序"); - - // 验证字段A - JsonNode fieldA = schema.get("properties").get("a"); + // 验证字段A(系统选择) + JsonNode fieldA = schema.get("properties").get("systemId"); assertEquals("string", fieldA.get("type").asText()); - assertEquals("选择A", fieldA.get("title").asText()); + assertEquals("选择系统", fieldA.get("title").asText()); assertEquals("Select", fieldA.get("x-component").asText()); assertEquals("FormItem", fieldA.get("x-decorator").asText()); + + // 验证装饰器属性 + JsonNode decoratorPropsA = fieldA.get("x-decorator-props"); + assertTrue(decoratorPropsA.get("asterisk").asBoolean()); + + // 验证组件属性 + JsonNode propsA = fieldA.get("x-component-props"); + assertEquals("请选择系统", propsA.get("placeholder").asText()); + assertEquals("name", propsA.get("labelField").asText()); + assertEquals("id", propsA.get("valueField").asText()); + assertTrue(propsA.get("showSearch").asBoolean()); + assertEquals("single", propsA.get("mode").asText()); + assertTrue(propsA.get("filterOption").asBoolean()); + + // 验证验证规则 + JsonNode validatorA = fieldA.get("x-validator").get(0); + assertTrue(validatorA.get("required").asBoolean()); + assertEquals("此项为必填项", validatorA.get("message").asText()); + + // 验证展示控制 assertEquals("editable", fieldA.get("x-pattern").asText()); assertEquals("visible", fieldA.get("x-display").asText()); assertFalse(fieldA.get("x-hidden").asBoolean()); - // 验证组件属性 - JsonNode propsA = fieldA.get("x-component-props"); - assertEquals("/api/options/A", propsA.get("api").asText()); - assertEquals("name", propsA.get("labelField").asText()); - assertEquals("id", propsA.get("valueField").asText()); - assertTrue(propsA.get("allowClear").asBoolean()); - - // 验证字段B的联动 - JsonNode fieldB = schema.get("properties").get("b"); + // 验证字段B(视图选择) + JsonNode fieldB = schema.get("properties").get("viewId"); JsonNode reactionsB = fieldB.get("x-reactions"); - assertTrue(reactionsB.isArray()); JsonNode reactionB = reactionsB.get(0); - assertTrue(reactionB.get("dependencies").isArray()); - assertEquals("a", reactionB.get("dependencies").get(0).asText()); - + + // 验证B依赖于A + assertTrue(reactionB.has("dependencies")); + JsonNode depsB = reactionB.get("dependencies"); + assertTrue(depsB.isArray()); + assertEquals("systemId", depsB.get(0).asText()); + + // 验证B的条件 + assertEquals("{{$deps.systemId}}", reactionB.get("when").asText()); + + // 验证B的数据源 JsonNode fulfillB = reactionB.get("fulfill"); JsonNode stateB = fulfillB.get("state"); - assertEquals("{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.a).then(data => data.data)}}", + assertFalse(stateB.get("loading").asBoolean()); + assertEquals("{{$fetch('/api/v1/views?systemId=' + $deps.systemId).then(data => data.data)}}", stateB.get("dataSource").asText()); - // 验证字段C的联动 - JsonNode fieldC = schema.get("properties").get("c"); + // 验证字段C(任务选择) + JsonNode fieldC = schema.get("properties").get("jobId"); JsonNode reactionsC = fieldC.get("x-reactions"); - assertTrue(reactionsC.isArray()); JsonNode reactionC = reactionsC.get(0); - assertTrue(reactionC.get("dependencies").isArray()); - assertEquals(2, reactionC.get("dependencies").size()); - assertEquals("a", reactionC.get("dependencies").get(0).asText()); - assertEquals("b", reactionC.get("dependencies").get(1).asText()); - + + // 验证C依赖于A和B + JsonNode depsC = reactionC.get("dependencies"); + assertEquals(2, depsC.size()); + assertEquals("systemId", depsC.get(0).asText()); + assertEquals("viewId", depsC.get(1).asText()); + + // 验证C的条件 + assertEquals("{{$deps.systemId && $deps.viewId}}", reactionC.get("when").asText()); + + // 验证C的数据源 JsonNode fulfillC = reactionC.get("fulfill"); JsonNode stateC = fulfillC.get("state"); - assertEquals("{{$fetch('/api/v1/jenkins-job/list?externalSystemId=' + $deps.a + '&viewId=' + $deps.b).then(data => data.data)}}", + assertFalse(stateC.get("loading").asBoolean()); + assertEquals( + "{{$fetch('/api/v1/jobs?systemId=' + $deps.systemId + '&viewId=' + $deps.viewId).then(data => data.data)}}", stateC.get("dataSource").asText()); - - // 验证编辑器字段 - JsonNode fieldScript = schema.get("properties").get("script"); - assertEquals("string", fieldScript.get("type").asText()); - assertEquals("Pipeline script", fieldScript.get("title").asText()); - assertEquals("MonacoEditor", fieldScript.get("x-component").asText()); - - // 验证编辑器属性 - JsonNode scriptProps = fieldScript.get("x-component-props"); - JsonNode options = scriptProps.get("options"); - assertEquals("groovy", options.get("language").asText()); - assertEquals("vs-dark", options.get("theme").asText()); - assertFalse(options.get("minimap").get("enabled").asBoolean()); - assertEquals("on", options.get("lineNumbers").asText()); - assertEquals("on", options.get("wordWrap").asText()); - assertEquals(14, options.get("fontSize").asInt()); - assertEquals(4, options.get("tabSize").asInt()); - assertTrue(options.get("automaticLayout").asBoolean()); - assertTrue(options.get("folding").asBoolean()); - assertEquals("请输入Pipeline脚本", scriptProps.get("placeholder").asText()); - - // 验证Map字段 - JsonNode mapField = schema.get("properties").get("envVars"); - assertEquals("map", mapField.get("type").asText()); - assertEquals("环境变量", mapField.get("title").asText()); - assertEquals("ArrayItems", mapField.get("x-component").asText()); - - // 验证Map项配置 - JsonNode items = mapField.get("items"); - assertEquals("object", items.get("type").asText()); - assertEquals("ArrayItems.Item", items.get("x-component").asText()); - - // 验证键值配置 - JsonNode properties = items.get("properties"); - - // 验证键配置 - JsonNode keySchema = properties.get("key"); - assertEquals("string", keySchema.get("type").asText()); - assertEquals("变量名", keySchema.get("title").asText()); - assertEquals("Select", keySchema.get("x-component").asText()); - - // 验证键的组件属性 - JsonNode keyProps = keySchema.get("x-component-props"); - assertEquals("/api/v1/env-vars/keys", keyProps.get("api").asText()); - assertTrue(keyProps.get("showSearch").asBoolean()); - assertTrue(keyProps.get("allowClear").asBoolean()); - - // 验证值配置 - JsonNode valueSchema = properties.get("value"); - assertEquals("string", valueSchema.get("type").asText()); - assertEquals("变量值", valueSchema.get("title").asText()); - assertEquals("Input", valueSchema.get("x-component").asText()); - - // 验证操作按钮 - JsonNode operations = properties.get("operations"); - assertEquals("void", operations.get("type").asText()); - assertEquals("ArrayItems.Remove", operations.get("x-component").asText()); - - // 验证添加按钮 - JsonNode addition = mapField.get("properties").get("addition"); - assertEquals("void", addition.get("type").asText()); - assertEquals("添加环境变量", addition.get("title").asText()); - assertEquals("ArrayItems.Addition", addition.get("x-component").asText()); - - // 打印生成的Schema以便查看 - System.out.println(schema.toPrettyString()); } } -@FormilyForm(name = "测试表单") -class TestForm { +@FormilyForm( + name = "级联选择表单", + version = "2.0", + validateFirst = true, + labelCol = 6, + wrapperCol = 18 +) +class CascadeForm { @FormilyField( - title = "选择A", + title = "选择系统", component = "Select", order = 1, + required = true, props = @FormilyComponentProps( - api = "/api/options/A", labelField = "name", valueField = "id", - allowClear = true + showSearch = true, + mode = "single", + filterOption = true, + placeholder = "请选择系统" ), - validators = { - @FormilyValidator( - required = true, - message = "请选择A" + reactions = { + @FormilyReaction( + state = "dataSource", + value = "{{$fetch('/api/v1/systems').then(data => data.data)}}", + showLoading = true ) } ) - private String a; + private String systemId; @FormilyField( - title = "选择B", + title = "选择视图", component = "Select", order = 2, + required = true, props = @FormilyComponentProps( - labelField = "viewName", - valueField = "id" + labelField = "name", + valueField = "id", + showSearch = true, + mode = "single", + filterOption = true, + placeholder = "请选择视图" ), - validators = { - @FormilyValidator( - required = true, - message = "请选择B" - ) - }, reactions = { @FormilyReaction( - dependencies = {"a"}, + dependencies = {"systemId"}, + when = "{{$deps.systemId}}", state = "dataSource", - value = "{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.a).then(data => data.data)}}" + value = "{{$fetch('/api/v1/views?systemId=' + $deps.systemId).then(data => data.data)}}", + showLoading = true ) } ) - private String b; + private String viewId; @FormilyField( - title = "选择C", + title = "选择任务", component = "Select", order = 3, + required = true, props = @FormilyComponentProps( - labelField = "jobName", - valueField = "id" + labelField = "name", + valueField = "id", + showSearch = true, + mode = "single", + filterOption = true, + placeholder = "请选择任务" ), - validators = { - @FormilyValidator( - required = true, - message = "请选择C" - ) - }, reactions = { @FormilyReaction( - dependencies = {"a", "b"}, + dependencies = {"systemId", "viewId"}, + when = "{{$deps.systemId && $deps.viewId}}", state = "dataSource", - value = "{{$fetch('/api/v1/jenkins-job/list?externalSystemId=' + $deps.a + '&viewId=' + $deps.b).then(data => data.data)}}" + value = "{{$fetch('/api/v1/jobs?systemId=' + $deps.systemId + '&viewId=' + $deps.viewId).then(data => data.data)}}", + showLoading = true ) } ) - private String c; - - @FormilyField( - title = "环境变量", - type = "map", - order = 4, - mapConfig = @FormilyMapConfig( - keyTitle = "变量名", - valueTitle = "变量值", - keyComponent = "Select", - valueComponent = "Input", - keyProps = @FormilyComponentProps( - api = "/api/v1/env-vars/keys", - showSearch = true, - allowClear = true - ), - addText = "添加环境变量", - allowCustomKey = true - ) - ) - private Map envVars; - - @FormilyField( - title = "Pipeline script", - type = "string", - component = "MonacoEditor", - order = 5, - props = @FormilyComponentProps( - editor = @FormilyEditorProps( - language = "groovy", - theme = "vs-dark", - minimap = false, - lineNumbers = true, - wordWrap = true, - fontSize = 14, - tabSize = 4, - automaticLayout = true, - folding = true, - placeholder = "请输入Pipeline脚本" - ) - ), - validators = { - @FormilyValidator( - required = true, - message = "请输入Pipeline脚本" - ) - } - ) - private String script; + private String jobId; } \ No newline at end of file