Просмотр исходного кода

[+]响应增加注释面板,请求响应兼容旧版本,swagger文档生成优化,兼容knife4j优化

Lianjy 4 лет назад
Родитель
Сommit
b6fb5783dc

+ 5 - 6
magic-api/src/main/java/org/ssssssss/magicapi/controller/RequestHandler.java

@@ -104,13 +104,12 @@ public class RequestHandler extends MagicController {
 			doValidate("header", requestEntity.getApiInfo().getHeaders(), headers, HEADER_INVALID);
 			// 验证 path
 			doValidate("path", paths, requestEntity.getPathVariables(), PATH_VARIABLE_INVALID);
-			String requestBody = requestEntity.getApiInfo().getRequestBody();
-			if (StringUtils.isNotBlank(requestBody)) {
-				BaseDefinition body = JsonUtils.readValue(requestBody, BaseDefinition.class);
+			BaseDefinition requestBody = requestEntity.getApiInfo().getRequestBodyDefinition();
+			if (requestBody.getChildren().size() > 0) {
 				Object bodyValue = context.get(VAR_NAME_REQUEST_BODY);
-				body.setName("root");
-				doValidate(VAR_NAME_REQUEST_BODY, Collections.singletonList(body), new HashMap<String, Object>() {{
-					put(body.getName(), bodyValue);
+				requestBody.setName(StringUtils.defaultIfBlank(requestBody.getName(), "root"));
+				doValidate(VAR_NAME_REQUEST_BODY, Collections.singletonList(requestBody), new HashMap<String, Object>() {{
+					put(requestBody.getName(), bodyValue);
 				}}, BODY_INVALID);
 			}
 		} catch (ValidateException e) {

+ 33 - 3
magic-api/src/main/java/org/ssssssss/magicapi/model/ApiInfo.java

@@ -63,6 +63,16 @@ public class ApiInfo extends MagicEntity {
 	 */
 	private transient JsonNode jsonNode;
 
+	/**
+	 * 请求体属性
+	 */
+	private BaseDefinition requestBodyDefinition;
+
+    /**
+     * 输出结果属性
+     */
+    private BaseDefinition responseBodyDefinition;
+
 	public String getMethod() {
 		return method;
 	}
@@ -231,7 +241,23 @@ public class ApiInfo extends MagicEntity {
 				.map(it -> Objects.toString(it.getValue(), null)).orElse(null);
 	}
 
-	@Override
+    public BaseDefinition getRequestBodyDefinition() {
+        return requestBodyDefinition;
+    }
+
+    public void setRequestBodyDefinition(BaseDefinition requestBodyDefinition) {
+        this.requestBodyDefinition = requestBodyDefinition;
+    }
+
+    public BaseDefinition getResponseBodyDefinition() {
+        return responseBodyDefinition;
+    }
+
+    public void setResponseBodyDefinition(BaseDefinition responseBodyDefinition) {
+        this.responseBodyDefinition = responseBodyDefinition;
+    }
+
+    @Override
 	public boolean equals(Object o) {
 		if (this == o) return true;
 		if (o == null || getClass() != o.getClass()) return false;
@@ -247,13 +273,15 @@ public class ApiInfo extends MagicEntity {
 				Objects.equals(option, apiInfo.option) &&
 				Objects.equals(requestBody, apiInfo.requestBody) &&
 				Objects.equals(headers, apiInfo.headers) &&
-				Objects.equals(description, apiInfo.description);
+				Objects.equals(description, apiInfo.description) &&
+				Objects.equals(requestBodyDefinition, apiInfo.requestBodyDefinition) &&
+				Objects.equals(responseBodyDefinition, apiInfo.responseBodyDefinition);
 	}
 
 
 	@Override
 	public int hashCode() {
-		return Objects.hash(id, method, path, script, name, groupId, parameters, option, requestBody, headers, responseBody, description);
+		return Objects.hash(id, method, path, script, name, groupId, parameters, option, requestBody, headers, responseBody, description, requestBodyDefinition, responseBodyDefinition);
 	}
 
 	public ApiInfo copy() {
@@ -271,6 +299,8 @@ public class ApiInfo extends MagicEntity {
 		info.setResponseBody(this.responseBody);
 		info.setDescription(this.description);
 		info.setPaths(this.paths);
+		info.setRequestBodyDefinition(this.requestBodyDefinition);
+		info.setResponseBodyDefinition(this.responseBodyDefinition);
 		return info;
 	}
 }

+ 0 - 1
magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerEntity.java

@@ -346,7 +346,6 @@ public class SwaggerEntity {
 				Map<String, Object> schema = new HashMap<>();
 				schema.put("type", type);
 				schema.put("example", example);
-				this.schema = doProcessSchema(example);
 				this.schema = "";
 			} else {
 				this.example = example;

+ 56 - 29
magic-api/src/main/java/org/ssssssss/magicapi/swagger/SwaggerProvider.java

@@ -77,11 +77,18 @@ public class SwaggerProvider {
 				List<SwaggerEntity.Parameter> parameters = parseParameters(mapper, info);
 				hasBody = parameters.stream().anyMatch(it -> VAR_NAME_REQUEST_BODY.equals(it.getIn()));
                 if (hasBody) {
-                    BaseDefinition baseDefinition = JsonUtils.readValue(info.getRequestBody(), BaseDefinition.class);
-                    doProcessDefinition(baseDefinition, info, StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY));
+                    BaseDefinition baseDefinition = info.getRequestBodyDefinition();
+                    doProcessDefinition(baseDefinition, info, StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY), "request");
                 }
 				parameters.forEach(path::addParameter);
-				path.addResponse("200", mapper.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
+				// path.addResponse("200", mapper.readValue(Objects.toString(info.getResponseBody(), BODY_EMPTY), Object.class));
+				Map responseMap = parseResponse(info);
+				if (!responseMap.isEmpty()) {
+					path.setResponses(responseMap);
+					BaseDefinition baseDefinition = info.getResponseBodyDefinition();
+					doProcessDefinition(baseDefinition, info, StringUtils.defaultIfBlank(baseDefinition.getName(), "responseData"), "response");
+				}
+
 			} catch (Exception ignored) {
 			}
 			if (hasBody) {
@@ -121,25 +128,24 @@ public class SwaggerProvider {
 				});
 		paths.forEach(it -> parameters.add(new SwaggerEntity.Parameter(it.isRequired(), it.getName(), VAR_NAME_PATH_VARIABLE, it.getDataType().getJavascriptType(), it.getDescription(), it.getValue())));
 		try {
-			if (StringUtils.isNotBlank(info.getRequestBody()) && !BODY_EMPTY.equals(info.getRequestBody().replaceAll("\\s", ""))) {
-				BaseDefinition baseDefinition = JsonUtils.readValue(info.getRequestBody(), BaseDefinition.class);
-				if (BooleanLiteral.isTrue(baseDefinition)) {
-					SwaggerEntity.Parameter parameter = new SwaggerEntity.Parameter(baseDefinition.isRequired(), StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() : VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, baseDefinition.getDataType().getJavascriptType(), baseDefinition.getDescription(), baseDefinition);
-
-					Map<String, Object> schema = new HashMap<>(2);
-					String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
-					String voName =  groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«";
-					if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(baseDefinition.getDataType().getJavascriptType())) {
-						voName += StringUtils.defaultIfBlank(baseDefinition.getChildren().get(0).getName(), VAR_NAME_REQUEST_BODY + "_" + StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY)) + "»»";
-					} else {
-						voName += StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY) + "»»";
-					}
-
-					schema.put("originalRef", voName);
-					schema.put("$ref", DEFINITION + voName);
-					parameter.setSchema(schema);
-					parameters.add(parameter);
+			BaseDefinition baseDefinition = info.getRequestBodyDefinition();
+			if (baseDefinition.getChildren().size() > 0) {
+
+				SwaggerEntity.Parameter parameter = new SwaggerEntity.Parameter(baseDefinition.isRequired(), StringUtils.isNotBlank(baseDefinition.getName()) ? baseDefinition.getName() : VAR_NAME_REQUEST_BODY, VAR_NAME_REQUEST_BODY, baseDefinition.getDataType().getJavascriptType(), baseDefinition.getDescription(), baseDefinition);
+
+				Map<String, Object> schema = new HashMap<>(2);
+				String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
+				String voName =  groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«request«";
+				if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(baseDefinition.getDataType().getJavascriptType())) {
+					voName += StringUtils.defaultIfBlank(baseDefinition.getChildren().get(0).getName(), VAR_NAME_REQUEST_BODY + "_" + StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY)) + "»»»";
+				} else {
+					voName += StringUtils.defaultIfBlank(baseDefinition.getName(), VAR_NAME_REQUEST_BODY) + "»»»";
 				}
+
+				schema.put("originalRef", voName);
+				schema.put("$ref", DEFINITION + voName);
+				parameter.setSchema(schema);
+				parameters.add(parameter);
 			}
 
 		} catch (Exception ignored) {
@@ -147,23 +153,43 @@ public class SwaggerProvider {
 		return parameters;
 	}
 
-    private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String parentName) {
+	private Map<String, Object> parseResponse(ApiInfo info) {
+		Map<String, Object> result = new HashMap<>();
+
+		BaseDefinition baseDefinition = info.getRequestBodyDefinition();
+		if (baseDefinition.getChildren().size() > 0) {
+			String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
+			String voName =  groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«response«";
+			voName += StringUtils.defaultIfBlank(baseDefinition.getName(), "responseData") + "»»»";
+
+			Map<String, Object> schema = new HashMap<>(2);
+			schema.put("originalRef", voName);
+			schema.put("$ref", DEFINITION + voName);
+
+			Map<String, Object> response = new HashMap<>(2);
+			response.put("description", "OK");
+			response.put("schema", schema);
+			result.put("200", response);
+		}
+
+		return result;
+	}
+    private Map<String, Object> doProcessDefinition(BaseDefinition target, ApiInfo info, String parentName, String definitionType) {
         Map<String, Object> result = new HashMap<>(4);
-        result.put("type", target.getDataType().getJavascriptType());
         result.put("description", target.getDescription());
         if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY.equalsIgnoreCase(target.getDataType().getJavascriptType())) {
             if (target.getChildren().size() > 0) {
-                result.put("items", doProcessDefinition(target.getChildren().get(0), info, parentName + "_" + StringUtils.defaultIfBlank(target.getName(), VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY)));
+                result.put("items", doProcessDefinition(target.getChildren().get(0), info, parentName + "_" + StringUtils.defaultIfBlank(target.getName(), VAR_NAME_REQUEST_BODY_VALUE_TYPE_ARRAY), definitionType));
             } else {
                 result.put("items", Collections.emptyList());
             }
-
+			result.put("type", target.getDataType().getJavascriptType());
         } else if (VAR_NAME_REQUEST_BODY_VALUE_TYPE_OBJECT.equalsIgnoreCase(target.getDataType().getJavascriptType())) {
             String groupName = groupServiceProvider.getFullName(info.getGroupId()).replace("/", "-");
-            String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + "«" + StringUtils.defaultIfBlank(target.getName(), parentName)  + "»»";
+            String voName = groupName + "«" + info.getPath().replaceFirst("/", "").replaceAll("/", "_") + (StringUtils.equals("response", definitionType) ? "«response«" : "«request«") + StringUtils.defaultIfBlank(target.getName(), parentName)  + "»»»";
             if (this.DEFINITION_MAP.containsKey(voName)) {
 				// TODO 应该不会出现名字都一样的
-				voName.replace("»»", "_" + parentName + "»»");
+				voName.replace("»»»", "_" + parentName + "»»»");
 			}
             result.put("originalRef", voName);
             result.put("$ref", DEFINITION + voName);
@@ -171,14 +197,15 @@ public class SwaggerProvider {
             Map<String, Object> definition = new HashMap<>(4);
             Map<String, Map<String, Object>> properties = new HashMap<>(target.getChildren().size());
             for (BaseDefinition obj : target.getChildren()) {
-                properties.put(obj.getName(), doProcessDefinition(obj, info, parentName));
+                properties.put(obj.getName(), doProcessDefinition(obj, info, parentName, definitionType));
             }
             definition.put("properties", properties);
             definition.put("description", target.getDescription());
-
+			definition.put("type", target.getDataType().getJavascriptType());
             this.DEFINITION_MAP.put(voName, definition);
         } else {
             result.put("example", target.getValue());
+			result.put("type", target.getDataType().getJavascriptType());
         }
         return result;
     }

+ 37 - 31
magic-editor/src/console/src/components/common/magic-json.vue

@@ -23,36 +23,39 @@
             <magic-select :options="bodyTypes" :value.sync="fieldObj.dataType" default-value="String" style="width: 100%"/>
           </div>
         </div>
-        <div class="box-item">
-          <div class="item-title">是否必填</div>
-          <div class="item-content">
-            <div style="width: 25px; height: 25px;"><magic-checkbox :value.sync="fieldObj.required"/></div>
+        <template v-if="type == 'request'">
+          <div class="box-item">
+            <div class="item-title">是否必填</div>
+            <div class="item-content">
+              <div style="width: 25px; height: 25px;"><magic-checkbox :value.sync="fieldObj.required"/></div>
+            </div>
           </div>
-        </div>
-        <div class="box-item">
-          <div class="item-title">默认值</div>
-          <div class="item-content">
-            <magic-input :value.sync="fieldObj.defaultValue" style="width: 100%"/>
+          <div class="box-item">
+            <div class="item-title">默认值</div>
+            <div class="item-content">
+              <magic-input :value.sync="fieldObj.defaultValue" style="width: 100%"/>
+            </div>
           </div>
-        </div>
-        <div class="box-item">
-          <div class="item-title">验证方式</div>
-          <div class="item-content">
-            <magic-select :options="validates" :value.sync="fieldObj.validateType" default-value="pass" style="width: 100%"/>
+          <div class="box-item">
+            <div class="item-title">验证方式</div>
+            <div class="item-content">
+              <magic-select :options="validates" :value.sync="fieldObj.validateType" default-value="pass" style="width: 100%"/>
+            </div>
           </div>
-        </div>
-        <div class="box-item">
-          <div class="item-title">表达式或正则表达式</div>
-          <div class="item-content">
-            <magic-input :value.sync="fieldObj.expression" style="width: 100%"/>
+          <div class="box-item">
+            <div class="item-title">表达式或正则表达式</div>
+            <div class="item-content">
+              <magic-input :value.sync="fieldObj.expression" style="width: 100%"/>
+            </div>
           </div>
-        </div>
-        <div class="box-item">
-          <div class="item-title">验证说明</div>
-          <div class="item-content">
-            <magic-input :value.sync="fieldObj.error" style="width: 100%"/>
+          <div class="box-item">
+            <div class="item-title">验证说明</div>
+            <div class="item-content">
+              <magic-input :value.sync="fieldObj.error" style="width: 100%"/>
+            </div>
           </div>
-        </div>
+        </template>
+
         <div class="box-item">
           <div class="item-title">字段注释</div>
           <div class="item-content">
@@ -72,12 +75,14 @@
             <magic-input :value.sync="fieldObj.description" style="width: 100%"/>
           </div>
         </div>
-        <div class="box-item">
-          <div class="item-title">是否必填</div>
-          <div class="item-content">
-            <div style="width: 25px; height: 25px;"><magic-checkbox :value.sync="fieldObj.required"/></div>
+        <template v-if="type == 'request'">
+          <div class="box-item">
+            <div class="item-title">是否必填</div>
+            <div class="item-content">
+              <div style="width: 25px; height: 25px;"><magic-checkbox :value.sync="fieldObj.required"/></div>
+            </div>
           </div>
-        </div>
+        </template>
       </div>
     </div>
   </div>
@@ -99,7 +104,8 @@
       },
       // 解决子组件不强制刷新
       forceUpdate: Boolean,
-      height: String
+      height: String,
+      type: String
     },
     data() {
       return {

+ 6 - 35
magic-editor/src/console/src/components/editor/magic-script-editor.vue

@@ -490,7 +490,7 @@ export default {
         try {
          let requestBody = JSON.parse(this.info.requestBody)
           requestConfig.params = params
-          requestConfig.data = JSON.stringify(this.buildRequestBodyData(requestBody))
+          requestConfig.data = this.info.requestBody
           requestConfig.headers['Content-Type'] = 'application/json'
           requestConfig.transformRequest = []
         } catch (e) {
@@ -529,40 +529,6 @@ export default {
         info.ext.eventSource.close()
       })
     },
-    buildRequestBodyData(o) {
-      let requestBody = o
-      let newBody = {}
-      if ('Object' == requestBody.dataType) {
-        let body = {}
-        newBody = this.createJsonData(body, requestBody.children)
-      } else if ('Array' == requestBody.dataType) {
-        let body = []
-        newBody = this.createJsonData(body, requestBody.children, true)
-      }
-      // console.log('buildRequestBodyData', newBody);
-      return newBody
-    },
-    createJsonData(newBody, data, arrayFlag = false) {
-      data.map(item => {
-        let key, value = item.value;
-        if (!arrayFlag) {
-          key = item.name
-        }
-        if ('Object' == item.dataType) {
-          value = {}
-          newBody[key] = this.createJsonData(value, item.children)
-        } else if ('Array' == item.dataType) {
-          value = []
-          newBody[key] = this.createJsonData(value, item.children, true)
-        } else {
-            newBody[key] = (value == 'null' || value == 'undefined') ? null : value
-        }
-        if (arrayFlag) {
-          newBody.push(value)
-        }
-      })
-      return newBody;
-    },
     viewHistory() {
       if (!this.selected) {
         return
@@ -681,6 +647,7 @@ export default {
                 }
                 target.responseBody = utils.formatJson(data.data)
                 bus.$emit('switch-tab', 'result')
+                bus.$emit('update-response-body-definition', target.responseBodyDefinition);
                 bus.$emit('update-response-body', target.responseBody)
                 target.ext.eventSource.close();
               } else if (data.code === contants.RESPONSE_CODE_DEBUG) {
@@ -717,6 +684,7 @@ export default {
                     filename = decodeURIComponent(disposition.substring(disposition.indexOf('filename=') + 9))
                   }
                   target.responseBody = utils.formatJson({filename})
+                  bus.$emit('update-response-body-definition', target.responseBodyDefinition);
                   bus.$emit('update-response-body', target.responseBody)
                   let a = document.createElement('a')
                   a.download = filename
@@ -733,6 +701,7 @@ export default {
                   // 图片
                   this.imageUrl = `data:${contentType};base64,${data.data}`
                   this.showImageDialog = true
+                  bus.$emit('update-response-body-definition', target.responseBodyDefinition);
                   target.responseBody = utils.formatJson(data.data)
                   bus.$emit('update-response-body', target.responseBody)
                   bus.$emit('report', 'output_image')
@@ -742,6 +711,7 @@ export default {
                     content: '您没有权限执行测试'
                   })
                 } else {
+                  bus.$emit('update-response-body-definition', target.responseBodyDefinition);
                   target.responseBody = utils.formatJson(data.data)
                   bus.$emit('update-response-body', target.responseBody)
                 }
@@ -758,6 +728,7 @@ export default {
               // })
               try {
                 target.ext.eventSource.close();
+                bus.$emit('update-response-body-definition', target.responseBodyDefinition);
                 target.responseBody = utils.formatJson(res.data)
                 bus.$emit('update-response-body', target.responseBody)
               } catch (ignored) {

+ 2 - 0
magic-editor/src/console/src/components/layout/magic-options.vue

@@ -76,7 +76,9 @@ export default {
           this.selectedTab = this.tabs[0].id
         }
         this.$nextTick(() => {
+          bus.$emit('update-request-body-definition', info.requestBodyDefinition);
           bus.$emit('update-request-body', info.requestBody);
+          bus.$emit('update-response-body-definition', info.responseBodyDefinition);
           bus.$emit('update-response-body', info.responseBody);
         })
       } else {

+ 15 - 71
magic-editor/src/console/src/components/layout/magic-request.vue

@@ -158,7 +158,7 @@
               <div ref="bodyEditor" class="ma-body-editor"></div>
             </div>
             <div style="flex: 1;">
-              <magic-json :jsonData="requestBody" :forceUpdate="forceUpdate" :height="layoutHeight"></magic-json>
+              <magic-json :jsonData="requestBody || []" :forceUpdate="forceUpdate" :height="layoutHeight" type="request"></magic-json>
             </div>
           </div>
 
@@ -244,89 +244,29 @@
         bodyIndex: 0,
         requestBody: [],
         forceUpdate: false,
-        editorJson: '{\n\t\n}',
-        bodyEditorFlag: false,
         layoutHeight: '255px'
       }
     },
     watch: {
       requestBody: {
-        handler(newVal, oldVal) {
-          if (this.bodyEditorFlag) {
-            this.info.requestBody = JSON.stringify(newVal[0])
-          }
+        handler(requestBodyArr) {
+          this.info.requestBodyDefinition = requestBodyArr[0]
         },
         deep: true
       }
     },
     mounted() {
-      let that = this;
-      bus.$on('update-request-body', (newVal) => {
-       // console.log('update-request-body');
-        that.initRequestBodyDom()
-        if (!newVal || newVal == null) {
-          that.bodyEditorFlag = false
-          that.requestBody = []
-          that.bodyEditor && that.bodyEditor.setValue('{\r\n\t\r\n}')
-          return
-        }
-        try {
-          let body = JSON.parse(newVal)
-          if (body.dataType && body.children) {
-            that.requestBody = [body]
-            that.buildBodyEditorData(body)
-          } else {
-            /**
-             * 旧的json结构,不能直接用,需通过editor转换
-             */
-            that.editorJson = formatJson(newVal)
-            that.bodyEditor && that.bodyEditor.setValue(that.editorJson)
-          }
-
-        } catch (e) {
-          // console.log(e);
-        }
-
+      bus.$on('update-request-body', (requestBody) => {
+        this.initRequestBodyDom()
+        this.bodyEditor && this.bodyEditor.setValue(formatJson(requestBody) || requestBody || '{\r\n\t\r\n}')
       })
 
+      bus.$on('update-request-body-definition', (requestBodyDefinition) => {
+        this.requestBody = requestBodyDefinition ? [requestBodyDefinition] : []
+      })
     },
 
     methods: {
-      buildBodyEditorData(o) {
-        let requestBody = o
-        let newBody = {}
-        if ('Object' === requestBody.dataType) {
-          let body = {}
-          newBody = this.createJsonData(body, requestBody.children)
-        } else if ('Array' === requestBody.dataType) {
-          let body = []
-          newBody = this.createJsonData(body, requestBody.children, true)
-        }
-        // console.log('buildBodyEditorData', newBody);
-        this.editorJson = formatJson(JSON.stringify(newBody))
-        this.bodyEditor && this.bodyEditor.setValue(this.editorJson)
-      },
-      createJsonData(newBody, data, arrayFlag = false) {
-        data.map(item => {
-          let key, value = item.value;
-          if (!arrayFlag) {
-            key = item.name
-          }
-          if ('Object' === item.dataType) {
-            value = {}
-            newBody[key] = this.createJsonData(value, item.children)
-          } else if ('Array' === item.dataType) {
-            value = []
-            newBody[key] = this.createJsonData(value, item.children, true)
-          } else {
-            newBody[key] = (value === 'null' || value === 'undefined') ? null : value
-          }
-          if (arrayFlag) {
-            newBody.push(value)
-          }
-        })
-        return newBody;
-      },
       layout() {
         this.$nextTick(() => {
           if (this.bodyEditor && isVisible(this.$refs.bodyEditor)) {
@@ -402,13 +342,14 @@
             wordWrap: 'on',
             lineDecorationsWidth: 35,
             theme: store.get('skin') || 'default',
-            value: this.editorJson || '{\r\n\t\r\n}'
+            value: formatJson(this.info.requestBody) || '{\r\n\t\r\n}'
           })
           this.layout()
           this.bodyEditor.onDidChangeModelContent(() => {
-            this.bodyEditorFlag = true
             this.updateRequestBody(this.bodyEditor.getValue())
+            this.info.requestBody = this.bodyEditor.getValue()
           })
+          this.bodyEditor && this.bodyEditor.setValue(formatJson(this.info.requestBody) || '{\r\n\t\r\n}')
           bus.$on('update-window-size', () => this.layout())
         }
       },
@@ -493,6 +434,9 @@
         return "String";
       },
       valueCopy(newBody, oldBody, arrayFlag = false) {
+        if (oldBody.length == 0) {
+          return newBody
+        }
         let that = this;
         newBody.map(item => {
           let oldItemArr = oldBody.filter(old => {

+ 141 - 3
magic-editor/src/console/src/components/layout/magic-run.vue

@@ -1,23 +1,45 @@
 <template>
-  <div class="ma-run">
-    <div ref="resultEditor"></div>
+  <div class="ma-run" style="display: flex; flex-direction: row;">
+    <div style="width: 40%">
+      <div ref="resultEditor" class="ma-body-editor"></div>
+    </div>
+    <div style="flex: 1;">
+      <magic-json :jsonData="responseBody || []" :forceUpdate="forceUpdate" :height="layoutHeight" type="response"></magic-json>
+    </div>
+
   </div>
 </template>
 
 <script>
+
+  import MagicJson from '@/components/common/magic-json.vue'
 import bus from '@/scripts/bus.js'
 import * as monaco from 'monaco-editor'
 import store from '@/scripts/store.js'
-import {isVisible} from '@/scripts/utils.js'
+import {isVisible, deepClone} from '@/scripts/utils.js'
 
 export default {
   name: 'MagicRun',
   props: {
     info: Object,
   },
+  components: {
+    MagicJson
+  },
   data() {
     return {
       resultEditor: null,
+      responseBody: [],
+      forceUpdate: false,
+      layoutHeight: '255px'
+    }
+  },
+  watch: {
+    responseBody: {
+      handler(responseBodyArr) {
+        this.info.responseBodyDefinition = responseBodyArr[0]
+      },
+      deep: true
     }
   },
   updated() {
@@ -27,6 +49,10 @@ export default {
     this.createEditor()
     bus.$on('update-response-body', (responseBody) => {
       this.resultEditor && this.resultEditor.setValue(responseBody || '')
+      this.updateResponseBody(responseBody)
+    })
+    bus.$on('update-response-body-definition', (responseBodyDefinition) => {
+      this.responseBody = responseBodyDefinition ? [responseBodyDefinition] : []
     })
   },
   methods: {
@@ -50,6 +76,9 @@ export default {
       this.$nextTick(() => {
         if (this.resultEditor && isVisible(this.$refs.resultEditor)) {
           this.resultEditor.layout()
+          if (this.$refs.resultEditor && this.$refs.resultEditor.firstChild) {
+            this.layoutHeight = this.$refs.resultEditor.firstChild.style.height
+          }
         }
       })
     },
@@ -59,6 +88,111 @@ export default {
       }
       this.layout()
     },
+    updateResponseBody(bodyStr) {
+      if (['{}','[]'].indexOf(bodyStr.replace(/\s/g,"")) > -1) {
+        this.responseBody = []
+        return false
+      }
+      try {
+        let body = JSON.parse(bodyStr)
+        let reqBody = []
+        reqBody.push({
+          name: '',
+          value: '',
+          dataType: this.getType(body),
+          validateType: '',
+          expression: '',
+          error: '',
+          description: '',
+          children: this.processBody(body, 0),
+          level: 0,
+          selected: this.responseBody.length <= 0
+        })
+
+        this.responseBody = this.valueCopy(reqBody, this.responseBody)
+        this.forceUpdate = !this.forceUpdate;
+      } catch (e) {
+        // console.error(e)
+      }
+    },
+    processBody(body, level) {
+      let arr = [], that = this
+      Object.keys(body).forEach((key) => {
+        let param = {
+          name: 'Array' !== this.getType(body) ? key : '',
+          value: 'Object' !== that.getType(body[key]) && 'Array' !== that.getType(body[key]) ? body[key] : '',
+          dataType: this.getType(body[key]),
+          validateType: '',
+          expression: '',
+          error: '',
+          description: '',
+          children: [],
+          level: level + 1,
+          selected: false
+        }
+        if ('Object' === that.getType(body[key]) || 'Array' === that.getType(body[key])) {
+          param.children = that.processBody(body[key], level + 1);
+        }
+        arr.push(param)
+
+      })
+      return arr;
+    },
+    getType(object) {
+      if (Object.prototype.toString.call(object) === '[object Number]') {
+        if(parseInt(object) !== parseFloat(object)) {
+          return "Float";
+        }
+        // if (object >= -128 && object <= 127) {
+        //   return "Byte";
+        // }
+        // if (object >= -32768 && object <= 32767) {
+        //   return "Short";
+        // }
+        if (object >= -2147483648 && object <= 2147483647) {
+          return "Integer";
+        }
+        return "Long";
+      }
+      if (Object.prototype.toString.call(object) === '[object String]') {
+        return "String";
+      }
+      if (Object.prototype.toString.call(object) === '[object Boolean]') {
+        return "Boolean";
+      }
+      if (Object.prototype.toString.call(object) === '[object Array]') {
+        return "Array";
+      }
+      if (Object.prototype.toString.call(object) === '[object Object]') {
+        return "Object";
+      }
+      return "String";
+    },
+    valueCopy(newBody, oldBody, arrayFlag = false) {
+      if (oldBody.length == 0) {
+        return newBody
+      }
+      let that = this;
+      newBody.map(item => {
+        let oldItemArr = oldBody.filter(old => {
+          if (old.level === 0 || arrayFlag) {
+            return old
+          }
+          return old.name === item.name
+        })
+        if (oldItemArr.length > 0) {
+          if (item.dataType === 'Object' || item.dataType === 'Array') {
+            item.children = that.valueCopy(item.children, oldItemArr[0].children, item.dataType === 'Array')
+          } else {
+            item.expression = oldItemArr[0].expression
+          }
+          item.name = oldItemArr[0].name
+          item.description = oldItemArr[0].description
+        }
+
+      })
+      return deepClone(newBody);
+    }
   },
   destroyed() {
     if (this.resultEditor) {
@@ -80,4 +214,8 @@ div.ma-run > div {
   width: 100%;
   height: 100%;
 }
+.ma-body-editor {
+  width: 100%;
+  height: 100%;
+}
 </style>