feat(接口测试): 接口测试文档增加高级参数展开项

--story=1009220 --user=宋天阳 【接口测试】文档优化
https://www.tapd.cn/55049933/s/1223428
This commit is contained in:
song-tianyang 2022-08-15 13:44:24 +08:00 committed by 建国
parent 1de3d68e09
commit 491c5276c5
13 changed files with 931 additions and 66 deletions

View File

@ -145,7 +145,7 @@ public class ShareInfoService {
JSONArray headArr = requestObj.getJSONArray("headers"); JSONArray headArr = requestObj.getJSONArray("headers");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj != null && headObj.containsKey("name") && headObj.containsKey("value")) { if (this.isObjectHasKey(headObj, "name")) {
requestHeadDataArr.add(headObj); requestHeadDataArr.add(headObj);
} }
} }
@ -157,9 +157,8 @@ public class ShareInfoService {
try { try {
JSONArray headArr = requestObj.getJSONArray("arguments"); JSONArray headArr = requestObj.getJSONArray("arguments");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name") && headObj.containsKey("value")) { if (this.isObjectHasKey(headObj, "name")) {
urlParamArr.add(headObj); urlParamArr.add(headObj);
} }
} }
@ -175,7 +174,7 @@ public class ShareInfoService {
JSONArray headArr = requestObj.getJSONArray("rest"); JSONArray headArr = requestObj.getJSONArray("rest");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name")) { if (this.isObjectHasKey(headObj, "name")) {
restParamArr.add(headObj); restParamArr.add(headObj);
} }
} }
@ -189,7 +188,7 @@ public class ShareInfoService {
if (requestObj.containsKey("body")) { if (requestObj.containsKey("body")) {
try { try {
JSONObject bodyObj = requestObj.getJSONObject("body"); JSONObject bodyObj = requestObj.getJSONObject("body");
if (bodyObj.containsKey("type")) { if (this.isObjectHasKey(bodyObj, "type")) {
String type = bodyObj.getString("type"); String type = bodyObj.getString("type");
if (StringUtils.equals(type, "WWW_FORM")) { if (StringUtils.equals(type, "WWW_FORM")) {
apiInfoDTO.setRequestBodyParamType("x-www-from-urlencoded"); apiInfoDTO.setRequestBodyParamType("x-www-from-urlencoded");
@ -237,7 +236,7 @@ public class ShareInfoService {
Map<String, String> previewObjMap = new LinkedHashMap<>(); Map<String, String> previewObjMap = new LinkedHashMap<>();
for (int i = 0; i < kvsArr.size(); i++) { for (int i = 0; i < kvsArr.size(); i++) {
JSONObject kv = kvsArr.getJSONObject(i); JSONObject kv = kvsArr.getJSONObject(i);
if (kv.containsKey("name")) { if (this.isObjectHasKey(kv, "name")) {
String value = ""; String value = "";
if (kv.containsKey("value")) { if (kv.containsKey("value")) {
value = String.valueOf(kv.get("value")); value = String.valueOf(kv.get("value"));
@ -257,14 +256,14 @@ public class ShareInfoService {
Map<String, String> previewObjMap = new LinkedHashMap<>(); Map<String, String> previewObjMap = new LinkedHashMap<>();
for (int i = 0; i < kvsArr.size(); i++) { for (int i = 0; i < kvsArr.size(); i++) {
JSONObject kv = kvsArr.getJSONObject(i); JSONObject kv = kvsArr.getJSONObject(i);
if (kv.containsKey("description") && kv.containsKey("files")) { if (this.isObjectHasKey(kv, "description") && this.isObjectHasKey(kv, "files")) {
Map<String, String> bodyMap = new HashMap<>(); Map<String, String> bodyMap = new HashMap<>();
String name = kv.getString("description"); String name = kv.getString("description");
JSONArray fileArr = kv.getJSONArray("files"); JSONArray fileArr = kv.getJSONArray("files");
String value = ""; String value = "";
for (int j = 0; j < fileArr.size(); j++) { for (int j = 0; j < fileArr.size(); j++) {
JSONObject fileObj = fileArr.getJSONObject(j); JSONObject fileObj = fileArr.getJSONObject(j);
if (fileObj.containsKey("name")) { if (this.isObjectHasKey(fileObj, "name")) {
value += fileObj.getString("name") + " ;"; value += fileObj.getString("name") + " ;";
} }
} }
@ -292,13 +291,13 @@ public class ShareInfoService {
//赋值响应头 //赋值响应头
if (apiModel.getResponse() != null) { if (apiModel.getResponse() != null) {
JSONObject responseJsonObj = this.genJSONObject(apiModel.getResponse()); JSONObject responseJsonObj = this.genJSONObject(apiModel.getResponse());
if (responseJsonObj != null && responseJsonObj.containsKey("headers")) { if (this.isObjectHasKey(responseJsonObj, "headers")) {
try { try {
JSONArray responseHeadDataArr = new JSONArray(); JSONArray responseHeadDataArr = new JSONArray();
JSONArray headArr = responseJsonObj.getJSONArray("headers"); JSONArray headArr = responseJsonObj.getJSONArray("headers");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name") && headObj.containsKey("value")) { if (this.isObjectHasKey(headObj, "name")) {
responseHeadDataArr.add(headObj); responseHeadDataArr.add(headObj);
} }
} }
@ -308,10 +307,10 @@ public class ShareInfoService {
} }
} }
// 赋值响应体 // 赋值响应体
if (responseJsonObj != null && responseJsonObj.containsKey("body")) { if (this.isObjectHasKey(responseJsonObj, "body")) {
try { try {
JSONObject bodyObj = responseJsonObj.getJSONObject("body"); JSONObject bodyObj = responseJsonObj.getJSONObject("body");
if (bodyObj.containsKey("type")) { if (this.isObjectHasKey(bodyObj, "type")) {
String type = bodyObj.getString("type"); String type = bodyObj.getString("type");
if (StringUtils.equals(type, "WWW_FORM")) { if (StringUtils.equals(type, "WWW_FORM")) {
apiInfoDTO.setResponseBodyParamType("x-www-from-urlencoded"); apiInfoDTO.setResponseBodyParamType("x-www-from-urlencoded");
@ -345,7 +344,7 @@ public class ShareInfoService {
JSONArray kvsArr = bodyObj.getJSONArray("kvs"); JSONArray kvsArr = bodyObj.getJSONArray("kvs");
for (int i = 0; i < kvsArr.size(); i++) { for (int i = 0; i < kvsArr.size(); i++) {
JSONObject kv = kvsArr.getJSONObject(i); JSONObject kv = kvsArr.getJSONObject(i);
if (kv.containsKey("name")) { if (this.isObjectHasKey(kv, "name")) {
bodyParamArr.add(kv); bodyParamArr.add(kv);
} }
} }
@ -365,7 +364,7 @@ public class ShareInfoService {
String value = ""; String value = "";
for (int j = 0; j < fileArr.size(); j++) { for (int j = 0; j < fileArr.size(); j++) {
JSONObject fileObj = fileArr.getJSONObject(j); JSONObject fileObj = fileArr.getJSONObject(j);
if (fileObj.containsKey("name")) { if (this.isObjectHasKey(fileObj, "name")) {
value += fileObj.getString("name") + " ;"; value += fileObj.getString("name") + " ;";
} }
} }
@ -384,13 +383,13 @@ public class ShareInfoService {
} }
// 赋值响应码 // 赋值响应码
if (responseJsonObj != null && responseJsonObj.containsKey("statusCode")) { if (this.isObjectHasKey(responseJsonObj, "statusCode")) {
try { try {
JSONArray responseStatusDataArr = new JSONArray(); JSONArray responseStatusDataArr = new JSONArray();
JSONArray statusArr = responseJsonObj.getJSONArray("statusCode"); JSONArray statusArr = responseJsonObj.getJSONArray("statusCode");
for (int index = 0; index < statusArr.size(); index++) { for (int index = 0; index < statusArr.size(); index++) {
JSONObject statusObj = statusArr.getJSONObject(index); JSONObject statusObj = statusArr.getJSONObject(index);
if (statusObj.containsKey("name") && statusObj.containsKey("value")) { if (this.isObjectHasKey(statusObj, "name")) {
responseStatusDataArr.add(statusObj); responseStatusDataArr.add(statusObj);
} }
} }
@ -633,10 +632,7 @@ public class ShareInfoService {
return planId; return planId;
} }
public void scenarioReportValidate(String shareId, String reportId) { private boolean isObjectHasKey(JSONObject object, String key) {
TestPlanApiScenario testPlanApiScenario = testPlanScenarioCaseService.selectByReportId(reportId); return object != null && object.containsKey(key);
if (!StringUtils.equals(getPlanId(shareId), testPlanApiScenario.getTestPlanId())) {
MSException.throwException("validate failure!");
}
} }
} }

View File

@ -553,11 +553,6 @@ export default {
margin-left: 10px; margin-left: 10px;
} }
.blackFontClass {
font-weight: bold;
font-size: 14px;
}
.smallFontClass { .smallFontClass {
font-size: 13px; font-size: 13px;
margin: 20px 5px; margin: 20px 5px;

View File

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<el-row class="apiInfoRow"> <el-row class="apiInfoRow">
<div class="blackFontClass"> <div>
<el-row> <el-row>
<div class="tip" style="float: left"> <div class="tip" style="float: left">
<span>{{ title }}</span> <span>{{ title }}</span>
@ -30,18 +30,30 @@
<br/> <br/>
</div> </div>
</div> </div>
<div v-else-if="getJsonArr(stringData).length===0"> <div v-else-if="tableData.length===0">
<div class="simpleFontClass" style="margin-top: 10px"> <div class="simpleFontClass" style="margin-top: 10px">
{{ $t('api_test.definition.document.data_set.none') }} {{ $t('api_test.definition.document.data_set.none') }}
</div> </div>
</div> </div>
<div v-else> <div v-else>
<el-table border :show-header="true" <el-table border :show-header="true" row-key="id"
:data="getJsonArr(stringData)" class="test-content document-table"> :data="tableData" :class="getTableClass()" ref="expandTable">
<el-table-column v-for="item in tableColoumArr" :key="item.id" <el-table-column v-for="item in tableColoumArr" :key="item.id"
:prop="item.prop" :prop="item.prop"
:label="item.label" :label="item.label"
show-overflow-tooltip/> show-overflow-tooltip>
</el-table-column>
<el-table-column type="expand" v-if="tableCanExpand" :label="getCollapseOption()"
width="80px">
<template slot="header">
<el-button type="text" size="mini" @click="expandAllRows">
{{ expandAllRow ? $t("commons.close_all") : $t("commons.expand_all") }}
</el-button>
</template>
<template v-slot:default="scope">
<table-advanced-setting :table-data="scope.row"></table-advanced-setting>
</template>
</el-table-column>
</el-table> </el-table>
</div> </div>
</div> </div>
@ -56,12 +68,20 @@
<script> <script>
import {getCurrentUser, getUUID} from "@/common/js/utils";
import tableAdvancedSetting
from "@/business/components/api/definition/components/document/components/plugin/TableAdvancedSetting";
export default { export default {
name: "ApiInfoCollapse", name: "ApiInfoCollapse",
components: {}, components: {tableAdvancedSetting},
data() { data() {
return { return {
active: true, active: true,
expandAllRow: false,
language: "zh_CN",
tableData: [],
expandTitle: this.$t("commons.expand_all"),
tableColoumArr: [ tableColoumArr: [
{id: 1, prop: "name", label: this.$t('api_test.definition.document.table_coloum.name')}, {id: 1, prop: "name", label: this.$t('api_test.definition.document.table_coloum.name')},
{id: 2, prop: "isRequired", label: this.$t('api_test.definition.document.table_coloum.is_required')}, {id: 2, prop: "isRequired", label: this.$t('api_test.definition.document.table_coloum.is_required')},
@ -76,6 +96,12 @@ export default {
remarks: String, remarks: String,
isRequest: Boolean, isRequest: Boolean,
isResponse: Boolean, isResponse: Boolean,
tableCanExpand: {
type: Boolean,
default() {
return true;
}
},
isText: Boolean, isText: Boolean,
stringData: { stringData: {
type: String, type: String,
@ -87,6 +113,11 @@ export default {
activated() { activated() {
}, },
created: function () { created: function () {
//language zh_CN/zh_TW/en_US
let user = getCurrentUser();
if (user) {
this.language = user.language;
}
}, },
mounted() { mounted() {
}, },
@ -95,13 +126,51 @@ export default {
return this.isRequest || this.isResponse; return this.isRequest || this.isResponse;
} }
}, },
watch: {}, watch: {
stringData() {
this.tableData = this.getJsonArr(this.stringData);
},
expandAllRow() {
if (this.$refs.expandTable) {
let expand = this.expandAllRow;
if (this.tableData) {
this.$nextTick(() => {
this.tableData.forEach(i => {
this.$refs.expandTable.toggleRowExpansion(i, expand)
});
this.$refs.expandTable.doLayout();
})
}
}
this.expandTitle = this.expandAllRow ? this.$t("commons.close_all") : this.$t("commons.expand_all");
}
},
methods: { methods: {
getTableClass() {
if (this.language === "zh_TW") {
return "test-content document-table tw-table";
} else if (this.language === "en_US") {
return "test-content document-table us-table";
} else {
return "test-content document-table cn-table";
}
},
getCollapseOption() {
if (this.expandAllRow) {
return this.$t('api_test.definition.document.close');
} else {
return this.$t('api_test.definition.document.open');
}
},
expandAllRows() {
this.expandAllRow = !this.expandAllRow;
},
getJsonArr(jsonString) { getJsonArr(jsonString) {
let returnJsonArr = []; let returnJsonArr = [];
if (jsonString === '无' || jsonString === null) { if (jsonString === '无' || jsonString === null) {
return returnJsonArr; return returnJsonArr;
} }
try {
let jsonArr = JSON.parse(jsonString); let jsonArr = JSON.parse(jsonString);
// //
for (var index = 0; index < jsonArr.length; index++) { for (var index = 0; index < jsonArr.length; index++) {
@ -112,9 +181,13 @@ export default {
} else { } else {
item.isRequired = "false"; item.isRequired = "false";
} }
item.id = getUUID();
returnJsonArr.push(item); returnJsonArr.push(item);
} }
} }
} catch (e) {
returnJsonArr = [];
}
return returnJsonArr; return returnJsonArr;
}, },
changeActive() { changeActive() {
@ -164,11 +237,61 @@ export default {
border-right: 0px solid #EBEEF5 border-right: 0px solid #EBEEF5
} }
/*修改展开按钮时不旋转*/
.document-table /deep/ .el-table__expand-icon {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
.document-table /deep/ .el-table__expanded-cell {
padding: 5px;
}
.document-table /deep/ .el-icon-arrow-right {
position: unset;
}
.document-table /deep/ th { .document-table /deep/ th {
background-color: #FAFAFA; background-color: #FAFAFA;
border-right: 0px solid #EBEEF5 border-right: 0px solid #EBEEF5
} }
/*展开按钮未点击的样式是加号带边框*/
.cn-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "展开";
padding: 2px;
}
/*展开按钮点击后的样式是减号带边框*/
.cn-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "收起";
}
.tw-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "展開";
padding: 2px;
}
.tw-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "收起";
}
.us-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "Open";
padding: 2px;
}
.us-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "Close";
padding: 2px;
}
.right-button { .right-button {
float: right; float: right;
} }

View File

@ -41,7 +41,8 @@
</div> </div>
</el-row> </el-row>
<!--api请求头--> <!--api请求头-->
<api-info-collapse table-coloum-type="nameAndValue" :title="$t('api_test.definition.document.request_head')" <api-info-collapse :table-can-expand="false" table-coloum-type="nameAndValue"
:title="$t('api_test.definition.document.request_head')"
:string-data="apiInfo.requestHead"/> :string-data="apiInfo.requestHead"/>
<!--QUERY参数--> <!--QUERY参数-->
<api-info-collapse table-coloum-type="simple" :title="'QUERY'+$t('api_test.definition.document.request_param')" <api-info-collapse table-coloum-type="simple" :title="'QUERY'+$t('api_test.definition.document.request_param')"
@ -56,7 +57,8 @@
</api-info-collapse> </api-info-collapse>
<!--响应头--> <!--响应头-->
<api-info-collapse table-coloum-type="nameAndValue" :title="$t('api_test.definition.document.response_head')" <api-info-collapse :table-can-expand="false" table-coloum-type="nameAndValue"
:title="$t('api_test.definition.document.response_head')"
:string-data="apiInfo.responseHead"/> :string-data="apiInfo.responseHead"/>
<!--响应体--> <!--响应体-->
<api-info-collapse :is-response="true" :remarks="apiInfo.responseBodyParamType" <api-info-collapse :is-response="true" :remarks="apiInfo.responseBodyParamType"
@ -65,7 +67,7 @@
</api-info-collapse> </api-info-collapse>
<!--响应状态码--> <!--响应状态码-->
<api-info-collapse :is-text="true" :string-data="getName(apiInfo.responseCode)" <api-info-collapse :table-can-expand="false" :is-text="true" :string-data="getName(apiInfo.responseCode)"
:title="$t('api_test.definition.document.response_code')"/> :title="$t('api_test.definition.document.response_code')"/>
<!-- 备注 --> <!-- 备注 -->
<api-remark-show :data="apiInfo.remark"></api-remark-show> <api-remark-show :data="apiInfo.remark"></api-remark-show>

View File

@ -2,9 +2,8 @@
<div> <div>
<el-row class="apiInfoRow"> <el-row class="apiInfoRow">
<div> <div>
<el-table border v-if="formParamTypes.includes(apiInfo.requestBodyParamType)" <el-table border v-if="formParamTypes.includes(apiInfo.requestBodyParamType)" :show-header="true" row-key="id"
:data="getJsonArr(apiInfo.requestBodyFormData)" :data="tableData" :class="getTableClass()" ref="expandTable">
class="test-content document-table">
<el-table-column prop="name" <el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')" :label="$t('api_test.definition.document.table_coloum.name')"
min-width="120px" min-width="120px"
@ -26,9 +25,20 @@
:label="$t('api_test.definition.document.table_coloum.default_value')" :label="$t('api_test.definition.document.table_coloum.default_value')"
min-width="120px" min-width="120px"
show-overflow-tooltip/> show-overflow-tooltip/>
<el-table-column type="expand" :label="getCollapseOption()" width="80px">
<template slot="header">
<el-button type="text" size="mini" @click="expandAllRows">
{{ expandAllRow ? $t("commons.close_all") : $t("commons.expand_all") }}
</el-button>
</template>
<template v-slot:default="scope">
<table-advanced-setting :table-data="scope.row"></table-advanced-setting>
</template>
</el-table-column>
</el-table> </el-table>
<div v-else-if="apiInfo.requestBodyParamType === 'JSON-SCHEMA' || apiInfo.requestBodyParamType === 'JSON'" style="margin-left: 10px"> <div v-else-if="apiInfo.requestBodyParamType === 'JSON-SCHEMA' || apiInfo.requestBodyParamType === 'JSON'"
<ms-json-code-edit :show-preview="false" :json-schema-disable="true" :body="apiInfo.jsonSchemaBody" style="margin-left: 10px">
<json-schema-show :show-preview="false" :json-schema-disable="true" :body="apiInfo.jsonSchemaBody"
ref="jsonCodeEdit"/> ref="jsonCodeEdit"/>
</div> </div>
<div v-else class="showDataDiv"> <div v-else class="showDataDiv">
@ -58,15 +68,21 @@
<script> <script>
import {formatJson} from "@/common/js/format-utils"; import JsonSchemaShow
import MsJsonCodeEdit from "@/business/components/common/json-schema/JsonSchemaEditor"; from "@/business/components/api/definition/components/document/components/JsonSchema/JsonSchemaShow";
import tableAdvancedSetting
from "@/business/components/api/definition/components/document/components/plugin/TableAdvancedSetting";
import {getCurrentUser, getUUID} from "@/common/js/utils";
export default { export default {
name: "ApiRequestInfo", name: "ApiRequestInfo",
components: {MsJsonCodeEdit}, components: {JsonSchemaShow, tableAdvancedSetting},
data() { data() {
return { return {
tableData: [],
language: "zh_CN",
active: true, active: true,
expandAllRow: false,
formParamTypes: ['form-data', 'x-www-from-urlencoded', 'BINARY'], formParamTypes: ['form-data', 'x-www-from-urlencoded', 'BINARY'],
}; };
}, },
@ -76,13 +92,60 @@ export default {
activated() { activated() {
}, },
created: function () { created: function () {
if (this.apiInfo && this.apiInfo.requestBodyFormData) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
}
//language zh_CN/zh_TW/en_US
let user = getCurrentUser();
if (user) {
this.language = user.language;
}
}, },
mounted() { mounted() {
}, },
computed: { computed: {},
watch: {
'apiInfo.requestBodyFormData': {
handler(v) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
},
deep: true
},
expandAllRow() {
if (this.$refs.expandTable) {
let expand = this.expandAllRow;
if (this.tableData) {
this.$nextTick(() => {
this.tableData.forEach(i => {
this.$refs.expandTable.toggleRowExpansion(i, expand)
});
this.$refs.expandTable.doLayout();
})
}
}
this.expandTitle = this.expandAllRow ? this.$t("commons.close_all") : this.$t("commons.expand_all");
}
}, },
watch: {},
methods: { methods: {
getTableClass() {
if (this.language === "zh_TW") {
return "test-content document-table tw-table";
} else if (this.language === "en_US") {
return "test-content document-table us-table";
} else {
return "test-content document-table cn-table";
}
},
expandAllRows() {
this.expandAllRow = !this.expandAllRow;
},
getCollapseOption() {
if (this.expandAllRow) {
return this.$t('api_test.definition.document.close');
} else {
return this.$t('api_test.definition.document.open');
}
},
formatBoolean(row, column, cellValue) { formatBoolean(row, column, cellValue) {
var ret = ''; // var ret = ''; //
if (cellValue) { if (cellValue) {
@ -110,6 +173,7 @@ export default {
for (var index = 0; index < jsonArr.length; index++) { for (var index = 0; index < jsonArr.length; index++) {
var item = jsonArr[index]; var item = jsonArr[index];
if (item.name !== "" && item.name !== null) { if (item.name !== "" && item.name !== null) {
item.id = getUUID();
returnJsonArr.push(item); returnJsonArr.push(item);
} }
} }
@ -148,14 +212,91 @@ export default {
.apiInfoRow { .apiInfoRow {
margin: 10px 10px; margin: 10px 10px;
} }
.blackFontClass { .blackFontClass {
font-weight: bold; font-weight: bold;
font-size: 14px; font-size: 14px;
} }
.showDataDiv { .showDataDiv {
background-color: #F5F7F9; background-color: #F5F7F9;
margin: 10px 10px; margin: 10px 10px;
max-height: 300px; max-height: 300px;
overflow: auto; overflow: auto;
} }
.document-table {
margin: 10px 0px 10px 10px;
width: auto;
}
.document-table /deep/ .el-table__row {
font-size: 12px;
font-weight: initial;
}
.document-table /deep/ .has-gutter {
font-size: 12px;
color: #404040;
}
.document-table /deep/ td {
border-right: 0px solid #EBEEF5
}
/*修改展开按钮时不旋转*/
.document-table /deep/ .el-table__expand-icon {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
.document-table /deep/ .el-table__expanded-cell {
padding: 5px;
}
.document-table /deep/ .el-icon-arrow-right {
position: unset;
}
.document-table /deep/ th {
background-color: #FAFAFA;
border-right: 0px solid #EBEEF5
}
/*展开按钮未点击的样式是加号带边框*/
.cn-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "展开";
padding: 2px;
}
/*展开按钮点击后的样式是减号带边框*/
.cn-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "收起";
}
.tw-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "展開";
padding: 2px;
}
.tw-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "收起";
}
.us-table /deep/ .el-table__expand-icon .el-icon-arrow-right:before {
position: unset;
content: "Open";
padding: 2px;
}
.us-table /deep/ .el-table__expand-icon--expanded .el-icon-arrow-right:before {
position: unset;
content: "Close";
padding: 2px;
}
</style> </style>

View File

@ -28,10 +28,12 @@
show-overflow-tooltip/> show-overflow-tooltip/>
</el-table> </el-table>
<div v-else-if="apiInfo.responseBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px"> <div v-else-if="apiInfo.responseBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px">
<ms-json-code-edit :json-schema-disable="true" :show-preview="false" :body="apiInfo.jsonSchemaResponseBody" ref="jsonCodeEdit"/> <ms-json-code-edit :json-schema-disable="true" :show-preview="false" :body="apiInfo.jsonSchemaResponseBody"
ref="jsonCodeEdit"/>
</div> </div>
<div v-else-if="formatRowDataToJsonSchema(apiInfo,'response') " style="margin-left: 10px"> <div v-else-if="formatRowDataToJsonSchema(apiInfo,'response') " style="margin-left: 10px">
<ms-json-code-edit :json-schema-disable="true" :show-preview="false" :body="apiInfo.responseJsonSchema" ref="jsonCodeEdit"/> <ms-json-code-edit :json-schema-disable="true" :show-preview="false" :body="apiInfo.responseJsonSchema"
ref="jsonCodeEdit"/>
</div> </div>
<div v-else class="showDataDiv"> <div v-else class="showDataDiv">
<br/> <br/>
@ -134,11 +136,6 @@ export default {
margin: 10px 10px; margin: 10px 10px;
} }
.blackFontClass {
font-weight: bold;
font-size: 14px;
}
.showDataDiv { .showDataDiv {
background-color: #F5F7F9; background-color: #F5F7F9;
margin: 10px 10px; margin: 10px 10px;

View File

@ -0,0 +1,349 @@
<template>
<div class="json-schema-editor">
<el-row class="row" :gutter="20">
<el-col :span="8" class="ms-col-name">
<div :style="{marginLeft:`${10*deep}px`}" class="ms-col-name-c"/>
<span v-if="pickValue.type === 'object' || pickValue.type === 'array'" :class="hidden ? 'el-icon-caret-left ms-transform':
'el-icon-caret-bottom'" @click="hidden = !hidden"/>
<span v-else style="width:10px;display:inline-block"></span>
<input class="el-input el-input__inner" style="height: 32px" :disabled="disabled || root" :value="pickKey"
@blur="onInputName" size="small"/>
</el-col>
<el-col :span="4">
<el-select v-model="pickValue.type" :disabled="disabled || disabledType" class="ms-col-type"
@change="onChangeType" size="small">
<el-option :key="t" :value="t" :label="t" v-for="t in types"/>
</el-select>
</el-col>
<el-col :span="5">
<ms-mock
:disabled="disabled || pickValue.type === 'object' || pickKey === 'root' || pickValue.type === 'array' || pickValue.type === 'null'"
:schema="pickValue"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
@editScenarioAdvance="editScenarioAdvance"/>
</el-col>
<el-col :span="5">
<el-input :disabled="disabled" v-model="pickValue.description" class="ms-col-title"
:placeholder="$t('schema.description')" size="small"/>
</el-col>
<el-col :span="2">
<div v-if="hasAdvancedSetting">
<el-link @click="changeCollapseStatus">{{ getCollapseOption() }}</el-link>
</div>
</el-col>
</el-row>
<div>
<el-collapse-transition>
<div v-show="collapseStatus" :style="{marginLeft:`${10*deep+10}px`}">
<json-advanced-setting :json-data=" pickValue"/>
</div>
</el-collapse-transition>
</div>
<template v-if="!hidden && pickValue.properties && !isArray && reloadItemOver">
<json-schema-panel v-for="(item,key,index) in pickValue.properties" :value="{[key]:item}"
:parent="pickValue" :key="index" :deep="deep + 1" :root="false" class="children"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:disabled="disabled"
:expand-all-params="expandAllParams"
@editScenarioAdvance="editScenarioAdvance"
:lang="lang" :custom="custom" @changeAllItemsType="changeAllItemsType"/>
</template>
<template v-if="!hidden && isArray && reloadItemOver">
<json-schema-panel v-for="(item,key,index) in pickValue.items" :value="{[key]:item}" :parent="pickValue"
:key="index"
:deep="deep+1" :root="false" class="children"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:expand-all-params="expandAllParams"
@editScenarioAdvance="editScenarioAdvance"
:lang="lang" :custom="custom" @changeAllItemsType="changeAllItemsType"/>
</template>
</div>
</template>
<script>
import {getUUID} from "@/common/js/utils";
import {TYPE, TYPE_NAME, TYPES} from "@/business/components/common/json-schema/schema/editor/type/type";
import MsMock from "@/business/components/common/json-schema/schema/editor/mock/MockComplete";
import JsonAdvancedSetting
from "@/business/components/api/definition/components/document/components/plugin/JsonAdvancedSetting";
export default {
name: 'JsonSchemaPanel',
components: {MsMock, JsonAdvancedSetting},
props: {
value: {
type: Object,
required: true
},
showMockVars: {
type: Boolean,
default() {
return false;
}
},
disabled: { //namename,name
type: Boolean,
default: false
},
disabledType: { //
type: Boolean,
default: false
},
isItem: { //
type: Boolean,
default: false
},
deep: { // deep=0
type: Number,
default: 0
},
root: { //root
type: Boolean,
default: true
},
parent: { //
type: Object,
default: null
},
custom: { //enable custom properties
type: Boolean,
default: false
},
lang: { // i18n language
type: String,
default: 'zh_CN'
},
expandAllParams: {
type: Boolean,
default() {
return false;
}
},
scenarioDefinition: Array,
},
computed: {
pickValue() {
return Object.values(this.value)[0]
},
pickKey() {
return Object.keys(this.value)[0]
},
isObject() {
return this.pickValue.type === 'object'
},
isArray() {
return this.pickValue.type === 'array'
},
checked() {
return this.parent && this.parent.required && this.parent.required.indexOf(this.pickKey) >= 0
},
advanced() {
return TYPE[this.pickValue.type]
},
types() {
return TYPES(this.pickKey);
},
hasAdvancedSetting() {
if (this.isNotEmptyValue(this.pickValue["default"]) || this.isNotEmptyValue(this.pickValue["minLength"]) || this.isNotEmptyValue(this.pickValue["maxLength"])
|| this.isNotEmptyValue(this.pickValue["format"]) || this.isNotEmptyValue(this.pickValue["description"]) || this.isNotEmptyValue(this.pickValue["pattern"])
|| this.isNotEmptyValue(this.pickValue["enum"])) {
return true;
} else {
return false;
}
},
},
data() {
return {
TYPE_NAME,
collapseStatus: false,
hidden: false,
countAdd: 1,
reloadItemOver: true,
advancedValue: {},
addProp: {},//
customing: false
}
},
created() {
if (this.pickValue) {
if (this.pickValue.hidden === undefined) {
this.hidden = this.root ? false : true;
} else {
this.hidden = this.root ? false : this.pickValue.hidden;
}
} else {
this.hidden = true;
}
},
watch: {
expandAllParams() {
this.collapseStatus = this.expandAllParams;
}
},
methods: {
isNotEmptyValue(value) {
return value && value !== '';
},
changeCollapseStatus() {
this.collapseStatus = !this.collapseStatus;
},
getCollapseOption() {
if (this.collapseStatus) {
return this.$t('api_test.definition.document.close');
} else {
return this.$t('api_test.definition.document.open');
}
},
onInputName(e) {
const val = e.target.value
const p = {};
for (let key in this.parent.properties) {
if (key != this.pickKey) {
p[key] = this.parent.properties[key]
} else {
p[val] = this.parent.properties[key]
delete this.parent.properties[key]
}
}
this.$set(this.parent, 'properties', p)
},
onChangeType() {
if (this.parent && this.parent.type === 'array') {
this.$emit('changeAllItemsType', this.pickValue.type);
} else {
this.$delete(this.pickValue, 'properties')
this.$delete(this.pickValue, 'items')
this.$delete(this.pickValue, 'required')
this.$delete(this.pickValue, 'mock')
if (this.isArray) {
this.$set(this.pickValue, 'items', [{type: 'string', mock: {mock: ""}}]);
}
}
},
changeAllItemsType(changeType) {
if (this.isArray && this.pickValue.items && this.pickValue.items.length > 0) {
this.pickValue.items.forEach(item => {
item.type = changeType;
this.$delete(item, 'properties')
this.$delete(item, 'items')
this.$delete(item, 'required')
this.$delete(item, 'mock')
if (changeType === 'array') {
this.$set(item, 'items', [{type: 'string', mock: {mock: ""}}]);
}
});
}
},
onRootCheck(e) {
const checked = e.target.checked
this._deepCheck(checked, this.pickValue)
},
_deepCheck(checked, node) {
if (node.type === 'object' && node.properties) {
checked ? this.$set(node, 'required', Object.keys(node.properties)) : this.$delete(node, 'required')
Object.keys(node.properties).forEach(key => this._deepCheck(checked, node.properties[key]))
} else if (node.type === 'array' && node.items.type === 'object') {
checked ? this.$set(node.items, 'required', Object.keys(node.items.properties)) : this.$delete(node.items, 'required')
Object.keys(node.items.properties).forEach(key => this._deepCheck(checked, node.items.properties[key]))
}
},
confirmAddCustomNode() {
this.customProps.push(this.addProp)
this.addProp = {}
this.customing = false
},
_joinName() {
return `feild_${this.deep}_${this.countAdd++}_${getUUID().substring(0, 5)}`
},
editScenarioAdvance(data) {
this.$emit('editScenarioAdvance', data);
},
}
}
</script>
<style scoped>
.json-schema-editor .row {
display: flex;
margin: 12px;
}
.json-schema-editor .row .ms-col-name {
display: flex;
align-items: center;
}
.json-schema-editor .row .ms-col-name .ms-col-name-c {
display: flex;
align-items: center;
}
.json-schema-editor .row .ms-col-name .ms-col-name-required {
flex: 0 0 30px;
text-align: center;
}
.json-schema-editor .row .ms-col-type {
width: 100%;
}
.json-schema-editor .row .ms-col-setting {
display: inline-block;
}
.json-schema-editor .row .setting-icon {
color: rgba(0, 0, 0, 0.45);
border: none;
}
.json-schema-editor .row .plus-icon {
border: none;
}
.json-schema-editor .row .close-icon {
color: #888;
border: none;
}
.json-schema-editor-advanced-modal {
color: rgba(0, 0, 0, 0.65);
min-width: 600px;
}
.json-schema-editor-advanced-modal pre {
font-family: monospace;
height: 100%;
overflow-y: auto;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: 12px;
width: 50%;
}
.json-schema-editor-advanced-modal h3 {
display: block;
border-left: 3px solid #1890ff;
padding: 0 8px;
}
.json-schema-editor-advanced-modal .ms-advanced-search-form {
display: flex;
}
.json-schema-editor-advanced-modal .ms-advanced-search-form .ms-form-item .ms-form-item-control-wrapper {
flex: 1;
}
.col-item-setting {
padding-top: 8px;
cursor: pointer;
}
.ms-transform {
transform: rotate(-180deg);
transition: 0ms;
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<div id="app" v-loading="loading">
<el-row>
<el-col>
<el-button style="float: right" type="text" size="mini" @click="expandAll">
{{ expandTitle }}
</el-button>
</el-col>
</el-row>
<div :style="jsonSchemaDisable ? '' : 'min-height: 200px'">
<json-schema-panel class="schema"
:disabled="jsonSchemaDisable"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
:expand-all-params="expandAllParams"
@editScenarioAdvance="editScenarioAdvance"
lang="zh_CN" custom/>
</div>
</div>
</template>
<script>
import JsonSchemaPanel
from "@/business/components/api/definition/components/document/components/JsonSchema/JsonSchemaPanel";
const Convert = require('../../../../../../common/json-schema/convert/convert.js');
const MsConvert = new Convert();
export default {
name: 'JsonSchemaShow',
components: {JsonSchemaPanel},
props: {
body: {},
showPreview: {
type: Boolean,
default: true
},
jsonSchemaDisable: {
type: Boolean,
default: false
},
showMockVars: {
type: Boolean,
default() {
return false;
}
},
scenarioDefinition: Array,
},
created() {
if (!this.body.jsonSchema && this.body.raw && this.checkIsJson(this.body.raw)) {
let obj = {"root": MsConvert.format(JSON.parse(this.body.raw))}
this.schema = obj;
} else if (this.body.jsonSchema) {
this.schema = {"root": this.body.jsonSchema};
}
this.body.jsonSchema = this.schema.root;
},
watch: {
schema: {
handler(newValue, oldValue) {
this.body.jsonSchema = this.schema.root;
},
deep: true
},
body: {
handler(newValue, oldValue) {
if (!this.body.jsonSchema && this.body.raw && this.checkIsJson(this.body.raw)) {
let obj = {"root": MsConvert.format(JSON.parse(this.body.raw))}
this.schema = obj;
} else if (this.body.jsonSchema) {
this.schema = {"root": this.body.jsonSchema};
}
this.body.jsonSchema = this.schema.root;
},
deep: true
}
},
data() {
return {
schema:
{
"root": {
"type": "object",
"properties": {},
}
},
loading: false,
expandAllParams: false,
}
},
computed: {
expandTitle() {
return this.expandAllParams ? this.$t("commons.close_all") : this.$t("commons.expand_all");
}
},
methods: {
expandAll() {
this.expandAllParams = !this.expandAllParams;
},
checkIsJson(json) {
try {
JSON.parse(json);
return true;
} catch (e) {
return false;
}
},
jsonData(data) {
this.schema.root = {};
this.$nextTick(() => {
this.schema.root = data;
this.body.jsonSchema = this.schema.root;
})
},
editScenarioAdvance(data) {
this.$emit('editScenarioAdvance', data);
},
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,69 @@
<template>
<div v-if="jsonData" style="border-width: 1px; border-style: ridge;">
<div style="margin: 0px 10px 10px 10px">
<el-form label-position="left" v-model="advancedValue">
<div :span="8" v-for="(item,key) in advancedValue" :key="key">
<el-form-item v-if="isNotEmptyValue(jsonData[key])" :label="$t('schema.'+key)+' : '" style="margin: 0">
{{ jsonData[key] }}
</el-form-item>
</div>
</el-form>
</div>
</div>
</template>
<script>
import {TYPE} from "@/business/components/common/json-schema/schema/editor/type/type";
import {isNull} from "@/business/components/common/json-schema/schema/editor/util";
export default {
name: "JsonAdvancedSetting",
props: {
jsonData: Object
},
data() {
return {
advancedValue: {},
customProps: [],
}
},
computed: {
advanced() {
return TYPE[this.jsonData.type]
},
advancedAttr() {
return TYPE[this.jsonData.type].attr
},
advancedNotEmptyValue() {
const jsonNode = Object.assign({}, this.advancedValue);
for (let key in jsonNode) {
isNull(jsonNode[key]) && delete jsonNode[key]
}
return jsonNode
},
completeNodeValue() {
const t = {}
for (const item of this.customProps) {
t[item.key] = item.value
}
return Object.assign({}, this.jsonData, this.advancedNotEmptyValue, t)
}
},
created() {
this.advancedValue = {};
this.advancedValue = this.advanced.value
for (const k in this.advancedValue) {
this.advancedValue[k] = this.jsonData[k]
}
},
methods: {
isNotEmptyValue(value) {
return value && value !== '';
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,62 @@
<template>
<div v-if="advancedValue" style="border-width: 1px; border-style: ridge;">
<div style="margin: 0px 10px 10px 10px">
<el-form label-position="left" v-model="advancedValue">
<div :span="8" v-for="(item, key) in advancedValue" :key="key">
<el-form-item :label="$t(key) + ' : '" style="margin: 0">
{{ advancedValue[key] }}
</el-form-item>
</div>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: "TableAdvancedSetting",
props: {
tableData: Object
},
data() {
return {
advancedValue: null,
customProps: [],
}
},
created() {
this.initAdvancedValue();
},
methods: {
isNotEmptyValue(value) {
return value && value !== '';
},
initAdvancedValue() {
if (this.tableData) {
this.advancedValue = {};
if (this.tableData["min"] && this.isNotEmptyValue(this.tableData["min"])) {
this.advancedValue["schema.minLength"] = this.tableData["min"];
}
if (this.tableData["max"] && this.isNotEmptyValue(this.tableData["max"])) {
this.advancedValue["schema.maxLength"] = this.tableData["max"];
}
if (this.tableData["urlEncode"]) {
this.advancedValue["commons.encode"] = this.$t("commons.yes");
}
if (this.tableData["description"] && this.isNotEmptyValue(this.tableData["description"])) {
this.advancedValue["commons.description"] = this.tableData["description"];
}
if (JSON.stringify(this.advancedValue) === "{}") {
this.advancedValue = null;
}
} else {
this.advancedValue = null;
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -7,6 +7,8 @@ export default {
trash: "Trash", trash: "Trash",
yes: "yes", yes: "yes",
no: "no", no: "no",
expand_all: "Expand all",
close_all: "Close all",
example: "Demo", example: "Demo",
subject: "Subject", subject: "Subject",
excelFile: "Excel", excelFile: "Excel",

View File

@ -7,6 +7,8 @@ export default {
trash: "回收站", trash: "回收站",
yes: "是", yes: "是",
no: "否", no: "否",
expand_all: "一键展开",
close_all: "一键收起",
example: "示例", example: "示例",
subject: "主题", subject: "主题",
excelFile: "表格文件.xls", excelFile: "表格文件.xls",

View File

@ -7,6 +7,8 @@ export default {
trash: "回收站", trash: "回收站",
yes: "是", yes: "是",
no: "否", no: "否",
expand_all: "一鍵展開",
close_all: "一鍵收起",
example: "示例", example: "示例",
subject: "主題", subject: "主題",
excelFile: "表格文件.xls", excelFile: "表格文件.xls",