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 5a7b7d7a..6b5fc76f 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 @@ -11,7 +11,7 @@ public @interface FormilyField { // 基础属性 String title(); // 字段标题 String description() default ""; // 字段描述 - String type() default "string"; // 字段类型(string/number/boolean/array/object) + String type() default "string"; // 字段类型(string/number/boolean/array/object/map) // 组件属性 String component() default "Input"; // 组件类型 @@ -29,4 +29,7 @@ public @interface FormilyField { String pattern() default "editable"; // 展示模式(editable/disabled/readOnly) String display() default "visible"; // 显示模式(visible/hidden/none) boolean hidden() default false; // 是否隐藏 + + // Map类型配置 + FormilyMapConfig mapConfig() default @FormilyMapConfig; // Map配置 } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyMapConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyMapConfig.java new file mode 100644 index 00000000..928318fc --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/annotation/formily/FormilyMapConfig.java @@ -0,0 +1,65 @@ +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({}) +@Retention(RetentionPolicy.RUNTIME) +public @interface FormilyMapConfig { + /** + * 键的标题 + */ + String keyTitle() default "键"; + + /** + * 值的标题 + */ + String valueTitle() default "值"; + + /** + * 键的组件类型 + */ + String keyComponent() default "Input"; + + /** + * 值的组件类型 + */ + String valueComponent() default "Input"; + + /** + * 键的组件属性 + */ + FormilyComponentProps keyProps() default @FormilyComponentProps; + + /** + * 值的组件属性 + */ + FormilyComponentProps valueProps() default @FormilyComponentProps; + + /** + * 是否允许添加 + */ + boolean allowAdd() default true; + + /** + * 是否允许删除 + */ + boolean allowRemove() default true; + + /** + * 是否允许自定义键 + */ + boolean allowCustomKey() default true; + + /** + * 添加按钮文本 + */ + String addText() default "添加"; + + /** + * 删除按钮文本 + */ + String removeText() 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 2feee3c6..7bbee25f 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 @@ -108,12 +108,64 @@ public class FormilySchemaFactory { // 组件属性 fieldSchema.put("x-decorator", field.decorator()); - fieldSchema.put("x-component", field.component()); + 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); + } - // 组件属性配置 - ObjectNode componentProps = generateComponentProps(field.props(), field.component()); - 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"); + } else { + fieldSchema.put("x-component", field.component()); + // 组件属性配置 + ObjectNode componentProps = generateComponentProps(field.props(), field.component()); + if (componentProps.size() > 0) { + fieldSchema.set("x-component-props", componentProps); + } } // 装饰器属性配置 @@ -146,6 +198,16 @@ public class FormilySchemaFactory { return fieldSchema; } + private static ObjectNode generateMapComponentProps(FormilyMapConfig config) { + ObjectNode props = objectMapper.createObjectNode(); + + props.put("allowAdd", config.allowAdd()); + props.put("allowRemove", config.allowRemove()); + props.put("allowCustomKey", config.allowCustomKey()); + + return props; + } + private static ObjectNode generateComponentProps(FormilyComponentProps props, String componentType) { ObjectNode node = objectMapper.createObjectNode(); 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 e174cdb7..954d3e76 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 @@ -2,6 +2,9 @@ package com.qqchen.deploy.backend.framework.annotation.formily; import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.Test; + +import java.util.Map; + import static org.junit.jupiter.api.Assertions.*; public class FormilySchemaFactoryTest { @@ -9,12 +12,12 @@ public class FormilySchemaFactoryTest { @Test public void testGenerateSchema() { JsonNode schema = FormilySchemaFactory.generateSchema(TestForm.class); - + // 验证基本结构 assertEquals("object", schema.get("type").asText()); assertEquals("测试表单", schema.get("name").asText()); assertTrue(schema.has("properties")); - + // 验证字段A JsonNode fieldA = schema.get("properties").get("a"); assertEquals("string", fieldA.get("type").asText()); @@ -24,14 +27,14 @@ public class FormilySchemaFactoryTest { 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"); JsonNode reactionsB = fieldB.get("x-reactions"); @@ -39,12 +42,12 @@ public class FormilySchemaFactoryTest { JsonNode reactionB = reactionsB.get(0); assertTrue(reactionB.get("dependencies").isArray()); assertEquals("a", reactionB.get("dependencies").get(0).asText()); - + JsonNode fulfillB = reactionB.get("fulfill"); JsonNode stateB = fulfillB.get("state"); - assertEquals("{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.a).then(data => data.data)}}", + assertEquals("{{$fetch('/api/v1/jenkins-view/list?externalSystemId=' + $deps.a).then(data => data.data)}}", stateB.get("dataSource").asText()); - + // 验证字段C的联动 JsonNode fieldC = schema.get("properties").get("c"); JsonNode reactionsC = fieldC.get("x-reactions"); @@ -54,10 +57,10 @@ public class FormilySchemaFactoryTest { assertEquals(2, reactionC.get("dependencies").size()); assertEquals("a", reactionC.get("dependencies").get(0).asText()); assertEquals("b", reactionC.get("dependencies").get(1).asText()); - + 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)}}", + assertEquals("{{$fetch('/api/v1/jenkins-job/list?externalSystemId=' + $deps.a + '&viewId=' + $deps.b).then(data => data.data)}}", stateC.get("dataSource").asText()); // 验证编辑器字段 @@ -65,7 +68,7 @@ public class FormilySchemaFactoryTest { 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"); @@ -79,7 +82,50 @@ public class FormilySchemaFactoryTest { 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()); } @@ -177,4 +223,23 @@ class TestForm { } ) private String script; + + @FormilyField( + title = "环境变量", + type = "map", + 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; } \ No newline at end of file