feat_接口定义_首页卡片可配置 --story=1003343 --user=王孝刚 首页展示卡片可配置 https://www.tapd.cn/55049933/s/1057199

This commit is contained in:
wxg0103 2021-10-15 11:46:27 +08:00 committed by fit2-zhao
parent 12977ef200
commit 5a7964dc46
14 changed files with 848 additions and 33 deletions

View File

@ -62,6 +62,12 @@ public class ApiDefinitionController {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true); Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, apiDefinitionService.list(request)); return PageUtils.setPageInfo(page, apiDefinitionService.list(request));
} }
@PostMapping("/week/list/{goPage}/{pageSize}")
@RequiresPermissions("PROJECT_API_DEFINITION:READ")
public Pager<List<ApiDefinitionResult>> weekList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, apiDefinitionService.weekList(request));
}
@PostMapping("/list/relevance/{goPage}/{pageSize}") @PostMapping("/list/relevance/{goPage}/{pageSize}")
public Pager<List<ApiDefinitionResult>> listRelevance(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) { public Pager<List<ApiDefinitionResult>> listRelevance(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiDefinitionRequest request) {

View File

@ -16,7 +16,15 @@ public class ApiDefinitionResult extends ApiDefinitionWithBLOBs {
private String caseStatus; private String caseStatus;
private String scenarioTotal;
private String casePassingRate; private String casePassingRate;
private String deleteUser; private String deleteUser;
private String[] ids;
private String caseType;
private String scenarioType;
} }

View File

@ -16,6 +16,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.mockconfig.MockConfigImportDTO; import io.metersphere.api.dto.mockconfig.MockConfigImportDTO;
import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult; import io.metersphere.api.dto.swaggerurl.SwaggerTaskResult;
@ -132,39 +133,59 @@ public class ApiDefinitionService {
return resList; return resList;
} }
public void initDefaultModuleId(){ public List<ApiDefinitionResult> weekList(ApiDefinitionRequest request) {
//获取7天之前的日期
Date startDay = DateUtils.dateSum(new Date(), -6);
//将日期转化为 00:00:00 的时间戳
Date startTime = null;
try {
startTime = DateUtils.getDayStartTime(startDay);
} catch (Exception e) {
}
if (startTime == null) {
return new ArrayList<>(0);
} else {
request = this.initRequest(request, true, true);
List<ApiDefinitionResult> resList = extApiDefinitionMapper.weekList(request, startTime.getTime());
calculateResult(resList, request.getProjectId());
calculateResultSce(resList);
return resList;
}
}
public void initDefaultModuleId() {
ApiDefinitionExample example = new ApiDefinitionExample(); ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andModuleIdIsNull(); example.createCriteria().andModuleIdIsNull();
List<ApiDefinition> updateApiList = apiDefinitionMapper.selectByExample(example); List<ApiDefinition> updateApiList = apiDefinitionMapper.selectByExample(example);
Map<String, Map<String,List<ApiDefinition>>> projectIdMap = new HashMap<>(); Map<String, Map<String, List<ApiDefinition>>> projectIdMap = new HashMap<>();
for (ApiDefinition api : updateApiList) { for (ApiDefinition api : updateApiList) {
String projectId = api.getProjectId(); String projectId = api.getProjectId();
String protocal = api.getProtocol(); String protocal = api.getProtocol();
if (projectIdMap.containsKey(projectId)) { if (projectIdMap.containsKey(projectId)) {
if(projectIdMap.get(projectId).containsKey(protocal)){ if (projectIdMap.get(projectId).containsKey(protocal)) {
projectIdMap.get(projectId).get(protocal).add(api); projectIdMap.get(projectId).get(protocal).add(api);
}else { } else {
List<ApiDefinition> list = new ArrayList<>(); List<ApiDefinition> list = new ArrayList<>();
list.add(api); list.add(api);
projectIdMap.get(projectId).put(protocal,list); projectIdMap.get(projectId).put(protocal, list);
} }
} else { } else {
List<ApiDefinition> list = new ArrayList<>(); List<ApiDefinition> list = new ArrayList<>();
list.add(api); list.add(api);
Map<String,List<ApiDefinition>> map = new HashMap<>(); Map<String, List<ApiDefinition>> map = new HashMap<>();
map.put(protocal,list); map.put(protocal, list);
projectIdMap.put(projectId, map); projectIdMap.put(projectId, map);
} }
} }
ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class); ApiModuleService apiModuleService = CommonBeanFactory.getBean(ApiModuleService.class);
for (Map.Entry<String, Map<String,List<ApiDefinition>>> entry : projectIdMap.entrySet()) { for (Map.Entry<String, Map<String, List<ApiDefinition>>> entry : projectIdMap.entrySet()) {
String projectId = entry.getKey(); String projectId = entry.getKey();
Map<String,List<ApiDefinition>> map = entry.getValue(); Map<String, List<ApiDefinition>> map = entry.getValue();
for (Map.Entry<String,List<ApiDefinition>> itemEntry : map.entrySet()) { for (Map.Entry<String, List<ApiDefinition>> itemEntry : map.entrySet()) {
String protocal = itemEntry.getKey(); String protocal = itemEntry.getKey();
ApiModule node = apiModuleService.getDefaultNodeUnCreateNew(projectId, protocal); ApiModule node = apiModuleService.getDefaultNodeUnCreateNew(projectId, protocal);
if(node != null){ if (node != null) {
List<ApiDefinition> testCaseList = itemEntry.getValue(); List<ApiDefinition> testCaseList = itemEntry.getValue();
for (ApiDefinition apiDefinition : testCaseList) { for (ApiDefinition apiDefinition : testCaseList) {
ApiDefinitionWithBLOBs updateCase = new ApiDefinitionWithBLOBs(); ApiDefinitionWithBLOBs updateCase = new ApiDefinitionWithBLOBs();
@ -596,7 +617,7 @@ public class ApiDefinitionService {
} }
private void _importCreate(List<ApiDefinition> sameRequest, ApiDefinitionMapper batchMapper, ApiDefinitionWithBLOBs apiDefinition, private void _importCreate(List<ApiDefinition> sameRequest, ApiDefinitionMapper batchMapper, ApiDefinitionWithBLOBs apiDefinition,
ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List<ApiTestCaseWithBLOBs> cases ,List<MockConfigImportDTO> mocks) { ApiTestCaseMapper apiTestCaseMapper, ApiTestImportRequest apiTestImportRequest, List<ApiTestCaseWithBLOBs> cases, List<MockConfigImportDTO> mocks) {
String originId = apiDefinition.getId(); String originId = apiDefinition.getId();
if (CollectionUtils.isEmpty(sameRequest)) { if (CollectionUtils.isEmpty(sameRequest)) {
apiDefinition.setId(UUID.randomUUID().toString()); apiDefinition.setId(UUID.randomUUID().toString());
@ -1198,6 +1219,20 @@ public class ApiDefinitionService {
return resList; return resList;
} }
public void calculateResultSce(List<ApiDefinitionResult> resList) {
if (!resList.isEmpty()) {
resList.stream().forEach(res -> {
List<Scenario> scenarioList = extApiDefinitionMapper.scenarioList(res.getId());
String count = String.valueOf(scenarioList.size());
res.setScenarioTotal(count);
String[] strings = new String[scenarioList.size()];
String[] ids2 = scenarioList.stream().map(Scenario::getId).collect(Collectors.toList()).toArray(strings);
res.setIds(ids2);
res.setScenarioType("scenario");
});
}
}
public void calculateResult(List<ApiDefinitionResult> resList, String projectId) { public void calculateResult(List<ApiDefinitionResult> resList, String projectId) {
if (!resList.isEmpty()) { if (!resList.isEmpty()) {
List<String> ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList()); List<String> ids = resList.stream().map(ApiDefinitionResult::getId).collect(Collectors.toList());
@ -1206,6 +1241,7 @@ public class ApiDefinitionService {
for (ApiDefinitionResult res : resList) { for (ApiDefinitionResult res : resList) {
ApiComputeResult compRes = resultMap.get(res.getId()); ApiComputeResult compRes = resultMap.get(res.getId());
if (compRes != null) { if (compRes != null) {
res.setCaseType("apiCase");
res.setCaseTotal(String.valueOf(compRes.getCaseTotal())); res.setCaseTotal(String.valueOf(compRes.getCaseTotal()));
res.setCasePassingRate(compRes.getPassRate()); res.setCasePassingRate(compRes.getPassRate());
// 状态优先级 未执行未通过通过 // 状态优先级 未执行未通过通过
@ -1217,6 +1253,7 @@ public class ApiDefinitionService {
res.setCaseStatus(Translator.get("execute_pass")); res.setCaseStatus(Translator.get("execute_pass"));
} }
} else { } else {
res.setCaseType("apiCase");
res.setCaseTotal("-"); res.setCaseTotal("-");
res.setCasePassingRate("-"); res.setCasePassingRate("-");
res.setCaseStatus("-"); res.setCaseStatus("-");
@ -1571,7 +1608,7 @@ public class ApiDefinitionService {
} }
public List<RelationshipEdgeDTO> getRelationshipApi(String id, String relationshipType) { public List<RelationshipEdgeDTO> getRelationshipApi(String id, String relationshipType) {
List<RelationshipEdge> relationshipEdges= relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType); List<RelationshipEdge> relationshipEdges = relationshipEdgeService.getRelationshipEdgeByType(id, relationshipType);
List<String> ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges); List<String> ids = relationshipEdgeService.getRelationIdsByType(relationshipType, relationshipEdges);
if (CollectionUtils.isNotEmpty(ids)) { if (CollectionUtils.isNotEmpty(ids)) {

View File

@ -5,6 +5,7 @@ import io.metersphere.api.dto.definition.ApiComputeResult;
import io.metersphere.api.dto.definition.ApiDefinitionRequest; import io.metersphere.api.dto.definition.ApiDefinitionRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult; import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO; import io.metersphere.api.dto.definition.ApiSwaggerUrlDTO;
import io.metersphere.api.dto.scenario.Scenario;
import io.metersphere.base.domain.ApiDefinition; import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.base.domain.ApiDefinitionExampleWithOperation; import io.metersphere.base.domain.ApiDefinitionExampleWithOperation;
import io.metersphere.controller.request.BaseQueryRequest; import io.metersphere.controller.request.BaseQueryRequest;
@ -20,6 +21,10 @@ public interface ExtApiDefinitionMapper {
List<ApiDefinitionResult> list(@Param("request") ApiDefinitionRequest request); List<ApiDefinitionResult> list(@Param("request") ApiDefinitionRequest request);
List<ApiDefinitionResult> weekList(@Param("request") ApiDefinitionRequest request , @Param("startTimestamp") long startTimestamp );
List<Scenario> scenarioList(@Param("apiDefinitionId") String apiDefinitionId);
int moduleCount(@Param("request") ApiDefinitionRequest request); int moduleCount(@Param("request") ApiDefinitionRequest request);
//List<ApiComputeResult> selectByIds(@Param("ids") List<String> ids); //List<ApiComputeResult> selectByIds(@Param("ids") List<String> ids);

View File

@ -257,6 +257,30 @@
</if> </if>
</select> </select>
<select id="weekList" resultType="io.metersphere.api.dto.definition.ApiDefinitionResult">
select api_definition.id, api_definition.project_id, api_definition.num, api_definition.tags,api_definition.follow_people,api_definition.original_state,
api_definition.name,api_definition.protocol,api_definition.path,api_definition.module_id,api_definition.module_path,api_definition.method,
api_definition.description,api_definition.environment_id,
api_definition.status, api_definition.user_id, api_definition.create_time, api_definition.update_time, project.name as
project_name, user.name as user_name,deleteUser.name AS delete_user,api_definition.delete_time, api_definition.remark
from (select * from api_definition where update_time >= #{startTimestamp} order by update_time desc)api_definition
left join project on api_definition.project_id = project.id
left join user on api_definition.user_id = user.id
left join user deleteUser on api_definition.delete_user_id = deleteUser.id
<include refid="queryWhereCondition"/>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
<if test="order.name == 'user_name'">
user_name ${order.type}
</if>
<if test="order.name != 'user_name'">
api_definition.${order.name} ${order.type}
</if>
</foreach>
</if>
</select>
<select id="listByIds" resultType="io.metersphere.api.dto.definition.ApiDefinitionResult"> <select id="listByIds" resultType="io.metersphere.api.dto.definition.ApiDefinitionResult">
select api_definition.id, api_definition.project_id, api_definition.num, api_definition.tags,api_definition.follow_people, select api_definition.id, api_definition.project_id, api_definition.num, api_definition.tags,api_definition.follow_people,
api_definition.name,api_definition.protocol,api_definition.path,api_definition.module_id,api_definition.module_path,api_definition.method, api_definition.name,api_definition.protocol,api_definition.path,api_definition.module_id,api_definition.module_path,api_definition.method,
@ -682,5 +706,13 @@
</foreach> </foreach>
and api_definition.status != 'Trash'; and api_definition.status != 'Trash';
</select> </select>
<select id="scenarioList" resultType="io.metersphere.api.dto.scenario.Scenario">
select DISTINCT t1.id
from api_scenario t1
join api_scenario_reference_id t2 on t1.id = t2.api_scenario_id
join api_test_case t3 on t2.reference_id = t3.id and t2.reference_type = 'REF'
where t3.api_definition_id = #{apiDefinitionId}
and t3.`status`!='Trash'
</select>
</mapper> </mapper>

View File

@ -220,7 +220,8 @@
<el-drawer :visible.sync="showReportVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" <el-drawer :visible.sync="showReportVisible" :destroy-on-close="true" direction="ltr" :withHeader="true"
:modal="false" :modal="false"
size="90%"> size="90%">
<ms-api-report-detail @invisible="showReportVisible = false" @refresh="search" :infoDb="infoDb" :report-id="showReportId" :currentProjectId="projectId"/> <ms-api-report-detail @invisible="showReportVisible = false" @refresh="search" :infoDb="infoDb"
:report-id="showReportId" :currentProjectId="projectId"/>
</el-drawer> </el-drawer>
<!--测试计划--> <!--测试计划-->
<el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false" <el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false"
@ -248,7 +249,12 @@ import {downloadFile, getCurrentProjectID, getUUID, setDefaultTheme, strMapToObj
import {API_SCENARIO_CONFIGS} from "@/business/components/common/components/search/search-components"; import {API_SCENARIO_CONFIGS} from "@/business/components/common/components/search/search-components";
import {API_SCENARIO_LIST, ORIGIN_COLOR} from "../../../../../common/js/constants"; import {API_SCENARIO_LIST, ORIGIN_COLOR} from "../../../../../common/js/constants";
import {buildBatchParam, getCustomTableHeader, getCustomTableWidth, getLastTableSortField} from "@/common/js/tableUtils"; import {
buildBatchParam,
getCustomTableHeader,
getCustomTableWidth,
getLastTableSortField
} from "@/common/js/tableUtils";
import {API_SCENARIO_FILTERS} from "@/common/js/table-constants"; import {API_SCENARIO_FILTERS} from "@/common/js/table-constants";
import {scenario} from "@/business/components/track/plan/event-bus"; import {scenario} from "@/business/components/track/plan/event-bus";
import MsTable from "@/business/components/common/components/table/MsTable"; import MsTable from "@/business/components/common/components/table/MsTable";
@ -580,7 +586,7 @@ export default {
}, },
methods: { methods: {
generateGraph() { generateGraph() {
getGraphByCondition('API_SCENARIO', buildBatchParam(this, this.$refs.scenarioTable.selectIds),(data) => { getGraphByCondition('API_SCENARIO', buildBatchParam(this, this.$refs.scenarioTable.selectIds), (data) => {
this.graphData = data; this.graphData = data;
this.$refs.relationshipGraph.open(); this.$refs.relationshipGraph.open();
}); });
@ -638,6 +644,15 @@ export default {
this.condition.executeStatus = 'executePass'; this.condition.executeStatus = 'executePass';
break; break;
} }
if (this.selectDataRange != null) {
let selectParamArr = this.selectDataRange.split(":");
if (selectParamArr.length === 2) {
if (selectParamArr[0] === "list") {
let ids = selectParamArr[1].split(",");
this.condition.ids = ids;
}
}
}
let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize; let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize;
if (this.condition.projectId) { if (this.condition.projectId) {
this.result = this.$post(url, this.condition, response => { this.result = this.$post(url, this.condition, response => {
@ -1052,7 +1067,7 @@ export default {
link.download = "场景JMX文件集.zip"; link.download = "场景JMX文件集.zip";
this.result.loading = false; this.result.loading = false;
link.click(); link.click();
},error => { }, error => {
this.result.loading = false; this.result.loading = false;
this.$error("导出JMX文件失败"); this.$error("导出JMX文件失败");
}); });

View File

@ -637,9 +637,13 @@ export default {
if (this.selectDataRange == 'thisWeekCount') { if (this.selectDataRange == 'thisWeekCount') {
this.condition.selectThisWeedData = true; this.condition.selectThisWeedData = true;
} else if (this.selectDataRange != null) { } else if (this.selectDataRange != null) {
let selectParamArr = this.selectDataRange.split("single:"); let selectParamArr = this.selectDataRange.split(":");
if (selectParamArr.length === 2) { if (selectParamArr.length === 2) {
this.condition.id = selectParamArr[1]; if(selectParamArr[0] === "single") {
this.condition.id = selectParamArr[1];
}else {
this.condition.apiDefinitionId = selectParamArr[1];
}
} }
} }
}, },

View File

@ -3,25 +3,114 @@
<ms-main-container v-loading="result.loading"> <ms-main-container v-loading="result.loading">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="6"> <el-col :span="6">
<ms-api-info-card @redirectPage="redirectPage" :api-count-data="apiCountData" :interface-coverage="apiCoverage"/> <ms-api-info-card @redirectPage="redirectPage" :api-count-data="apiCountData"
:interface-coverage="apiCoverage"/>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<ms-test-case-info-card @redirectPage="redirectPage" :test-case-count-data="testCaseCountData"/> <ms-test-case-info-card @redirectPage="redirectPage" :test-case-count-data="testCaseCountData"/>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData" :interface-coverage="interfaceCoverage"/> <ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData"
:interface-coverage="interfaceCoverage"/>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/> <ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
<ms-failure-test-case-list @redirectPage="redirectPage"/> <el-card class="table-card" v-loading="result.loading" body-style="padding:10px;">
<template v-slot:header>
<el-col :span="22">
<span class="title">
{{ cardTitle.name }}
</span>
</el-col>
<el-dropdown @command="handleCommand">
<el-col :span="2" :offset="22">
<el-link type="primary" :underline="false">
<el-icon class="el-icon-more"></el-icon>
</el-link>
</el-col>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="caseTitle">
<el-radio v-model="testTitleFirst" label="caseTitle">{{
$t('api_test.home_page.failed_case_list.title')}}
</el-radio>
</el-dropdown-item>
<el-dropdown-item command="taskTitle">
<el-radio v-model="testTitleFirst" label="taskTitle">
{{ $t('api_test.home_page.running_task_list.title') }}
</el-radio>
</el-dropdown-item>
<el-dropdown-item command="newApiTitle">
<el-radio v-model="testTitleFirst" label="newApiTitle">
{{ $t('api_test.home_page.new_case_list.title') }}
</el-radio>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<div v-if="testTitleFirst === 'caseTitle'">
<ms-api-failure-test-case-list @redirectPage="redirectPage"/>
</div>
<div v-if="testTitleFirst === 'taskTitle'">
<ms-api-running-task-list :call-from="'api_test'" @redirectPage="redirectPage"/>
</div>
<div v-if="testTitleFirst === 'newApiTitle'">
<ms-api-new-test-case-list @redirectPage="redirectPage"/>
</div>
</el-card>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<ms-running-task-list :call-from="'api_test'" @redirectPage="redirectPage"/> <el-card class="table-card" v-loading="result.loading" body-style="padding:10px;">
<div v-if="testTitleSecond === 'caseTitle'">
<ms-api-failure-test-case-list @redirectPage="redirectPage" />
</div>
<div v-if="testTitleSecond === 'taskTitle'">
<ms-api-running-task-list :call-from="'api_test'" @redirectPage="redirectPage"/>
</div>
<div v-if="testTitleSecond === 'newApiTitle'">
<ms-api-new-test-case-list @redirectPage="redirectPage"/>
</div>
<template v-slot:header>
<el-col :span="22">
<span class="title">
{{ cardTitleSecond.name }}
</span>
</el-col>
<el-dropdown @command="handleCommandSecond">
<el-col :span="2" :offset="22">
<el-link type="primary" :underline="false">
<el-icon class="el-icon-more"></el-icon>
</el-link>
</el-col>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="caseTitle">
<el-radio v-model="testTitleSecond" label="caseTitle">{{
$t('api_test.home_page.failed_case_list.title')
}}
</el-radio>
</el-dropdown-item>
<el-dropdown-item command="taskTitle">
<el-radio v-model="testTitleSecond" label="taskTitle">
{{ $t('api_test.home_page.running_task_list.title') }}
</el-radio>
</el-dropdown-item>
<el-dropdown-item command="newApiTitle">
<el-radio v-model="testTitleSecond" label="newApiTitle">
{{ $t('api_test.home_page.new_case_list.title') }}
</el-radio>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-card>
</el-col> </el-col>
</el-row> </el-row>
@ -37,17 +126,20 @@ import MsSceneInfoCard from "./components/SceneInfoCard";
import MsScheduleTaskInfoCard from "./components/ScheduleTaskInfoCard"; import MsScheduleTaskInfoCard from "./components/ScheduleTaskInfoCard";
import MsTestCaseInfoCard from "./components/TestCaseInfoCard"; import MsTestCaseInfoCard from "./components/TestCaseInfoCard";
import MsApiFailureTestCaseList from "./components/ApiFailureTestCaseList";
import MsFailureTestCaseList from "./components/FailureTestCaseList"; import MsFailureTestCaseList from "./components/FailureTestCaseList";
import MsRunningTaskList from "./components/RunningTaskList" import MsRunningTaskList from "./components/RunningTaskList";
import {getCurrentProjectID,getUUID} from "@/common/js/utils"; import MsApiRunningTaskList from "./components/ApiRunningTaskList";
import {getCurrentProjectID, getUUID} from "@/common/js/utils";
import MsApiNewTestCaseList from "./components/ApiNewTestCaseList";
export default { export default {
name: "ApiTestHomePage", name: "ApiTestHomePage",
components: { components: {
MsApiInfoCard, MsSceneInfoCard, MsScheduleTaskInfoCard, MsTestCaseInfoCard, MsApiInfoCard, MsSceneInfoCard, MsScheduleTaskInfoCard, MsTestCaseInfoCard,
MsFailureTestCaseList, MsRunningTaskList, MsApiFailureTestCaseList, MsRunningTaskList,
MsMainContainer, MsContainer MsMainContainer, MsContainer,MsFailureTestCaseList,MsApiRunningTaskList,MsApiNewTestCaseList
}, },
data() { data() {
@ -60,14 +152,85 @@ export default {
interfaceCoverage: "waitting...", interfaceCoverage: "waitting...",
apiCoverage: "waitting...", apiCoverage: "waitting...",
result: {}, result: {},
testTitleFirst: " ",
testTitleSecond: " ",
cardTitle: {},
cardTitleSecond: {},
} }
}, },
activated() { activated() {
this.search(); this.search();
}, },
created() { created() {
this.testTitleFirst = "caseTitle";
if (localStorage.getItem("cardTitle")) {
if (localStorage.getItem("titleFirst")) {
this.testTitleFirst = localStorage.getItem("titleFirst");
}
this.cardTitle.name = localStorage.getItem("cardTitle");
} else{
this.cardTitle.name=this.$t('api_test.home_page.failed_case_list.title');
}
this.testTitleSecond = "taskTitle";
if (localStorage.getItem("cardTitleSecond")) {
if (localStorage.getItem("titleSecond")) {
this.testTitleSecond = localStorage.getItem("titleSecond");
}
this.cardTitleSecond.name = localStorage.getItem("cardTitleSecond");
} else{
this.cardTitleSecond.name=this.$t('api_test.home_page.running_task_list.title');
}
}, },
methods: { methods: {
handleCommand(cmd) {
if(cmd){
switch (cmd) {
case "caseTitle":
this.cardTitle.name=this.$t('api_test.home_page.failed_case_list.title');
localStorage.setItem("cardTitle" , this.cardTitle.name);
localStorage.setItem("titleFirst" , "caseTitle");
break;
case "taskTitle":
this.cardTitle.name = this.$t('api_test.home_page.running_task_list.title');
localStorage.setItem("cardTitle" , this.cardTitle.name);
localStorage.setItem("titleFirst" , "taskTitle");
break;
case "newApiTitle":
this.cardTitle.name = this.$t('api_test.home_page.new_case_list.title');
localStorage.setItem("cardTitle" , this.cardTitle.name);
localStorage.setItem("titleFirst" , "newApiTitle");
break;
}
}
},
handleCommandSecond(cmd) {
if(cmd){
switch (cmd) {
case "caseTitle":
this.cardTitleSecond.name=this.$t('api_test.home_page.failed_case_list.title');
localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name);
localStorage.setItem("titleSecond" , "caseTitle");
break;
case "taskTitle":
this.cardTitleSecond.name = this.$t('api_test.home_page.running_task_list.title');
localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name);
localStorage.setItem("titleSecond" , "taskTitle");
break;
case "newApiTitle":
this.cardTitleSecond.name = this.$t('api_test.home_page.new_case_list.title');
localStorage.setItem("cardTitleSecond" , this.cardTitleSecond.name);
localStorage.setItem("titleSecond" , "newApiTitle");
break;
}
}
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
});
},
search() { search() {
let selectProjectId = getCurrentProjectID(); let selectProjectId = getCurrentProjectID();
@ -79,7 +242,7 @@ export default {
this.sceneCountData = response.data; this.sceneCountData = response.data;
}); });
this.apiCoverage = "waitting...", this.apiCoverage = "waitting...",
this.interfaceCoverage = "waitting..."; this.interfaceCoverage = "waitting...";
this.$get("/api/countInterfaceCoverage/" + selectProjectId, response => { this.$get("/api/countInterfaceCoverage/" + selectProjectId, response => {
this.interfaceCoverage = response.data; this.interfaceCoverage = response.data;
}); });
@ -96,19 +259,25 @@ export default {
this.scheduleTaskCountData = response.data; this.scheduleTaskCountData = response.data;
}); });
}, },
redirectPage(page,dataType,selectType){ redirectPage(page, dataType, selectType, title) {
//api //api
//UUID //UUID
let uuid = getUUID(); let uuid = getUUID();
switch (page){ switch (page) {
case "api": case "api":
this.$router.push({name:'ApiDefinition',params:{redirectID:uuid,dataType:dataType,dataSelectRange:selectType}}); this.$router.push({
name: 'ApiDefinition',
params: {redirectID: uuid, dataType: dataType, dataSelectRange: selectType}
});
break; break;
case "scenario": case "scenario":
this.$router.push({name:'ApiAutomation',params:{redirectID:uuid,dataType:dataType,dataSelectRange:selectType}}); this.$router.push({
name: 'ApiAutomation',
params: {redirectID: uuid, dataType: dataType, dataSelectRange: selectType}
});
break; break;
case "testPlanEdit": case "testPlanEdit":
this.$router.push('/track/plan/view/'+selectType) this.$router.push('/track/plan/view/' + selectType)
break; break;
} }
} }

View File

@ -0,0 +1,103 @@
<template>
<div class="table-card" v-loading="result.loading" body-style="padding:10px;">
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="sortIndex" :label="$t('api_test.home_page.failed_case_list.table_coloum.index')" width="100" show-overflow-tooltip/>
<el-table-column prop="caseName" :label="$t('api_test.home_page.failed_case_list.table_coloum.case_name')" width="150">
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.caseType,row.id)">
{{ row.caseName }}
</el-link>
</template>
</el-table-column>
<el-table-column
prop="caseType"
column-key="caseType"
:label="$t('api_test.home_page.failed_case_list.table_coloum.case_type')"
width="150"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.caseType == 'apiCase'" type="success" effect="plain" :content="$t('api_test.home_page.failed_case_list.table_value.case_type.api')"/>
<ms-tag v-if="scope.row.caseType == 'scenario'" type="warning" effect="plain" :content="$t('api_test.home_page.failed_case_list.table_value.case_type.scene')"/>
<ms-tag v-if="scope.row.caseType == 'load'" type="danger" effect="plain" :content="$t('api_test.home_page.failed_case_list.table_value.case_type.load')"/>
</template>
</el-table-column>
<el-table-column prop="testPlan" :label="$t('api_test.home_page.failed_case_list.table_coloum.test_plan')">
<template v-slot:default="{row}">
<div v-for="(testPlan, index) in row.testPlanDTOList" :key="index">
<el-link type="info" @click="redirect('testPlanEdit',testPlan.id)">
{{ testPlan.name }};
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="failureTimes" :label="$t('api_test.home_page.failed_case_list.table_coloum.failure_times')" width="100" show-overflow-tooltip/>
</el-table>
</div>
</template>
<script>
import MsTag from "@/business/components/common/components/MsTag";
import {getCurrentProjectID} from "@/common/js/utils";
export default {
name: "MsApiFailureTestCaseList",
components: {
MsTag
},
data() {
return {
result: {},
tableData: [],
loading: false
}
},
computed: {
projectId() {
return getCurrentProjectID();
},
},
methods: {
search() {
if (this.projectId) {
this.result = this.$get("/api/faliureCaseAboutTestPlan/"+ this.projectId +"/10", response => {
this.tableData = response.data;
});
}
},
redirect(pageType,param){
switch (pageType){
case "testPlanEdit":
this.$emit('redirectPage','testPlanEdit',null, param);
break;
case "apiCase":
this.$emit('redirectPage','api','apiTestCase', 'single:'+param);
break;
case "scenario":
this.$emit('redirectPage','scenario','scenario', 'edit:'+param);
break;
}
}
},
created() {
this.search();
},
activated() {
this.search();
}
}
</script>
<style scoped>
.el-table {
cursor:pointer;
}
.el-card /deep/ .el-card__header {
border-bottom: 0px solid #EBEEF5;
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<div class="table-card" v-loading="result.loading" body-style="padding:10px;">
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="num" :label="$t('api_test.home_page.new_case_list.table_coloum.index')" width="100"
show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('api_test.home_page.new_case_list.table_coloum.api_name')" width="150">
</el-table-column>
<el-table-column prop="path" :label="$t('api_test.home_page.new_case_list.table_coloum.path')" width="150">
</el-table-column>
<el-table-column prop="status" :label="$t('api_test.home_page.new_case_list.table_coloum.api_status')">
<template v-slot:default="scope">
<span class="el-dropdown-link">
<api-status :value="scope.row.status"/>
</span>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.home_page.new_case_list.table_coloum.update_time')" width="170">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="caseTotal" :label="$t('api_test.home_page.new_case_list.table_coloum.relation_case')"
width="150">
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.caseType,row.id)">
{{ row.caseTotal }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="scenarioTotal"
:label="$t('api_test.home_page.new_case_list.table_coloum.relation_scenario')">
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.scenarioType,row.ids)">
{{ row.scenarioTotal }}
</el-link>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</div>
</template>
<script>
import MsTag from "@/business/components/common/components/MsTag";
import {getCurrentProjectID} from "@/common/js/utils";
import {API_DEFINITION_CONFIGS} from "@/business/components/common/components/search/search-components";
import {API_STATUS} from "@/business/components/api/definition/model/JsonData";
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
import MsTablePagination from "../../../common/pagination/TablePagination";
import {initCondition} from "@/common/js/tableUtils";
export default {
name: "MsApiNewTestCaseList",
components: {
MsTag, ApiStatus, MsTablePagination
},
data() {
return {
result: {},
tableData: [],
loading: false,
currentPage: 1,
pageSize: 10,
total: 0,
condition: {
components: API_DEFINITION_CONFIGS,
},
status: API_STATUS,
currentProtocol: "HTTP",
}
},
computed: {
projectId() {
return getCurrentProjectID();
},
},
methods: {
search(currentProtocol) {
if (this.$refs.table) {
this.$refs.table.clear();
}
initCondition(this.condition, this.condition.selectAll);
this.selectDataCounts = 0;
if (!this.trashEnable) {
this.condition.moduleIds = this.selectNodeIds;
}
this.condition.projectId = this.projectId;
if (this.currentProtocol != null) {
this.condition.protocol = this.currentProtocol;
}
this.enableOrderDrag = (this.condition.orders && this.condition.orders.length) > 0 ? false : true;
//
this.getSelectDataRange();
this.condition.selectThisWeedData = false;
this.condition.apiCaseCoverage = null;
switch (this.selectDataRange) {
case 'thisWeekCount':
this.condition.selectThisWeedData = true;
break;
case 'uncoverage':
this.condition.apiCaseCoverage = 'uncoverage';
break;
case 'coverage':
this.condition.apiCaseCoverage = 'coverage';
break;
case 'Prepare':
this.condition.filters.status = [this.selectDataRange];
break;
case 'Completed':
this.condition.filters.status = [this.selectDataRange];
break;
case 'Underway':
this.condition.filters.status = [this.selectDataRange];
break;
}
if (currentProtocol) {
this.condition.moduleIds = [];
}
if (this.condition.projectId) {
this.result = this.$post("/api/definition/week/list/" + this.currentPage + "/" + this.pageSize, this.condition, response => {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
});
}
},
genProtocalFilter(protocalType) {
if (protocalType === "HTTP") {
this.methodFilters = [
{text: 'GET', value: 'GET'},
{text: 'POST', value: 'POST'},
{text: 'PUT', value: 'PUT'},
{text: 'PATCH', value: 'PATCH'},
{text: 'DELETE', value: 'DELETE'},
{text: 'OPTIONS', value: 'OPTIONS'},
{text: 'HEAD', value: 'HEAD'},
{text: 'CONNECT', value: 'CONNECT'},
];
} else if (protocalType === "TCP") {
this.methodFilters = [
{text: 'TCP', value: 'TCP'},
];
} else if (protocalType === "SQL") {
this.methodFilters = [
{text: 'SQL', value: 'SQL'},
];
} else if (protocalType === "DUBBO") {
this.methodFilters = [
{text: 'DUBBO', value: 'DUBBO'},
{text: 'dubbo://', value: 'dubbo://'},
];
} else {
this.methodFilters = [
{text: 'GET', value: 'GET'},
{text: 'POST', value: 'POST'},
{text: 'PUT', value: 'PUT'},
{text: 'PATCH', value: 'PATCH'},
{text: 'DELETE', value: 'DELETE'},
{text: 'OPTIONS', value: 'OPTIONS'},
{text: 'HEAD', value: 'HEAD'},
{text: 'CONNECT', value: 'CONNECT'},
{text: 'DUBBO', value: 'DUBBO'},
{text: 'dubbo://', value: 'dubbo://'},
{text: 'SQL', value: 'SQL'},
{text: 'TCP', value: 'TCP'},
];
}
},
//
getSelectDataRange() {
let dataRange = this.$route.params.dataSelectRange;
let dataType = this.$route.params.dataType;
if (dataType === 'api') {
this.selectDataRange = dataRange;
} else {
this.selectDataRange = 'all';
}
},
redirect(pageType, param) {
switch (pageType) {
case "apiCase":
this.$emit('redirectPage', 'api', 'apiTestCase', 'singleList:' + param);
break;
case "scenario":
if(param) {
this.$emit('redirectPage', 'scenario', 'scenario', 'list:' + param);
break;
}
}
},
},
created() {
this.search();
},
activated() {
this.search();
}
}
</script>
<style scoped>
.el-table {
cursor: pointer;
}
.el-card /deep/ .el-card__header {
border-bottom: 0px solid #EBEEF5;
}
</style>

View File

@ -0,0 +1,181 @@
<template>
<div class="table-card" v-loading="result.loading" body-style="padding:10px;">
<ms-table
:enable-selection="false"
:condition="condition"
:data="tableData"
@refresh="search"
screen-height="300px">
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('commons.name')" width="200" >
<template v-slot:default="{row}">
<!-- 若为只读用户不可点击之后跳转-->
<span v-if="isReadOnly">
{{ row.name }}
</span>
<el-link v-else type="info" @click="redirect(row)">
{{ row.name }}
</el-link>
</template>
</el-table-column>
<ms-table-column
prop="taskType"
:filters="typeFilters"
:label="$t('api_test.home_page.running_task_list.table_coloum.task_type')" width="120">
<template v-slot:default="scope">
<ms-tag v-if="scope.row.taskGroup == 'API_SCENARIO_TEST'" type="success" effect="plain" :content="$t('api_test.home_page.running_task_list.scenario_schedule')"/>
<ms-tag v-if="scope.row.taskGroup == 'TEST_PLAN_TEST'" type="warning" effect="plain" :content="$t('api_test.home_page.running_task_list.test_plan_schedule')"/>
<ms-tag v-if="scope.row.taskGroup == 'SWAGGER_IMPORT'" type="danger" effect="plain" :content="$t('api_test.home_page.running_task_list.swagger_schedule')"/>
</template>
</ms-table-column>
<el-table-column prop="rule" :label="$t('api_test.home_page.running_task_list.table_coloum.run_rule')" width="120" show-overflow-tooltip/>
<el-table-column width="100" :label="$t('api_test.home_page.running_task_list.table_coloum.task_status')">
<template v-slot:default="scope">
<div>
<el-switch
:disabled="isReadOnly"
v-model="scope.row.taskStatus"
class="captcha-img"
@change="closeTaskConfirm(scope.row)"
></el-switch>
</div>
</template>
</el-table-column>
<el-table-column width="170" :label="$t('api_test.home_page.running_task_list.table_coloum.next_execution_time')">
<template v-slot:default="scope">
<span>{{ scope.row.nextExecutionTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="creator" :label="$t('api_test.home_page.running_task_list.table_coloum.create_user')" width="100" show-overflow-tooltip/>
<el-table-column width="170" :label="$t('api_test.home_page.running_task_list.table_coloum.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
</ms-table>
</div>
</template>
<script>
import MsTag from "@/business/components/common/components/MsTag";
import {getCurrentProjectID} from "@/common/js/utils";
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
export default {
name: "MsApiRunningTaskList",
components: {
MsTableColumn,
MsTable,
MsTag
},
props: {
callFrom: String,
},
data() {
return {
value: '100',
result: {},
tableData: [],
visible: false,
loading: false,
typeFilters: [],
condition: {
filters: {
}
}
}
},
computed:{
isReadOnly(){
return false;
},
projectId() {
return getCurrentProjectID();
},
},
mounted() {
if (this.callFrom === 'api_test') {
this.typeFilters = [
{text: this.$t('api_test.home_page.running_task_list.scenario_schedule'), value: 'API_SCENARIO_TEST'},
{text: this.$t('api_test.home_page.running_task_list.swagger_schedule'), value: 'SWAGGER_IMPORT'},
];
} else {
this.typeFilters = [
{text: this.$t('api_test.home_page.running_task_list.test_plan_schedule'), value: 'TEST_PLAN_TEST'}
];
}
},
methods: {
search() {
if (!this.condition.filters.task_type) {
if (this.callFrom === 'api_test') {
this.condition.filters.task_type = ['SWAGGER_IMPORT', 'API_SCENARIO_TEST'];
} else {
this.condition.filters.task_type = ['TEST_PLAN_TEST'];
}
}
if (this.projectId) {
this.result = this.$post('/api/runningTask/' + this.projectId, this.condition, response => {
this.tableData = response.data;
});
}
},
closeTaskConfirm(row){
let flag = row.taskStatus;
row.taskStatus = !flag; //switch
this.$confirm(this.$t('api_test.home_page.running_task_list.confirm.close_title'), this.$t('commons.prompt'), {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.updateTask(row);
}).catch(() => {
});
},
updateTask(taskRow){
this.result = this.$post('/api/schedule/updateEnableByPrimyKey/disable', taskRow, response => {
this.search();
});
},
redirect(param){
if(param.taskGroup === 'TEST_PLAN_TEST'){
this.$emit('redirectPage','testPlanEdit','', param.scenarioId);
}else if (param.taskGroup === 'API_SCENARIO_TEST') {
this.$emit('redirectPage', 'scenario', 'scenario', 'edit:' + param.scenarioId);
} else if (param.taskGroup === 'SWAGGER_IMPORT') {
this.$emit('redirectPage', 'api', 'api', {param});
}
}
},
created() {
this.search();
},
activated() {
this.search();
},
handleStatus(scope) {
// console.log(scope.row.userId)
}
}
</script>
<style scoped>
.el-table {
cursor:pointer;
}
.el-card /deep/ .el-card__header {
border-bottom: 0px solid #EBEEF5;
}
</style>

View File

@ -1258,6 +1258,7 @@ export default {
pool_max: "Max Number of Configuration", pool_max: "Max Number of Configuration",
query_timeout: "Max Wait(ms)", query_timeout: "Max Wait(ms)",
name_cannot_be_empty: "SQL request name cannot be empty", name_cannot_be_empty: "SQL request name cannot be empty",
tips: "Tips: Allowmultiqueries = true should be configured to execute multiple SQL statements",
dataSource_cannot_be_empty: "SQL request datasource cannot be empty", dataSource_cannot_be_empty: "SQL request datasource cannot be empty",
result_variable: "Result variable", result_variable: "Result variable",
variable_names: "Variable names", variable_names: "Variable names",
@ -1404,6 +1405,18 @@ export default {
} }
} }
}, },
new_case_list: {
title: "Updated interfaces in the past 7 days",
table_coloum: {
index: "ID",
api_name: "Api name",
path: "path",
api_status: "Api status",
update_time: "Update time",
relation_case: "Relation CASE",
relation_scenario: "Relation Scenario"
},
},
running_task_list: { running_task_list: {
title: "Running schedule task", title: "Running schedule task",
table_coloum: { table_coloum: {

View File

@ -1413,6 +1413,18 @@ export default {
} }
} }
}, },
new_case_list: {
title: "过去7天有更新的接口",
table_coloum: {
index: "ID",
api_name: "接口名称",
path: "路径",
api_status: "状态",
update_time: "更新时间",
relation_case: "关联CASE",
relation_scenario: "关联场景"
},
},
running_task_list: { running_task_list: {
title: "运行中的定时任务", title: "运行中的定时任务",
table_coloum: { table_coloum: {

View File

@ -1103,6 +1103,7 @@ export default {
common_config: "通用配置", common_config: "通用配置",
http_config: "HTTP配置", http_config: "HTTP配置",
database_config: "數據庫配置", database_config: "數據庫配置",
tips: "Tips: 執行多條SQL語句需配寘allowMultiQueries=true",
tcp_config: "TCP配置", tcp_config: "TCP配置",
import: "導入環境", import: "導入環境",
request_timeout: "鏈接超時", request_timeout: "鏈接超時",
@ -1413,6 +1414,18 @@ export default {
} }
} }
}, },
new_case_list: {
title: "過去7天有更新的接口",
table_coloum: {
index: "ID",
api_name: "接口名稱",
path: "路徑",
api_status: "狀態",
update_time: "更新時間",
relation_case: "關聯CASE",
relation_scenario: "關聯場景"
},
},
running_task_list: { running_task_list: {
title: "運行中的定時任務", title: "運行中的定時任務",
table_coloum: { table_coloum: {