refactor: 文档页面性能优化

文档页面性能优化
This commit is contained in:
song.tianyang 2021-03-21 20:44:24 +08:00
parent 787909a415
commit 362e0b3720
10 changed files with 231 additions and 824 deletions

View File

@ -12,6 +12,7 @@ import io.metersphere.base.domain.ApiDocumentShare;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
@ -35,6 +36,21 @@ public class ApiDocumentController {
return returnList;
}
@PostMapping("/selectApiInfoByParam")
public List<ApiDocumentInfoDTO> selectApiInfoByParam(@RequestBody ApiDocumentRequest request) {
List<ApiDocumentInfoDTO> returnList = new ArrayList<>();
List<ApiDefinitionWithBLOBs> apiModels = apiDefinitionService.getBLOBs(request.getApiIdList());
for (ApiDefinitionWithBLOBs apiModel : apiModels) {
try{
ApiDocumentInfoDTO returnDTO = apiDocumentService.conversionModelToDTO(apiModel);
returnList.add(returnDTO);
}catch (Exception e){
e.printStackTrace();
}
}
return returnList;
}
@GetMapping("/selectApiInfoById/{id}")
public ApiDocumentInfoDTO selectApiInfoById(@PathVariable String id) {
ApiDefinitionWithBLOBs apiModel = apiDefinitionService.getBLOBs(id);

View File

@ -136,6 +136,16 @@ public class ApiDefinitionService {
public ApiDefinitionWithBLOBs getBLOBs(String id) {
return apiDefinitionMapper.selectByPrimaryKey(id);
}
public List<ApiDefinitionWithBLOBs> getBLOBs(List<String> idList) {
if(idList == null || idList.isEmpty()){
return new ArrayList<>(0);
}else{
ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(idList);
example.setOrderByClause("create_time DESC ");
return apiDefinitionMapper.selectByExampleWithBLOBs(example);
}
}
public void create(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());

View File

@ -1,24 +1,15 @@
package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.document.ApiDocumentInfoDTO;
import io.metersphere.api.dto.document.ApiDocumentRequest;
import io.metersphere.api.dto.document.ApiDocumentShareRequest;
import io.metersphere.api.dto.document.ApiDocumentSimpleInfoDTO;
import io.metersphere.api.dto.document.ApiDocumentShareType;
import io.metersphere.api.dto.document.ApiDocumentShareDTO;
import io.metersphere.api.dto.document.*;
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
import io.metersphere.base.domain.ApiDocumentShare;
import io.metersphere.base.domain.ApiDocumentShareExample;
import io.metersphere.base.mapper.ApiDocumentShareMapper;
import io.metersphere.base.mapper.ext.ExtApiDocumentMapper;
import io.metersphere.base.mapper.ext.ExtApiDocumentShareMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.service.SystemParameterService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@ -226,7 +217,7 @@ public class ApiDocumentService {
//赋值响应头
if (apiModel.getResponse() != null) {
JSONObject responseJsonObj = JSONObject.parseObject(apiModel.getResponse());
if (responseJsonObj.containsKey("headers")) {
if (responseJsonObj!=null && responseJsonObj.containsKey("headers")) {
JSONArray responseHeadDataArr = new JSONArray();
JSONArray headArr = responseJsonObj.getJSONArray("headers");
for (int index = 0; index < headArr.size(); index++) {
@ -238,7 +229,7 @@ public class ApiDocumentService {
apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString());
}
// 赋值响应体
if (responseJsonObj.containsKey("body")) {
if (responseJsonObj!=null && responseJsonObj.containsKey("body")) {
JSONObject bodyObj = responseJsonObj.getJSONObject("body");
if (bodyObj.containsKey("type")) {
String type = bodyObj.getString("type");
@ -295,7 +286,7 @@ public class ApiDocumentService {
}
}
// 赋值响应码
if (responseJsonObj.containsKey("statusCode")) {
if (responseJsonObj!=null && responseJsonObj.containsKey("statusCode")) {
JSONArray responseStatusDataArr = new JSONArray();
JSONArray statusArr = responseJsonObj.getJSONArray("statusCode");
for (int index = 0; index < statusArr.size(); index++) {

View File

@ -2,30 +2,28 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtApiDocumentMapper">
<select id="findApiDocumentSimpleInfoByRequest" resultType="io.metersphere.api.dto.document.ApiDocumentInfoDTO">
SELECT api.id,api.name FROM Api_definition api
<where>
<if test="request.projectId != null">
AND api.project_Id = #{request.projectId}
</if>
<if test="request.name != null and request.name != '' ">
AND api.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.type != null and request.type != '' and request.type != 'ALL' ">
AND api.method = #{request.type}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.apiIdList != null and request.apiIdList.size() > 0">
AND api.id in
<foreach collection="request.apiIdList" item="apiId" separator="," open="(" close=")">
#{apiId}
</foreach>
</if>
</where>
SELECT api.id,api.name FROM Api_definition api WHERE api.protocol = 'HTTP' AND api.status != 'Trash'
<if test="request.projectId != null">
AND api.project_Id = #{request.projectId}
</if>
<if test="request.name != null and request.name != '' ">
AND api.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.type != null and request.type != '' and request.type != 'ALL' ">
AND api.method = #{request.type}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.apiIdList != null and request.apiIdList.size() > 0">
AND api.id in
<foreach collection="request.apiIdList" item="apiId" separator="," open="(" close=")">
#{apiId}
</foreach>
</if>
<if test="request.orderCondition == 'createTimeDesc'">
ORDER BY api.create_time DESC
</if>

View File

@ -22,4 +22,9 @@ public class IndexController {
return "redirect:/";
}
}
@GetMapping(value = "/document")
public String document() {
return "document:/";
}
}

View File

@ -29,7 +29,7 @@
</el-row>
<el-divider></el-divider>
<div ref="apiDocInfoDiv" @scroll="handleScroll" >
<div v-for="(apiInfo) in apiInfoArray" :key="apiInfo.id" ref="apiDocInfoDivItem">
<div v-for="(apiInfo) in apiShowArray" :key="apiInfo.id" ref="apiDocInfoDivItem">
<div style="font-size: 17px">
<el-popover
v-if="projectId"
@ -283,12 +283,13 @@ import ApiStatus from "@/business/components/api/definition/components/list/ApiS
import {calculate} from "@/business/components/api/definition/model/ApiTestModel";
import MsJsonCodeEdit from "@/business/components/common/json-schema/JsonSchemaEditor";
import Api from "@/business/components/api/router";
import {uuid} from "@/common/js/utils";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const apiDocumentBatchShare = (requireComponent!=null&&requireComponent.keys().length) > 0 ? requireComponent("./share/ApiDocumentBatchShare.vue") : {};
export default {
name: "ApiDocumentItem",
name: "ApiDocumentAnchor",
components: {
Api,
MsJsonCodeEdit,
@ -301,7 +302,6 @@ export default {
batchShareUrl:"",
apiStepIndex: 0,
showXpackCompnent:false,
apiShowArray: [],
apiInfoArray: [],
modes: ['text', 'json', 'xml', 'html'],
formParamTypes: ['form-data', 'x-www-from-urlencoded', 'BINARY'],
@ -332,7 +332,11 @@ export default {
responseCode: "无",
},
methodColorMap: new Map(API_METHOD_COLOUR),
clientHeight: '',//
clientHeight: '',//,
maxCompnentSize : 5, //api
apiShowArray:[],//api
needAsyncSelect: false, //apimaxCompnentSizetrue
currentApiIndexInApiShowArray: 0,//apiapiShowArray
}
},
props: {
@ -414,26 +418,24 @@ export default {
simpleRequest.moduleIds = this.moduleIds;
}
let simpleInfoUrl = "/document/selectApiSimpleInfo";
let simpleInfoUrl = "/api/document/selectApiSimpleInfo";
this.apiInfoArray = [];
this.$post(simpleInfoUrl, simpleRequest, response => {
this.apiInfoArray = response.data;
this.apiStepIndex = 0;
if(response.data.length > 7){
this.apiShowArray = [
response.data[0],
response.data[1],
response.data[2],
response.data[3],
response.data[4],
response.data[5],
response.data[6],
];
}else{
this.apiShowArray = response.data;
}
if (this.apiInfoArray.length > 0) {
this.checkApiInfoNode(this.apiStepIndex);
this.checkApiInfoNode(this.apiStepIndex,true);
}
//body
// for(let dataIndex = 0; dataIndex < this.maxCompnentSize; dataIndex ++){
// if(dataIndex < response.data.length){
// this.apiShowArray.push(response.data[dataIndex]);
// }
// }
if(response.data.length > this.maxCompnentSize){
this.needAsyncSelect = true;
}else{
this.needAsyncSelect = false;
}
});
},
@ -458,7 +460,7 @@ export default {
genShareInfoParam.shareApiIdList = shareIdArr;
genShareInfoParam.shareType = shareType;
this.$post("/document/generateApiDocumentShareInfo", genShareInfoParam, res => {
this.$post("/api/document/generateApiDocumentShareInfo", genShareInfoParam, res => {
if(shareType == "Batch"){
this.batchShareUrl = thisHost+"/document"+res.data.shareUrl;
}else{
@ -468,12 +470,34 @@ export default {
});
},
selectApiInfo(index,apiId) {
let simpleInfoUrl = "/document/selectApiInfoById/" + apiId;
let simpleInfoUrl = "/api/document/selectApiInfoById/" + apiId;
this.$get(simpleInfoUrl, response => {
let returnData = response.data;
this.$set(this.apiInfoArray,index,returnData);
});
},
//itemIndex,afterNodeIndex,beforeNodeIndex showArray isRedirectScroll:
selectApiInfoBatch(indexArr,apiIdArr,itemIndex,afterNodeIndex,beforeNodeIndex,isRedirectScroll) {
if(indexArr.length != apiIdArr.length){
return;
}else {
let params = {};
params.apiIdList = apiIdArr;
this.$post("/api/document/selectApiInfoByParam", params, response => {
let returnDatas = response.data;
for(let dataIndex = 0; dataIndex < returnDatas.length;dataIndex ++){
let index = indexArr[dataIndex];
let data = returnDatas[dataIndex];
this.$set(this.apiInfoArray,index,data);
}
this.updateShowArray(itemIndex,afterNodeIndex,beforeNodeIndex);
if(isRedirectScroll){
this.redirectScroll();
}
});
}
},
clickStep(apiId) {
for (let index = 0; index < this.apiInfoArray.length; index++) {
if (apiId == this.apiInfoArray[index].id) {
@ -482,9 +506,7 @@ export default {
}
}
//
this.checkApiInfoNode(this.apiStepIndex);
//
this.redirectScroll(this.apiStepIndex);
this.checkApiInfoNode(this.apiStepIndex,true);
},
stepClick(stepIndex) {
this.apiStepIndex = stepIndex;
@ -575,14 +597,12 @@ export default {
this.$message.error(this.$t('api_report.error'));
},
handleScroll(){
//apiDocInfoDiv(item+20)
let apiDocDivScrollTop = this.$refs.apiDocInfoDiv.scrollTop;
let apiDocDivClientTop = this.$refs.apiDocInfoDiv.clientHeight;
let scrolledHeigh = apiDocDivScrollTop+apiDocDivClientTop;
let lastIndex = 0;
for (let index = 0; index < this.apiInfoArray.length; index++) {
for (let index = 0; index < this.apiShowArray.length; index++) {
//. : +-index(20px)>0 index
if(scrolledHeigh>0){
lastIndex = index;
@ -592,40 +612,82 @@ export default {
break;
}
}
this.apiStepIndex = lastIndex;
// 3
this.checkApiInfoNode(this.apiStepIndex);
let names = "";
for(let i = 0;i<this.apiShowArray.length;i++){
names += this.apiShowArray[i].name+";";
}
console.log("["+apiDocDivScrollTop+":"+this.apiStepIndex+"]:["+lastIndex+":"+this.currentApiIndexInApiShowArray+"]-------->"+names);
if(lastIndex < this.currentApiIndexInApiShowArray){
//
if(this.needAsyncSelect){
//apiShowArray 2
// apiStepIndex-1- 2 < apiInfoArray
let dataIndex = this.apiStepIndex -3;
if(dataIndex >= 0){
let apiInfo = this.apiInfoArray[dataIndex];
this.apiShowArray.unshift(apiInfo);
}else{
this.currentApiIndexInApiShowArray--;
}
if(this.apiShowArray.length > (this.currentApiIndexInApiShowArray+3)){
this.apiShowArray.pop();
}
}
this.apiStepIndex --;
}else if(lastIndex > this.currentApiIndexInApiShowArray){
//
if(this.needAsyncSelect){
//apiShowArray 2
// apiStepIndex+1+ 2 < apiInfoArray
let dataIndex = this.apiStepIndex +3;
if(dataIndex < this.apiInfoArray.length){
let apiInfo = this.apiInfoArray[dataIndex];
this.apiShowArray.push(apiInfo);
}
if(this.apiShowArray.length <= this.maxCompnentSize){
//currentApiIndexInApiShowArray
this.currentApiIndexInApiShowArray++;
}else{
this.apiShowArray.shift();
let itemHeight = this.$refs.apiDocInfoDivItem[0].offsetHeight+20;
this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivScrollTop-itemHeight);
}
}
this.apiStepIndex ++;
}
// this.apiStepIndex = lastIndex;
// // 2
// this.checkApiInfoNode(this.apiStepIndex);
},
redirectScroll(itemIndex){
redirectScroll(){
//api
// let apiDocDivClientTop = this.$refs.apiDocInfoDiv.clientHeight;
let apiDocDivClientTop = 0;
let itemHeightCount = 0;
for (let i = 0; i <= itemIndex-1; i++) {
let itemHeight = this.$refs.apiDocInfoDivItem[i].offsetHeight+20;
itemHeightCount+=itemHeight;
if(this.currentApiIndexInApiShowArray > 0){
for (let i = 0; i <= this.currentApiIndexInApiShowArray-1; i++) {
let itemHeight = this.$refs.apiDocInfoDivItem[i].offsetHeight+20;
itemHeightCount+=itemHeight;
}
}
this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivClientTop+itemHeightCount);
},
checkApiInfoNode(itemIndex){
//api3
let beforeNodeIndex = itemIndex<3?0:(itemIndex-3);
let afterNodeIndex = (itemIndex+3)<this.apiInfoArray.length?(itemIndex+3):this.apiInfoArray.length;
for(let beforeIndex = itemIndex;beforeIndex < afterNodeIndex;beforeIndex++){
let apiInfo = this.apiInfoArray[beforeIndex];
if(apiInfo==null){
continue;
}
if(apiInfo == null || !apiInfo.selectedFlag){
let apiId = apiInfo.id;
if(!apiInfo.isSearching){
apiInfo.isSearching = true;
this.selectApiInfo(beforeIndex,apiId);
}
}
}
//api2showArray
//isRedirectScroll
checkApiInfoNode(itemIndex,isRedirectScroll){
let beforeNodeIndex = itemIndex<2?0:(itemIndex-2);
let afterNodeIndex = (itemIndex+2)<this.apiInfoArray.length?(itemIndex+2):this.apiInfoArray.length;
this.apiShowArray = [];
let selectIndexArr = [];
let selectApiId = [];
//
for(let afterIndex = beforeNodeIndex;afterIndex <itemIndex;afterIndex++){
let apiInfo = this.apiInfoArray[afterIndex];
if(apiInfo==null){
@ -635,9 +697,57 @@ export default {
let apiId = apiInfo.id;
if(!apiInfo.isSearching) {
apiInfo.isSearching = true;
this.selectApiInfo(afterIndex,apiId);
selectIndexArr.push(afterIndex);
selectApiId.push(apiId);
}
}
this.apiShowArray.push(apiInfo);
}
this.currentApiIndexInApiShowArray = this.apiShowArray.length;
//
for(let beforeIndex = itemIndex;beforeIndex <= afterNodeIndex;beforeIndex++){
let apiInfo = this.apiInfoArray[beforeIndex];
if(apiInfo==null){
continue;
}
if(apiInfo == null || !apiInfo.selectedFlag){
let apiId = apiInfo.id;
if(!apiInfo.isSearching){
apiInfo.isSearching = true;
selectIndexArr.push(beforeIndex);
selectApiId.push(apiId);
}
}
this.apiShowArray.push(apiInfo);
}
if(selectIndexArr.length>0){
this.selectApiInfoBatch(selectIndexArr,selectApiId,itemIndex,afterNodeIndex,beforeNodeIndex,isRedirectScroll);
}else{
if(isRedirectScroll){
//
this.redirectScroll();
}
}
},
// apiShowArray
updateShowArray(itemIndex,afterNodeIndex,beforeNodeIndex){
this.apiShowArray = [];
//
for(let afterIndex = beforeNodeIndex;afterIndex <itemIndex;afterIndex++){
let apiInfo = this.apiInfoArray[afterIndex];
if(apiInfo==null){
continue;
}
this.apiShowArray.push(apiInfo);
}
this.currentApiIndexInApiShowArray = this.apiShowArray.length;
//
for(let beforeIndex = itemIndex;beforeIndex <= afterNodeIndex;beforeIndex++){
let apiInfo = this.apiInfoArray[beforeIndex];
if(apiInfo==null){
continue;
}
this.apiShowArray.push(apiInfo);
}
}
},
@ -679,6 +789,8 @@ export default {
.showDataDiv {
background-color: #F5F7F9;
margin: 20px 10px;
max-height: 300px;
overflow: auto;
}
/*

View File

@ -1,720 +0,0 @@
<template>
<div>
<el-container>
<el-main style="padding-top: 0px;padding-bottom: 0px">
<el-row style="margin-top: 10px">
<el-select size="small" :placeholder="$t('api_test.definition.document.order')" v-model="apiSearch.orderCondition" style="float: right;width: 180px;margin-right: 5px"
class="ms-api-header-select" @change="initApiDocSimpleList" clearable>
<el-option key="createTimeDesc" :label="$t('api_test.definition.document.create_time_sort')" value="createTimeDesc" />
<el-option key="editTimeAsc" :label="$t('api_test.definition.document.edit_time_positive_sequence')" value="editTimeAsc"/>
<el-option key="editTimeDesc" :label="$t('api_test.definition.document.edit_time_Reverse_order')" value="editTimeDesc"/>
</el-select>
<el-select size="small" :placeholder="$t('api_test.definition.document.request_method')" v-model="apiSearch.type" style="float: right;width: 180px;margin-right: 5px"
class="ms-api-header-select" @change="initApiDocSimpleList" clearable>
<el-option key="ALL" :label="$t('api_test.definition.document.data_set.all')" value="ALL"/>
<el-option key="GET" :label="'GET '+$t('api_test.definition.document.request_interface')" value="GET"/>
<el-option key="POST" :label="'POST '+$t('api_test.definition.document.request_interface')" value="POST"/>
<el-option key="PUT" :label="'PUT '+$t('api_test.definition.document.request_interface')" value="PUT"/>
<el-option key="DELETE" :label="'DELETE '+$t('api_test.definition.document.request_interface')" value="DELETE"/>
<el-option key="PATCH" :label="'PATCH '+$t('api_test.definition.document.request_interface')" value="PATCH"/>
<el-option key="OPTIONS" :label="'OPTIONS '+$t('api_test.definition.document.request_interface')" value="OPTIONS"/>
<el-option key="HEAD" :label="'HEAD '+$t('api_test.definition.document.request_interface')" value="HEAD"/>
<el-option key="CONNECT" :label="'CONNECT '+$t('api_test.definition.document.request_interface')" value="CONNECT"/>
</el-select>
<el-input :placeholder="$t('api_test.definition.document.search_by_api_name')" @blur="initApiDocSimpleList()" style="float: right;width: 180px;margin-right: 5px" size="small"
@keyup.enter.native="initApiDocSimpleList()" v-model="apiSearch.name"/>
<api-document-batch-share v-xpack v-if="showXpackCompnent" @shareApiDocument="shareApiDocument" :project-id="projectId" :share-url="batchShareUrl" style="float: right;margin: 6px;font-size: 17px"/>
<!-- <api-document-batch-share v-xpack v-if="showXpackCompnent"/>-->
</el-row>
<el-divider></el-divider>
<div ref="apiDocInfoDiv" @scroll="handleScroll" >
<div v-for="(apiInfo) in apiInfoArray" :key="apiInfo.id" ref="apiDocInfoDivItem">
<div style="font-size: 17px">
<el-popover
v-if="projectId"
placement="right"
width="260"
@show="shareApiDocument('false')">
<p>{{shareUrl}}</p>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}</el-button>
</div>
<i class="el-icon-share" slot="reference" style="margin-right: 10px;cursor: pointer"></i>
</el-popover>
{{ apiInfo.name }}
<span class="apiStatusTag">
<api-status :value="apiInfo.status"/>
</span>
</div>
<!--api请求信息-->
<el-row class="apiInfoRow">
<div class="tip">
{{ $t('api_test.definition.document.request_info') }}
</div>
</el-row>
<el-row class="apiInfoRow">
<div class="simpleFontClass">
<el-tag size="medium"
:style="{'background-color': getColor(true,apiInfo.method), border: getColor(true,apiInfo.method),borderRadius:'0px', marginRight:'20px',color:'white'}">
{{ apiInfo.method }}
</el-tag>
{{ apiInfo.uri }}
</div>
</el-row>
<!--api请求头-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.request_head') }}
<div v-if="getJsonArr(apiInfo.requestHead).length==0">
<div class="simpleFontClass" style="margin-top: 10px">
{{ $t('api_test.definition.document.data_set.none') }}
</div>
</div>
<div v-else>
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.requestHead)" row-key="name" class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.value')"
show-overflow-tooltip/>
</el-table>
</div>
</div>
</el-row>
<!--URL参数-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
URL{{ $t('api_test.definition.document.request_param') }}
<div v-if="getJsonArr(apiInfo.urlParams).length==0">
<div class="simpleFontClass" style="margin-top: 10px">
{{ $t('api_test.definition.document.data_set.none') }}
</div>
</div>
<div v-else>
<el-table border
:data="getJsonArr(apiInfo.urlParams)" row-key="name" class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="isEnable"
:label="$t('api_test.definition.document.table_coloum.is_required')"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.value')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="description"
:label="$t('api_test.definition.document.table_coloum.desc')"
min-width="280px"
show-overflow-tooltip/>
</el-table>
</div>
</div>
</el-row>
<!--api请求体 以及表格-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.request_body') }}
</div>
<div class="smallFontClass">
{{ $t('api_test.definition.document.table_coloum.type') }}:{{ apiInfo.requestBodyParamType }}
</div>
<div>
<el-table border v-if="formParamTypes.includes(apiInfo.requestBodyParamType)"
:data="getJsonArr(apiInfo.requestBodyFormData)" row-key="name"
class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="contentType"
:label="$t('api_test.definition.document.table_coloum.type')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="description"
:label="$t('api_test.definition.document.table_coloum.desc')"
min-width="280px"
show-overflow-tooltip/>
<el-table-column prop="required"
:label="$t('api_test.definition.document.table_coloum.is_required')"
:formatter="formatBoolean"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.default_value')"
min-width="120px"
show-overflow-tooltip/>
</el-table>
<div v-else-if="apiInfo.requestBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px">
<ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/>
</div>
<div v-else class="showDataDiv">
<br/>
<p style="margin: 0px 20px;"
v-html="formatRowData(apiInfo.requestBodyParamType,apiInfo.requestBodyStrutureData)">
</p>
<br/>
</div>
</div>
</el-row>
<!--范例展示-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.example_presentation') }}
</div>
<div class="showDataDiv">
<br/>
<p style="margin: 0px 20px;"
v-html="genPreviewData(apiInfo.requestPreviewData)">
</p>
<br/>
</div>
</el-row>
<!--响应信息-->
<el-row class="apiInfoRow">
<div class="tip">
{{ $t('api_test.definition.document.response_info') }}
</div>
</el-row>
<el-row class="apiInfoRow">
</el-row>
<!--响应头-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.response_head') }}:
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.responseHead)" row-key="name" class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.value')"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
<!--响应体-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.response_body') }}
</div>
<div class="smallFontClass">
{{ $t('api_test.definition.document.table_coloum.type') }}:{{ apiInfo.responseBodyParamType }}
</div>
<div>
<el-table border v-if="formParamTypes.includes(apiInfo.responseBodyParamType)"
:data="getJsonArr(apiInfo.responseBodyFormData)" row-key="id"
class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="contentType"
:label="$t('api_test.definition.document.table_coloum.type')"
min-width="120px"
show-overflow-tooltip/>
<el-table-column prop="description"
:label="$t('api_test.definition.document.table_coloum.desc')"
min-width="280px"
show-overflow-tooltip/>
<el-table-column prop="required"
:label="$t('api_test.definition.document.table_coloum.is_required')"
:formatter="formatBoolean"
min-width="80px"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.default_value')"
min-width="120px"
show-overflow-tooltip/>
</el-table>
<div v-else class="showDataDiv">
<br/>
<p style="margin: 0px 20px;"
v-html="formatRowData(apiInfo.responseBodyParamType,apiInfo.responseBodyStrutureData)">
</p>
<br/>
</div>
</div>
</el-row>
<!--响应状态码-->
<el-row class="apiInfoRow">
<div class="blackFontClass">
{{ $t('api_test.definition.document.response_code') }}:
<el-table border :show-header="false"
:data="getJsonArr(apiInfo.responseCode)" row-key="name" class="test-content document-table">
<el-table-column prop="name"
:label="$t('api_test.definition.document.table_coloum.name')"
show-overflow-tooltip/>
<el-table-column prop="value"
:label="$t('api_test.definition.document.table_coloum.value')"
show-overflow-tooltip/>
</el-table>
</div>
</el-row>
</div>
</div>
</el-main>
<!-- 右侧列表 -->
<el-aside width="200px" style="margin-top: 70px;">
<div ref="apiDocList" >
<el-steps style="height: 40%" direction="vertical" :active="apiStepIndex">
<el-step v-for="(apiInfo) in apiInfoArray" :key="apiInfo.id" @click.native="clickStep(apiInfo.id)">
<el-link slot="title">{{ apiInfo.name }}</el-link>
</el-step>
</el-steps>
</div>
</el-aside>
</el-container>
</div>
</template>
<script>
import {API_METHOD_COLOUR} from "@/business/components/api/definition/model/JsonData";
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
import {formatJson,} from "@/common/js/format-utils";
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
import {calculate} from "@/business/components/api/definition/model/ApiTestModel";
import MsJsonCodeEdit from "@/business/components/common/json-schema/JsonSchemaEditor";
import Api from "@/business/components/api/router";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const apiDocumentBatchShare = (requireComponent!=null&&requireComponent.keys().length) > 0 ? requireComponent("./share/ApiDocumentBatchShare.vue") : {};
export default {
name: "ApiDocumentItem",
components: {
Api,
MsJsonCodeEdit,
ApiStatus, MsCodeEdit,
"ApiDocumentBatchShare": apiDocumentBatchShare.default
},
data() {
return {
shareUrl:"",
batchShareUrl:"",
apiStepIndex: 0,
showXpackCompnent:false,
apiInfoArray: [],
modes: ['text', 'json', 'xml', 'html'],
formParamTypes: ['form-data', 'x-www-from-urlencoded', 'BINARY'],
mockVariableFuncs: [],
apiSearch:{
name:"",
type:"ALL",
orderCondition:"createTimeDesc",
},
apiInfoBaseObj: {
selectedFlag:false,
method: "无",
uri: "无",
name: "无",
id: "",
requestHead: "无",
urlParams: "无",
requestBodyParamType: "无",
requestBodyFormData: '[]',
requestBodyStrutureData: "",
sharePopoverVisible:false,
jsonSchemaBody: {},
responseHead: "无",
responseBody: "",
responseBodyParamType: "无",
responseBodyFormData: "无",
responseBodyStrutureData: "无",
responseCode: "无",
},
methodColorMap: new Map(API_METHOD_COLOUR),
clientHeight: '',//
}
},
props: {
projectId: String,
documentId: String,
moduleIds: Array,
pageHeaderHeight:Number,
},
activated() {
this.initApiDocSimpleList();
this.clientHeight = `${document.documentElement.clientHeight}`;//
let that = this;
window.onresize = function () {
this.clientHeight = `${document.documentElement.clientHeight}`;
this.changeFixed(this.clientHeight);
}
},
created: function () {
if(requireComponent!=null && JSON.stringify(apiDocumentBatchShare) != '{}'){
this.showXpackCompnent = true;
}
this.initApiDocSimpleList();
this.clientHeight = `${document.documentElement.clientHeight}`;//
let that = this;
window.onresize = function () {
this.clientHeight = `${document.documentElement.clientHeight}`;
this.changeFixed(this.clientHeight);
};
window.addEventListener('scroll',that.handleScroll);
},
mounted() {
let that = this;
window.onresize = function () {
that.clientHeight = `${document.documentElement.clientHeight}`;
that.changeFixed(that.clientHeight);
};
// handleScroll
window.addEventListener('scroll',this.handleScroll);
},
computed: {
},
watch: {
moduleIds() {
this.initApiDocSimpleList();
},
clientHeight() { //clientHeight
this.changeFixed(this.clientHeight);
}
},
methods: {
formatRowData(dataType, data) {
var returnData = data;
if (data) {
returnData = data.replace(/\n/g, '<br>');
}
return returnData;
},
changeFixed(clientHeight) {
if (this.$refs.apiDocInfoDiv) {
let countPageHeight = 350;
if(this.pageHeaderHeight!=0 && this.pageHeaderHeight != null){
countPageHeight = this.pageHeaderHeight
}
this.$refs.apiDocInfoDiv.style.height = clientHeight - countPageHeight + 'px';
this.$refs.apiDocInfoDiv.style.overflow = 'auto';
this.$refs.apiDocList.style.height = clientHeight - countPageHeight + 'px';
}
},
initApiDocSimpleList() {
let simpleRequest = this.apiSearch;
if (this.projectId != null && this.projectId != "") {
simpleRequest.projectId = this.projectId;
}
if (this.documentId != null && this.documentId != "") {
simpleRequest.shareId = this.documentId;
}
if (this.moduleIds.length > 0) {
simpleRequest.moduleIds = this.moduleIds;
}
let simpleInfoUrl = "/document/selectApiSimpleInfo";
this.apiInfoArray = [];
this.$post(simpleInfoUrl, simpleRequest, response => {
this.apiInfoArray = response.data;
this.apiStepIndex = 0;
if (this.apiInfoArray.length > 0) {
this.checkApiInfoNode(this.apiStepIndex);
}
});
},
shareApiDocument(isBatchShare){
let thisHost = window.location.host;
this.shareUrl = "";
this.batchShareUrl = "";
let shareIdArr = [];
let shareType = "Single";
if(isBatchShare == 'true'){
this.apiInfoArray.forEach(f => {
if (!f.id) {
return;
}
shareIdArr.push(f.id);
});
shareType = "Batch";
}else{
shareIdArr.push(this.apiInfoArray[this.apiStepIndex].id);
}
let genShareInfoParam = {};
genShareInfoParam.shareApiIdList = shareIdArr;
genShareInfoParam.shareType = shareType;
this.$post("/document/generateApiDocumentShareInfo", genShareInfoParam, res => {
if(shareType == "Batch"){
this.batchShareUrl = thisHost+"/document"+res.data.shareUrl;
}else{
this.shareUrl = thisHost+"/document"+res.data.shareUrl;
}
}, (error) => {
});
},
selectApiInfo(index,apiId) {
let simpleInfoUrl = "/document/selectApiInfoById/" + apiId;
this.$get(simpleInfoUrl, response => {
let returnData = response.data;
this.$set(this.apiInfoArray,index,returnData);
});
},
clickStep(apiId) {
for (let index = 0; index < this.apiInfoArray.length; index++) {
if (apiId == this.apiInfoArray[index].id) {
this.apiStepIndex = index;
break;
}
}
//
this.checkApiInfoNode(this.apiStepIndex);
//
this.redirectScroll(this.apiStepIndex);
},
stepClick(stepIndex) {
this.apiStepIndex = stepIndex;
},
getColor(enable, method) {
return this.methodColorMap.get(method);
},
formatBoolean(row, column, cellValue) {
var ret = '' //
if (cellValue) {
ret = "是" //
} else {
ret = "否"
}
return ret;
},
getJsonArr(jsonString) {
let returnJsonArr = [];
if (jsonString == '无' || jsonString == null) {
return returnJsonArr;
}
let jsonArr = JSON.parse(jsonString);
//
for (var index = 0; index < jsonArr.length; index++) {
var item = jsonArr[index];
if (item.name != "" && item.name != null) {
returnJsonArr.push(item);
}
}
return returnJsonArr;
},
//
genPreviewData(previewData) {
if (previewData != null && previewData != '') {
let showDataObj = {};
for (var key in previewData) {
// showDataObj.set(key,previewData[key]);
let value = previewData[key];
if(typeof(value)=='string'){
if (value.indexOf("@") >= 0) {
value = this.showPreview(value);
}
}
showDataObj[key] = value;
}
showDataObj = JSON.stringify(showDataObj);
previewData = formatJson(showDataObj);
}
return previewData;
},
showPreview(itemValue) {
//
if (!itemValue) {
return;
}
let index = itemValue.indexOf("|");
if (index > -1) {
itemValue = itemValue.substring(0, index).trim();
}
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
return;
}
itemValue += "|" + f.name;
if (f.params) {
itemValue += ":" + f.params.map(p => p.value).join(",");
}
});
itemValue = calculate(itemValue);
return itemValue;
},
onCopySuccess: function (e) {
if(this.apiStepIndex < this.apiInfoArray.length){
this.apiInfoArray[this.apiStepIndex].sharePopoverVisible = false;
}
this.$message({
message: this.$t('commons.copy_success'),
type: 'success'
});
},
onCopyError: function (e) {
if(this.apiStepIndex < this.apiInfoArray.length){
this.apiInfoArray[this.apiStepIndex].sharePopoverVisible = false;
}
this.$message.error(this.$t('api_report.error'));
},
handleScroll(){
//apiDocInfoDiv(item+20)
let apiDocDivScrollTop = this.$refs.apiDocInfoDiv.scrollTop;
let apiDocDivClientTop = this.$refs.apiDocInfoDiv.clientHeight;
let scrolledHeigh = apiDocDivScrollTop+apiDocDivClientTop;
let lastIndex = 0;
for (let index = 0; index < this.apiInfoArray.length; index++) {
//. : +-index(20px)>0 index
if(scrolledHeigh>0){
lastIndex = index;
let itemHeight = this.$refs.apiDocInfoDivItem[index].offsetHeight+20;
scrolledHeigh = scrolledHeigh - itemHeight;
}else{
break;
}
}
this.apiStepIndex = lastIndex;
// 3
this.checkApiInfoNode(this.apiStepIndex);
},
redirectScroll(itemIndex){
//api
// let apiDocDivClientTop = this.$refs.apiDocInfoDiv.clientHeight;
let apiDocDivClientTop = 0;
let itemHeightCount = 0;
for (let i = 0; i <= itemIndex-1; i++) {
let itemHeight = this.$refs.apiDocInfoDivItem[i].offsetHeight+20;
itemHeightCount+=itemHeight;
}
this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivClientTop+itemHeightCount);
},
checkApiInfoNode(itemIndex){
//api3
let beforeNodeIndex = itemIndex<3?0:(itemIndex-3);
let afterNodeIndex = (itemIndex+3)<this.apiInfoArray.length?(itemIndex+3):this.apiInfoArray.length;
for(let beforeIndex = itemIndex;beforeIndex < afterNodeIndex;beforeIndex++){
let apiInfo = this.apiInfoArray[beforeIndex];
if(apiInfo==null){
continue;
}
if(apiInfo == null || !apiInfo.selectedFlag){
let apiId = apiInfo.id;
if(!apiInfo.isSearching){
apiInfo.isSearching = true;
this.selectApiInfo(beforeIndex,apiId);
}
}
}
for(let afterIndex = beforeNodeIndex;afterIndex <itemIndex;afterIndex++){
let apiInfo = this.apiInfoArray[afterIndex];
if(apiInfo==null){
continue;
}
if(apiInfo == null || !apiInfo.selectedFlag){
let apiId = apiInfo.id;
if(!apiInfo.isSearching) {
apiInfo.isSearching = true;
this.selectApiInfo(afterIndex,apiId);
}
}
}
}
},
}
</script>
<style scoped>
.simpleFontClass {
font-weight: normal;
font-size: 14px;
margin-left: 10px;
}
.blackFontClass {
font-weight: bold;
font-size: 14px;
}
.smallFontClass {
font-size: 13px;
margin: 20px 10px;
}
.tip {
padding: 3px 5px;
font-size: 14px;
border-radius: 4px;
border-left: 4px solid #783887;
}
.apiInfoRow {
margin: 20px 10px;
}
.apiStatusTag {
margin: 20px 5px;
}
.showDataDiv {
background-color: #F5F7F9;
margin: 20px 10px;
}
/*
步骤条中已经完成后的节点样式和里面a标签的样式
*/
/deep/ .el-step__head.is-finish {
color: #C0C4CC;
border-color: #C0C4CC;
}
/deep/ .el-step__title.is-finish /deep/ .el-link.el-link--default {
color: #C0C4CC;
}
/*
步骤条中当前节点样式和当前a标签的样式
*/
/deep/ .el-step__head.is-process {
color: #783887;
border-color: #783887;
}
/deep/ .el-step__title.is-process /deep/ .el-link.el-link--default {
color: #783887;
}
.document-table {
margin: 20px 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/ th {
background-color: #FAFAFA;
border-right: 0px solid #EBEEF5
}
.el-divider--horizontal {
margin: 12px 0;
}
</style>

View File

@ -1,17 +1,18 @@
<template>
<div>
<api-document-item :project-id="projectId" :module-ids="moduleIds"/>
<!-- <api-document-item :project-id="projectId" :module-ids="moduleIds"/>-->
<api-document-anchor :project-id="projectId" :module-ids="moduleIds"></api-document-anchor>
</div>
</template>
<script>
import ApiDocumentItem from "@/business/components/api/definition/components/document/ApiDocumentItem";
import ApiDocumentAnchor from "@/business/components/api/definition/components/document/ApiDocumentAnchor";
export default {
name: "ApiDocumentsPage",
components: {
ApiDocumentItem,
ApiDocumentAnchor
},
data() {
return {

View File

@ -48,11 +48,6 @@ export default {
name: "ApiDefinition",
component: () => import('@/business/components/api/definition/ApiDefinition'),
},
{
path: "definition/document/:documentId",
name: "ApiDefinitionDocument",
component: () => import('@/business/components/api/definition/components/document/ApiDocumentItem'),
},
{
path: "automation",
name: "ApiAutomation",

View File

@ -1,19 +1,18 @@
<template>
<div>
<api-document-item :pageHeaderHeight="pageHeaderHeight" :project-id="projectId" :module-ids="moduleIds" :document-id="documentId" ref="apiDocumentItem"/>
<!-- <test-scroll></test-scroll>-->
<api-document-anchor :pageHeaderHeight="pageHeaderHeight" :project-id="projectId" :module-ids="moduleIds" :document-id="documentId" ref="apiDocumentAnchor"/>
</div>
</template>
<script>
import ApiDocumentItem from "@/business/components/api/definition/components/document/ApiDocumentItem";
import ApiDocumentAnchor from "@/business/components/api/definition/components/document/ApiDocumentAnchor";
export default {
name: "ApiDocumentsPage",
components: {
ApiDocumentItem,
ApiDocumentAnchor,
},
data() {
return {
@ -47,8 +46,8 @@ export default {
},
selectDocumentInfo(){
this.getUrlParam();
if(this.$refs.apiDocumentItem){
this.$refs.apiDocumentItem.initApiDocSimpleList();
if(this.$refs.apiDocumentAnchor){
this.$refs.apiDocumentAnchor.initApiDocSimpleList();
}
}
},