feat(测试跟踪): 测试跟踪首页交互升级

--story=1010425 --user=宋昌昌 【测试跟踪】测试跟踪首页交互升级 https://www.tapd.cn/55049933/s/1296114
This commit is contained in:
song-cc-rock 2022-11-15 16:25:14 +08:00 committed by 刘瑞斌
parent e7fe6e5d4e
commit 206354aeee
38 changed files with 2450 additions and 771 deletions

View File

@ -28,7 +28,8 @@ public interface ExtApiDefinitionExecResultMapper {
long countByTestCaseIDInProject(String projectId);
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("selectFunctionCase") boolean selectFunctionCase, @Param("startTimestamp") long startTimestamp);
List<ExecutedCaseInfoResult> findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(@Param("projectId") String projectId, @Param("selectFunctionCase") boolean selectFunctionCase,
@Param("startTimestamp") long startTimestamp, @Param("limitNumber") int limitNumber);
String selectExecResult(String resourceId);

View File

@ -61,93 +61,95 @@
<select id="findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber"
resultType="io.metersphere.api.dto.datacount.ExecutedCaseInfoResult">
SELECT *
FROM (
SELECT testCase.testPlanCaseID AS testPlanCaseID,
testCase.id AS id,
testCase.testCaseName AS caseName,
testCase.testPlanName AS testPlan,
testCase.testPlanId AS testPlanId,
caseErrorCountData.dataCountNumber AS failureTimes,
'apiCase' AS caseType
FROM (SELECT testPlanCase.id AS testPlanCaseID,
apiCase.id AS id,
apiCase.`name` AS testCaseName,
testPlan.id AS testPlanId,
testPlan.`name` AS testPlanName
FROM api_test_case apiCase
INNER JOIN test_plan_api_case testPlanCase ON testPlanCase.api_case_id = apiCase.id
INNER JOIN test_plan testPlan ON testPlan.id = testPlanCase.test_plan_id
WHERE (
(apiCase.`status` IS NULL OR apiCase.`status` != 'Trash')
AND apiCase.project_id = #{projectId})) testCase
INNER JOIN (SELECT executionInfo.source_id AS sourceId,
COUNT(executionInfo.id) AS dataCountNumber
FROM api_case_execution_info executionInfo
INNER JOIN test_plan_api_case testPlanCase
ON executionInfo.source_id = testPlanCase.id
WHERE executionInfo.`result` = 'ERROR'
AND executionInfo.create_time > #{startTimestamp}
GROUP BY source_id) caseErrorCountData
ON caseErrorCountData.sourceId = testCase.testPlanCaseID
UNION
SELECT scene.id AS testCaseID,
scene.id AS id,
scene.`name` AS caseName,
apiScene.testPlanName AS testPlan,
apiScene.testPlanId AS testPlanId,
count(executionInfo.id) AS failureTimes,
'scenario' AS caseType
FROM scenario_execution_info executionInfo
INNER JOIN (SELECT testPlanScenario.id,
testPlanScenario.api_scenario_id,
testPlan.id AS testPlanId,
testPlan.`name` AS testPlanName
FROM test_plan_api_scenario testPlanScenario
INNER JOIN test_plan testPlan ON testPlan.id = testPlanScenario.test_plan_id) apiScene
ON apiScene.id = executionInfo.source_id
INNER JOIN api_scenario scene ON scene.id = apiScene.api_scenario_id
WHERE scene.project_id = #{projectId}
AND scene.`status` != 'Trash'
AND ( executionInfo.result = 'ERROR' )
AND executionInfo.create_time >= #{startTimestamp}
GROUP BY
scene.id,apiScene.testPlanId
<if test="selectFunctionCase == true">
UNION
SELECT
testCase.id AS testCaseID,
SELECT * FROM (
SELECT *
FROM (
SELECT testCase.testPlanCaseID AS testPlanCaseID,
testCase.id AS id,
testCase.`name` AS caseName,
testCasePlan.testPlanName AS testPlan,
testCasePlan.testPlanId AS testPlanId,
count( executionInfo.id ) AS failureTimes,
'testCase' AS caseType
FROM
function_case_execution_info executionInfo
INNER JOIN (
SELECT
testPlanTestCase.id,
testPlanTestCase.case_id,
testCase.testCaseName AS caseName,
testCase.testPlanName AS testPlan,
testCase.testPlanId AS testPlanId,
caseErrorCountData.dataCountNumber AS failureTimes,
'apiCase' AS caseType
FROM (SELECT testPlanCase.id AS testPlanCaseID,
apiCase.id AS id,
apiCase.`name` AS testCaseName,
testPlan.id AS testPlanId,
testPlan.`name` AS testPlanName
FROM
test_plan_test_case testPlanTestCase
INNER JOIN test_plan testPlan ON testPlan.id = testPlanTestCase.plan_id
) testCasePlan ON testCasePlan.id = executionInfo.source_id
INNER JOIN test_case testCase ON testCase.id = testCasePlan.case_id
WHERE
testCase.project_id = #{projectId}
AND testCase.`status` != 'Trash'
AND ( executionInfo.result = 'Failure' )
FROM api_test_case apiCase
INNER JOIN test_plan_api_case testPlanCase ON testPlanCase.api_case_id = apiCase.id
INNER JOIN test_plan testPlan ON testPlan.id = testPlanCase.test_plan_id
WHERE (
(apiCase.`status` IS NULL OR apiCase.`status` != 'Trash')
AND apiCase.project_id = #{projectId})) testCase
INNER JOIN (SELECT executionInfo.source_id AS sourceId,
COUNT(executionInfo.id) AS dataCountNumber
FROM api_case_execution_info executionInfo
INNER JOIN test_plan_api_case testPlanCase
ON executionInfo.source_id = testPlanCase.id
WHERE executionInfo.`result` = 'ERROR'
AND executionInfo.create_time > #{startTimestamp}
GROUP BY source_id) caseErrorCountData
ON caseErrorCountData.sourceId = testCase.testPlanCaseID
UNION
SELECT scene.id AS testCaseID,
scene.id AS id,
scene.`name` AS caseName,
apiScene.testPlanName AS testPlan,
apiScene.testPlanId AS testPlanId,
count(executionInfo.id) AS failureTimes,
'scenario' AS caseType
FROM scenario_execution_info executionInfo
INNER JOIN (SELECT testPlanScenario.id,
testPlanScenario.api_scenario_id,
testPlan.id AS testPlanId,
testPlan.`name` AS testPlanName
FROM test_plan_api_scenario testPlanScenario
INNER JOIN test_plan testPlan ON testPlan.id = testPlanScenario.test_plan_id) apiScene
ON apiScene.id = executionInfo.source_id
INNER JOIN api_scenario scene ON scene.id = apiScene.api_scenario_id
WHERE scene.project_id = #{projectId}
AND scene.`status` != 'Trash'
AND ( executionInfo.result = 'ERROR' )
AND executionInfo.create_time >= #{startTimestamp}
GROUP BY
testCase.id
</if>
) showTable
ORDER BY showTable.failureTimes DESC
scene.id,apiScene.testPlanId
<if test="selectFunctionCase == true">
UNION
SELECT
testCase.id AS testCaseID,
testCase.id AS id,
testCase.`name` AS caseName,
testCasePlan.testPlanName AS testPlan,
testCasePlan.testPlanId AS testPlanId,
count( executionInfo.id ) AS failureTimes,
'testCase' AS caseType
FROM
function_case_execution_info executionInfo
INNER JOIN (
SELECT
testPlanTestCase.id,
testPlanTestCase.case_id,
testPlan.id AS testPlanId,
testPlan.`name` AS testPlanName
FROM
test_plan_test_case testPlanTestCase
INNER JOIN test_plan testPlan ON testPlan.id = testPlanTestCase.plan_id
) testCasePlan ON testCasePlan.id = executionInfo.source_id
INNER JOIN test_case testCase ON testCase.id = testCasePlan.case_id
WHERE
testCase.project_id = #{projectId}
AND testCase.`status` != 'Trash'
AND ( executionInfo.result = 'Failure' )
AND executionInfo.create_time >= #{startTimestamp}
GROUP BY
testCase.id
</if>
) showTable
ORDER BY showTable.failureTimes DESC LIMIT ${limitNumber}
) pageTable
</select>
<select id="selectExecResult" resultType="java.lang.String">
select ader.status

View File

@ -1,12 +1,16 @@
package io.metersphere.controller.home;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.DubboProvider;
import io.metersphere.api.dto.JmxInfoDTO;
import io.metersphere.api.dto.RegistryCenter;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
import io.metersphere.api.dto.datacount.response.ApiDataCountDTO;
import io.metersphere.api.dto.datacount.response.CoveredDTO;
import io.metersphere.api.dto.datacount.response.ExecuteResultCountDTO;
import io.metersphere.api.dto.datacount.response.ExecutedCaseInfoDTO;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.export.ScenarioToPerformanceInfoDTO;
import io.metersphere.base.domain.ApiDefinition;
@ -15,6 +19,8 @@ import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.enums.ExecutionExecuteTypeEnum;
import io.metersphere.commons.utils.DataFormattingUtil;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.service.BaseScheduleService;
import io.metersphere.service.definition.ApiDefinitionExecResultService;
import io.metersphere.service.definition.ApiDefinitionService;
@ -26,6 +32,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -223,4 +230,28 @@ public class ApiHomeController {
returnDTO.setProjectEnvMap(projectEnvMap);
return returnDTO;
}
@GetMapping("/failure/case/about/plan/{projectId}/{selectFunctionCase}/{limitNumber}/{goPage}/{pageSize}")
public Pager<List<ExecutedCaseInfoDTO>> failureCaseAboutTestPlan(@PathVariable String projectId, @PathVariable boolean selectFunctionCase,
@PathVariable int limitNumber, @PathVariable int goPage, @PathVariable int pageSize) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
List<ExecutedCaseInfoResult> selectDataList = apiDefinitionExecResultService.findFailureCaseInfoByProjectIDAndLimitNumberInSevenDays(projectId, selectFunctionCase, limitNumber);
List<ExecutedCaseInfoDTO> returnList = new ArrayList<>(selectDataList.size());
for (int dataIndex = 0; dataIndex < selectDataList.size(); dataIndex++) {
ExecutedCaseInfoDTO dataDTO = new ExecutedCaseInfoDTO();
dataDTO.setSortIndex(dataIndex + 1);
ExecutedCaseInfoResult selectData = selectDataList.get(dataIndex);
dataDTO.setCaseID(selectData.getTestCaseID());
dataDTO.setCaseName(selectData.getCaseName());
dataDTO.setTestPlan(selectData.getTestPlan());
dataDTO.setFailureTimes(selectData.getFailureTimes());
dataDTO.setTestPlanId(selectData.getTestPlanId());
dataDTO.setCaseType(selectData.getCaseType());
dataDTO.setId(selectData.getId());
dataDTO.setTestPlanDTOList(selectData.getTestPlanDTOList());
returnList.add(dataDTO);
}
return PageUtils.setPageInfo(page, returnList);
}
}

View File

@ -373,7 +373,7 @@ public class ApiDefinitionExecResultService {
if (startTime == null) {
return new ArrayList<>(0);
} else {
List<ExecutedCaseInfoResult> list = extApiDefinitionExecResultMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, selectFunctionCase, startTime.getTime());
List<ExecutedCaseInfoResult> list = extApiDefinitionExecResultMapper.findFailureCaseInTestPlanByProjectIDAndExecuteTimeAndLimitNumber(projectId, selectFunctionCase, startTime.getTime(), limitNumber);
List<ExecutedCaseInfoResult> returnList = new ArrayList<>(limitNumber);

View File

@ -1,6 +1,9 @@
package io.metersphere.task.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.commons.utils.CronUtils;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.dto.TaskInfoResult;
import io.metersphere.request.BaseQueryRequest;
@ -44,8 +47,10 @@ public class TaskController {
return taskService.getRunningTasks(request);
}
@PostMapping("/runningTask/{projectID}")
public List<TaskInfoResult> runningTask(@PathVariable String projectID, @RequestBody BaseQueryRequest request) {
@PostMapping("/runningTask/{projectID}/{goPage}/{pageSize}")
public Pager<List<TaskInfoResult> >runningTask(@PathVariable String projectID, @PathVariable int goPage, @PathVariable int pageSize,
@RequestBody BaseQueryRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
List<TaskInfoResult> resultList = taskService.findRunningTaskInfoByProjectID(projectID, request);
int dataIndex = 1;
for (TaskInfoResult taskInfo :
@ -56,7 +61,7 @@ public class TaskController {
taskInfo.setNextExecutionTime(nextExecutionTime.getTime());
}
}
return resultList;
return PageUtils.setPageInfo(page, resultList);
}
@PostMapping(value = "/stop/batch")

View File

@ -6,4 +6,5 @@ package io.metersphere.constants;
*/
public class SystemCustomField {
public static final String ISSUE_STATUS = "状态";
public static final String ISSUE_DEGREE = "严重程度";
}

View File

@ -139,9 +139,11 @@ public class TestCaseReviewController {
testCaseReviewService.editTestReviewStatus(reviewId);
}
@PostMapping("/list/all/relate")
public List<TestReviewDTOWithMetric> listRelateAll(@RequestBody ReviewRelateRequest request) {
return testCaseReviewService.listRelateAll(request);
@PostMapping("/list/all/relate/{goPage}/{pageSize}")
public Pager<List<TestReviewDTOWithMetric>> listRelateAll(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ReviewRelateRequest request) {
testCaseReviewService.setReviewIds(request);
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testCaseReviewService.listRelateAll(request));
}
@PostMapping("/edit/follows")

View File

@ -47,6 +47,13 @@ public class TrackController {
statistics.setReviewRage(df.format(reviewed) + "%");
}
long reviewedTotal = statistics.getPassCount() + statistics.getUnPassCount();
if (reviewedTotal != 0) {
float reviewPass = (float) statistics.getPassCount() * 100 / (statistics.getPassCount() + statistics.getUnPassCount());
DecimalFormat df = new DecimalFormat("0.0");
statistics.setReviewPassRage(df.format(reviewPass) + "%");
}
statistics.setP0CountStr("P0&nbsp;&nbsp;<br/><br/>" + statistics.getP0CaseCountNumber());
statistics.setP1CountStr("P1&nbsp;&nbsp;<br/><br/>" + statistics.getP1CaseCountNumber());
statistics.setP2CountStr("P2&nbsp;&nbsp;<br/><br/>" + statistics.getP2CaseCountNumber());

View File

@ -10,7 +10,14 @@ import java.util.List;
@Setter
public class BugStatistics {
private long bugTotalSize;
private String rage;
private long bugUnclosedCount;
private long bugTotalCount;
private long caseTotalCount;
private long unClosedP0Size;
private long unClosedP1Size;
private long unClosedP2Size;
private long unClosedP3Size;
private String unClosedRage;
private String bugCaseRage;
private List<TestPlanBugCount> list = new ArrayList<>();
}

View File

@ -90,6 +90,10 @@ public class TrackStatisticsDTO {
* 评审率
*/
private String reviewRage = " 0%";
/**
* 评审通过率
*/
private String reviewPassRage = " 0%";
/**
* 未评审
*/

View File

@ -3,10 +3,13 @@ package io.metersphere.request.testreview;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ReviewRelateRequest {
private String type;
private String projectId;
private String workspaceId;
private List<String> reviewIds;
}

View File

@ -121,4 +121,16 @@ public class CustomFieldIssuesService extends CustomFieldResourceService {
Map<String, String> statusMap = customFieldIssues.stream().collect(Collectors.toMap(CustomFieldIssues::getResourceId, CustomFieldIssues::getValue));
return statusMap;
}
public Map<String, String> getIssueDegreeMap(List<String> issueIds, String projectId) {
if (CollectionUtils.isEmpty(issueIds)) {
return new HashMap<>(0);
}
CustomField customField = baseCustomFieldService.getCustomFieldByName(projectId, SystemCustomField.ISSUE_DEGREE);
CustomFieldIssuesExample example = new CustomFieldIssuesExample();
example.createCriteria().andFieldIdEqualTo(customField.getId()).andResourceIdIn(issueIds);
List<CustomFieldIssues> customFieldIssues = customFieldIssuesMapper.selectByExample(example);
Map<String, String> statusMap = customFieldIssues.stream().collect(Collectors.toMap(CustomFieldIssues::getResourceId, CustomFieldIssues::getValue));
return statusMap;
}
}

View File

@ -440,7 +440,7 @@ public class TestCaseReviewService {
request.setWorkspaceId(workspaceId);
request.setProjectId(projectId);
request.setReviewIds(extTestReviewCaseMapper.findRelateTestReviewId(user.getId(), workspaceId, projectId));
request.setReviewIds(relateRequest.getReviewIds());
List<TestReviewDTOWithMetric> testReviews = extTestCaseReviewMapper.listRelate(request);
@ -477,6 +477,10 @@ public class TestCaseReviewService {
return testReviews;
}
public void setReviewIds(ReviewRelateRequest request) {
request.setReviewIds(extTestReviewCaseMapper.findRelateTestReviewId(SessionUtils.getUserId(), request.getWorkspaceId(), request.getProjectId()));
}
private String getReviewName(List<String> userIds, String projectId) {
QueryMemberRequest queryMemberRequest = new QueryMemberRequest();
queryMemberRequest.setProjectId(projectId);

View File

@ -118,10 +118,19 @@ public class TrackService {
int index = 1;
int totalUnClosedPlanBugSize = 0;
int totalPlanBugSize = 0;
int totalCaseSize = 0;
int unClosedP0Size = 0;
int unClosedP1Size = 0;
int unClosedP2Size = 0;
int unClosedP3Size = 0;
for (TestPlan plan : plans) {
Map<String, Integer> bugSizeMap = getPlanBugSize(plan.getId(), projectId);
int planBugSize = bugSizeMap.get("total");
int unClosedPlanBugSize = bugSizeMap.get("unClosed");
unClosedP0Size += bugSizeMap.get("p0Size");
unClosedP1Size += bugSizeMap.get("p1Size");
unClosedP2Size += bugSizeMap.get("p2Size");
unClosedP3Size += bugSizeMap.get("p3Size");
totalUnClosedPlanBugSize += unClosedPlanBugSize;
totalPlanBugSize += planBugSize;
// bug为0不记录
@ -137,6 +146,7 @@ public class TrackService {
testPlanBug.setPlanId(plan.getId());
int planCaseSize = getPlanCaseSize(plan.getId());
totalCaseSize += planCaseSize;
testPlanBug.setCaseSize(planCaseSize);
testPlanBug.setBugSize(unClosedPlanBugSize);
@ -146,10 +156,20 @@ public class TrackService {
}
bugStatistics.setList(list);
bugStatistics.setBugUnclosedCount(totalUnClosedPlanBugSize);
bugStatistics.setBugTotalCount(totalPlanBugSize);
bugStatistics.setCaseTotalCount(totalCaseSize);
float rage = totalPlanBugSize == 0 ? 0 : (float) totalUnClosedPlanBugSize * 100 / totalPlanBugSize;
DecimalFormat df = new DecimalFormat("0.0");
bugStatistics.setRage(df.format(rage) + "%");
bugStatistics.setBugTotalSize(totalUnClosedPlanBugSize);
bugStatistics.setUnClosedRage(df.format(rage) + "%");
float caseRange = totalCaseSize == 0 ? 0 : (float) totalUnClosedPlanBugSize * 100 / totalCaseSize;
bugStatistics.setBugCaseRage(df.format(caseRange) + "%");
bugStatistics.setUnClosedP0Size(unClosedP0Size);
bugStatistics.setUnClosedP1Size(unClosedP1Size);
bugStatistics.setUnClosedP2Size(unClosedP2Size);
bugStatistics.setUnClosedP3Size(unClosedP3Size);
return bugStatistics;
}
@ -162,20 +182,51 @@ public class TrackService {
List<String> issueIds = extTestCaseMapper.getTestPlanBug(planId);
Map<String, String> statusMap = customFieldIssuesService.getIssueStatusMap(issueIds, projectId);
Map<String, String> degreeMap = customFieldIssuesService.getIssueDegreeMap(issueIds, projectId);
Map<String, Integer> bugSizeMap = new HashMap<>();
bugSizeMap.put("total", issueIds.size());
// 缺陷是否有状态
List<String> unClosedIds = new ArrayList<>();
if (MapUtils.isEmpty(statusMap)) {
unClosedIds = issueIds;
bugSizeMap.put("unClosed", issueIds.size());
return bugSizeMap;
} else {
unClosedIds = issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
.collect(Collectors.toList());
bugSizeMap.put("unClosed", unClosedIds.size());
}
int unClosedSize = (int) issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
.count();
bugSizeMap.put("unClosed", unClosedSize);
// 如果没有严重程度字段
int p0Size = 0;
int p1Size = 0;
int p2Size = 0;
int p3Size = 0;
if (MapUtils.isEmpty(degreeMap)) {
bugSizeMap.put("p0Size", unClosedIds.size());
} else {
for (String unClosedId : unClosedIds) {
String degree = degreeMap.getOrDefault(unClosedId, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY);
if (StringUtils.equalsIgnoreCase(degree, "P0")) {
p0Size += 1;
} else if (StringUtils.equalsIgnoreCase(degree, "P1")) {
p1Size += 1;
} else if (StringUtils.equalsIgnoreCase(degree, "P2")) {
p2Size += 1;
} else if (StringUtils.equalsIgnoreCase(degree, "P3")) {
p3Size += 1;
} else {
p0Size += 1;
}
}
}
bugSizeMap.put("p0Size", p0Size);
bugSizeMap.put("p1Size", p1Size);
bugSizeMap.put("p2Size", p2Size);
bugSizeMap.put("p3Size", p3Size);
return bugSizeMap;
}

View File

@ -2,6 +2,6 @@ import {post, get} from "@/business/utils/sdk-utils";
const BASE_URL = '/home/';
export function homeTestPlanFailureCaseGet(projectId, selectFunctionCase, limitNumber) {
return get(BASE_URL + `failure/case/about/plan/${projectId}/${selectFunctionCase}/${limitNumber}`);
export function homeTestPlanFailureCaseGet(projectId, selectFunctionCase, limitNumber, currentPage, pageSize) {
return get(BASE_URL + `failure/case/about/plan/${projectId}/${selectFunctionCase}/${limitNumber}/${currentPage}/${pageSize}`);
}

View File

@ -3,8 +3,8 @@ import {buildPagePath} from "@/api/base-network";
const BASE_URL = '/test/case/review/';
export function getRelateTestCaseReview(param) {
return post(BASE_URL + 'list/all/relate', param);
export function getRelateTestCaseReview(currentPage, pageSize, param) {
return post(BASE_URL + 'list/all/relate/' + currentPage + "/" + pageSize, param);
}
export function testReviewApiCaseList(page, param) {

View File

@ -12,11 +12,23 @@ export function getTrackCaseBar(selectProjectId) {
return get("/track/case/bar/" + selectProjectId);
}
export function getTrackRunningTask(selectProjectId, param) {
return post("/task/center/runningTask/" + selectProjectId, param);
export function getTrackRunningTask(selectProjectId, currentPage, pageSize, param) {
return post("/task/center/runningTask/" + selectProjectId + "/" + currentPage + "/" + pageSize, param);
}
export function getTrackBugCount(selectProjectId) {
return get("/track/bug/count/" + selectProjectId);
}
export function formatNumber(param) {
let num = (param || 0).toString(), result = '';
while (num.length > 3) {
result = ',' + num.slice(-3) + result;
num = num.slice(0, num.length - 3);
}
if (num) {
result = num + result;
}
return result;
}

View File

@ -715,7 +715,10 @@ export default {
break;
case 'notReviewed':
this.condition.filters.review_status = ['Prepare'];
break;
break
case 'reviewed':
this.condition.filters.review_status = ['UnPass', 'Pass'];
break
case 'reviewSuccess':
this.condition.filters.review_status = ['Pass'];
break;

View File

@ -1,45 +1,47 @@
<template>
<ms-container>
<ms-main-container v-loading="result.loading">
<el-row :gutter="5">
<el-col :span="6">
<div class="square">
<case-count-card :track-count-data="trackCountData" class="track-card" @redirectPage="redirectPage"/>
</div>
</el-col>
<el-col :span="6">
<div class="square">
<relevance-case-card :relevance-count-data="relevanceCountData" class="track-card"
@redirectPage="redirectPage"/>
</div>
</el-col>
<el-col :span="12">
<div class="square">
<case-maintenance :case-option="caseOption" class="track-card"/>
</div>
</el-col>
</el-row>
<div style="background-color:#F5F6F7">
<ms-container>
<ms-main-container style="overflow-y: hidden">
<div class="track-home-layout">
<el-row :gutter="16">
<el-col :span="12">
<case-count-card @redirectPage="redirectPage"/>
</el-col>
<el-col :span="12">
<relevance-case-card @redirectPage="redirectPage"/>
</el-col>
</el-row>
<el-row :gutter="5">
<el-col :span="12">
<bug-count-card class="track-card"/>
</el-col>
<el-col :span="12">
<ms-failure-test-case-list class="track-card" :select-function-case="true" @redirectPage="redirectPage"/>
</el-col>
</el-row>
<el-row :gutter="16" style="margin-top: 16px">
<el-col :span="12">
<bug-count-card />
</el-col>
<el-col :span="12">
<case-maintenance />
</el-col>
</el-row>
<el-row :gutter="5">
<el-col :span="12">
<review-list class="track-card"/>
</el-col>
<el-col :span="12">
<ms-running-task-list :call-from="'track_home'" class="track-card" @redirectPage="redirectPage"/>
</el-col>
</el-row>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<ms-failure-test-case-list :select-function-case="true" @redirectPage="redirectPage"/>
</el-col>
</el-row>
</ms-main-container>
</ms-container>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<review-list/>
</el-col>
</el-row>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<ms-running-task-list :call-from="'track_home'" @redirectPage="redirectPage"/>
</el-col>
</el-row>
</div>
</ms-main-container>
</ms-container>
</div>
</template>
<script>
@ -49,15 +51,12 @@ import MsContainer from "metersphere-frontend/src/components/MsContainer";
import CaseCountCard from "./components/CaseCountCard";
import RelevanceCaseCard from "./components/RelevanceCaseCard";
import CaseMaintenance from "./components/CaseMaintenance";
import {COUNT_NUMBER, COUNT_NUMBER_SHALLOW} from "metersphere-frontend/src/utils/constants";
import BugCountCard from "./components/BugCountCard";
import ReviewList from "./components/ReviewList";
import MsRunningTaskList from "./components/RunningTaskList";
import {getUUID} from "metersphere-frontend/src/utils";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import MsFailureTestCaseList from "@/business/home/components/FailureTestCaseList";
import {getTrackCaseBar, getTrackCount, getTrackRelevanceCount} from "@/api/track";
import {useStore} from "@/store";
require('echarts/lib/component/legend');
export default {
@ -75,97 +74,14 @@ export default {
},
data() {
return {
result: {},
trackCountData: {},
relevanceCountData: {},
caseOption: {},
}
},
activated() {
this.init();
},
computed: {
projectId() {
return getCurrentProjectID();
},
},
methods: {
init() {
let selectProjectId = this.projectId;
if (!selectProjectId) {
return;
}
getTrackCount(selectProjectId)
.then(r => {
this.trackCountData = r.data;
});
getTrackRelevanceCount(selectProjectId)
.then(r => {
this.relevanceCountData = r.data;
});
getTrackCaseBar(selectProjectId)
.then(r => {
let data = r.data;
this.setBarOption(data);
});
},
setBarOption(data) {
let xAxis = [];
data.map(d => {
if (!xAxis.includes(d.xAxis)) {
xAxis.push(d.xAxis);
}
});
let yAxis1 = data.filter(d => d.groupName === 'FUNCTIONCASE').map(d => [d.xAxis, d.yAxis]);
let yAxis2 = data.filter(d => d.groupName === 'RELEVANCECASE').map(d => [d.xAxis, d.yAxis]);
let store = useStore();
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: xAxis
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
axisTick: {
show: false
}
},
legend: {
data: [this.$t('test_track.home.function_case_count'), this.$t('test_track.home.relevance_case_count')],
orient: 'vertical',
right: '80',
},
series: [
{
name: this.$t('test_track.home.function_case_count'),
data: yAxis1,
type: 'bar',
barWidth: 50,
itemStyle: {
color: store.theme ? store.theme : COUNT_NUMBER
}
},
{
name: this.$t('test_track.home.relevance_case_count'),
data: yAxis2,
type: 'bar',
barWidth: 50,
color: store.theme ? store.theme : COUNT_NUMBER_SHALLOW
}]
};
this.caseOption = option;
},
redirectPage(page, dataType, selectType, title) {
//api
//UUID
@ -196,21 +112,84 @@ export default {
</script>
<style scoped>
.square {
:deep(.el-card__header) {
border: 0px;
padding: 24px;
}
:deep(.el-card__body) {
border: 0px;
padding: 0px;
margin: 0px 24px 24px 24px;
}
.track-home-layout {
margin: 12px 24px;
min-width: 1100px;
}
.track-home-layout :deep(.dashboard-title) {
font-size: 18px;
font-weight: 500;
}
.track-home-layout :deep(.common-amount) {
margin-top: 4px;
}
.track-home-layout :deep(.dashboard-card) {
height: 392px;
}
.track-home-layout :deep(.main-info) {
height: 197px;
}
.track-home-layout :deep(.main-info-card) {
height: 197px;
width: 100%;
height: 400px;
color: #646A73;
background-color: #FFFFFF;
box-sizing: border-box;
border: 1px solid #DEE0E3;
border-radius: 4px;
}
.rectangle {
width: 100%;
height: 400px;
.track-home-layout :deep(.addition-info) {
height: 86px;
margin: 16px 0px 0px 0px;
}
.el-row {
margin-bottom: 5px;
.track-home-layout :deep(.addition-info-title) {
line-height: 22px;
font-size: 14px;
font-weight: 400;
color: #646A73;
}
.track-card {
height: 100%;
.track-home-layout :deep(.addition-info-text) {
line-height: 28px;
color: #783887;
font-size: 20px;
font-weight: 500;
}
.track-home-layout :deep(.addition-info-num) {
line-height: 22px;
color: #783887;
font-size: 14px;
font-weight: 500;
}
.track-home-layout :deep(.home-table-cell) {
height: 38px;
background-color: #F5F6F7;
font-size: 14px;
font-weight: 500;
border: 1px solid rgba(31, 35, 41, 0.15);
border-right-width: 0;
border-left-width: 0;
color: #1F2329;
line-height: 22px;
}
</style>

View File

@ -1,114 +1,195 @@
<template>
<el-card class="table-card" v-loading="result.loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never" body-style="padding:10px 5px;">
<div slot="header">
<span class="title">
{{ $t('test_track.home.bug_count') }}
</span>
</div>
<el-container>
<el-aside width="120px">
<count-rectangle-chart :content="bugTotalSize"/>
<div>
{{ $t('test_track.home.percentage') }}
<span class="rage">
{{ rage }}
</span>
</div>
</el-aside>
<el-table border :data="tableData" class="adjust-table table-content" height="300">
<el-table-column prop="index" :label="$t('test_track.home.serial_number')"
width="60" show-overflow-tooltip/>
<el-table-column prop="planName" :label="$t('test_track.home.test_plan_name')"
width="130" show-overflow-tooltip>
<template v-slot:default="scope">
<el-link type="info" @click="goPlan(scope.row.planId)">
{{ scope.row.planName }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('commons.create_time')" width="160" show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
column-key="status"
:label="$t('test_track.plan.plan_status')"
show-overflow-tooltip>
<template v-slot:default="scope">
<span @click.stop="clickt = 'stop'">
<plan-status-table-item :value="scope.row.status"/>
</span>
</template>
</el-table-column>
<el-table-column prop="caseSize" :label="$t('test_track.home.case_size')"
show-overflow-tooltip/>
<el-table-column prop="bugSize" :label="$t('test_track.home.bug_size')"
show-overflow-tooltip/>
<el-table-column prop="passRage" :label="$t('test_track.home.passing_rate')"
show-overflow-tooltip/>
</el-table>
</el-container>
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<bug-count-chart :bug-data="bugData" ref="countChart" @redirectPage="redirectPage"/>
</div>
<div class="addition-info">
<el-row :gutter="16" style="margin: 0">
<el-col :span="12" style="padding-left: 0">
<hover-card
:title="$t('home.bug_dashboard.un_closed_range')"
:main-info="bugData.unClosedRage"
:tool-tip="unClosedBugRangeToolTip"
>
<!--遗留缺陷所有缺陷-->
<template v-slot:mouseOut>
<div style="margin:16px 0px 0px 16px">
<el-row>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.bug_dashboard.un_closed_count') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num">
{{ formatAmount(bugData.bugUnclosedCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.bug_dashboard.total_count') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num">
{{ formatAmount(bugData.bugTotalCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
<el-col :span="12" style="padding-left: 0">
<hover-card
:title="$t('home.bug_dashboard.un_closed_bug_case_range')"
:main-info="bugData.bugCaseRage"
:tool-tip="unClosedBugCaseRangeToolTip"
>
<!--遗留缺陷所有缺陷-->
<template v-slot:mouseOut>
<div style="margin:16px 0px 0px 16px">
<el-row>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.bug_dashboard.un_closed_count') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num">
{{ formatAmount(bugData.bugUnclosedCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.bug_dashboard.case_count') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num">
{{ formatAmount(bugData.caseTotalCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
</div>
</div>
</div>
</el-card>
</template>
<script>
import bugCountChart from "@/business/home/components/chart/BugCountChart";
import hoverCard from "@/business/home/components/card/HoverCard";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
import CountRectangleChart from "metersphere-frontend/src/components/chart/CountRectangleChart";
import {getTrackBugCount} from "@/api/track";
import {formatNumber} from "@/api/track"
export default {
name: "BugCountCard",
components: {
CountRectangleChart,
PlanStatusTableItem
},
components: {bugCountChart, hoverCard},
data() {
return {
tableData: [],
result: {},
bugTotalSize: 0,
rage: '0%'
loading: false,
loadError: false,
unClosedBugRangeToolTip: this.$t('home.bug_dashboard.un_closed_range_tips'),
unClosedBugCaseRangeToolTip: this.$t('home.bug_dashboard.un_closed_bug_case_range_tips'),
bugData: {
bugCaseRage:" 0%",
bugTotalCount: 0,
bugUnclosedCount: 0,
caseTotalCount: 0,
unClosedRage:" 0%",
unClosedP0Size: 0,
unClosedP1Size: 0,
unClosedP2Size: 0,
unClosedP3Size: 0,
},
}
},
methods: {
init() {
getTrackBugCount(getCurrentProjectID())
.then((res) => {
let data = res.data;
this.tableData = data.list;
this.bugTotalSize = data.bugTotalSize;
this.rage = data.rage;
});
},
goPlan(id) {
if (!id) {
return;
}
this.$router.push('/track/plan/view/' + id);
}
},
created() {
this.init()
},
activated() {
this.init()
this.search();
},
methods: {
search() {
this.loading = true;
this.loadError = false;
let selectProjectId = getCurrentProjectID();
getTrackBugCount(selectProjectId)
.then(r => {
this.loading = false;
this.loadError = false;
this.bugData = r.data;
}).catch(() => {
this.loading = false;
this.loadError = true;
this.$refs.countChart.reload();
})
},
formatAmount(number) {
return formatNumber(number);
},
redirectPage(clickType) {
this.$emit("redirectPage", "testCase", "relationCase", clickType);
}
}
}
</script>
<style scoped>
.detail-container {
margin-top: 30px;
}
.default-property {
font-size: 14px
}
.main-property {
color: #F39021;
font-size: 14px
}
.el-card :deep( .el-card__header ) {
border-bottom: 0px solid #EBEEF5;
}
.rage {
.count-info-div {
margin: 3px;
}
.count-info-div :deep( p ) {
font-size: 10px;
}
.info-tool-tip {
position: absolute;
top: 0;
}
.rows-count-number {
font-family: 'ArialMT', 'Arial', sans-serif;
font-size: 18px;
color: var(--count_number);
font-size: 14px;
color: var(--count_number) !important;
}
</style>

View File

@ -1,128 +1,166 @@
<template>
<el-card class="table-card" v-loading="result.loading" body-style="padding:10px 5px;">
<el-card class="table-card" shadow="never" body-style="padding: 10px 5px;">
<div slot="header">
<span class="title">
{{ $t('test_track.home.case_count') }}
</span>
</div>
<!--数值统计-->
<el-container>
<el-aside width="120px">
<count-rectangle-chart :content="trackCountData.allCaseCountNumber"/>
</el-aside>
<el-main style="padding-left: 0px;padding-right: 0px;display: block">
<div style="width:200px;float:right;margin:0;overflow: auto">
<el-row align="right">
<el-col :span="6"
style="border-right-style: solid;border-right-width: 1px;border-right-color: #ECEEF4;">
<div class="count-info-div" v-html="trackCountData.p0CountStr"></div>
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<case-count-chart :track-data="trackData" ref="countChart" @redirectPage="redirectPage"/>
</div>
<div class="addition-info">
<el-row :gutter="16" style="margin: 0">
<el-col :span="12" style="padding-left: 0">
<hover-card
:title="$t('home.rate.case_review')"
:main-info="trackData.reviewRage"
:tool-tip="caseReviewRangeToolTip"
>
<!--未评审已评审-->
<template v-slot:mouseOut>
<div style="margin:16px 0px 0px 16px">
<el-row>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.case_review_dashboard.not_review') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('notReviewed')">
{{ formatAmount(trackData.prepareCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.case_review_dashboard.finished_review') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('reviewed')">
{{ formatAmount(trackData.passCount + trackData.unPassCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
<el-col :span="6"
style="border-right-style: solid;border-right-width: 1px;border-right-color: #ECEEF4;">
<div class="count-info-div" v-html="trackCountData.p1CountStr"></div>
</el-col>
<el-col :span="6"
style="border-right-style: solid;border-right-width: 1px;border-right-color: #ECEEF4;">
<div class="count-info-div" v-html="trackCountData.p2CountStr"></div>
</el-col>
<el-col :span="6" style="">
<div class="count-info-div" v-html="trackCountData.p3CountStr"></div>
<el-col :span="12" style="padding-right:0">
<hover-card
:title="$t('home.rate.case_review_pass')"
:main-info="trackData.reviewPassRage"
:tool-tip="caseFinishedReviewPassRageToolTip"
>
<!--未通过, 已通过-->
<template v-slot:mouseOut>
<div style="margin:16px 0px 0px 16px">
<el-row>
<el-col :span="8">
<span class="addition-info-title">
{{ $t("home.case_review_dashboard.not_pass") }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('UnPass')">
{{ formatAmount(trackData.unPassCount) }}
</el-link>
</div>
</el-col>
<el-col :span="8">
<span class="addition-info-title">
{{ $t("home.case_review_dashboard.pass") }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('Pass')">
{{ formatAmount(trackData.passCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
</div>
</el-main>
</el-container>
<!-- 本周新增-->
<el-container class="detail-container">
<el-header style="height:20px;padding: 0px;margin-bottom: 0px;font-size: 14px">
<el-row>
<el-col>
{{ $t('api_test.home_page.api_details_card.this_week_add') }}
<el-link type="info" @click="redirectPage('thisWeekCount')" target="_blank" style="color: #000000">
{{ trackCountData.thisWeekAddedCount }}
</el-link>
{{ $t('api_test.home_page.unit_of_measurement') }}
</el-col>
</el-row>
</el-header>
<el-main style="padding:0px">
<el-row>
<el-col :span="8">&nbsp;</el-col>
</el-row>
</el-main>
</el-container>
<!-- 评审通过率 -->
<el-container class="detail-container">
<el-header style="height:20px;padding: 0px;margin-bottom: 5px;font-size: 14px">
<el-row>
<span style="float: left">
{{ $t('test_track.home.review_rate') + ":" }}&nbsp;&nbsp;
</span>
<span style="font-size: 14px">
<b>{{ trackCountData.reviewRage }}</b>
<el-tooltip placement="top" class="info-tool-tip">
<div slot="content">{{ $t('api_test.home_page.formula.review') }}</div>
<el-button icon="el-icon-info" style="padding:0px;border: 0px"></el-button>
</el-tooltip>
</span>
</el-row>
</el-header>
<el-main style="padding:0px">
<el-row>
<el-col :span="8">
<span class="default-property">
{{ $t('test_track.review.prepare') }}
<el-link class="rows-count-number" @click="redirectPage('notReviewed')" target="_blank">
<b>
{{ trackCountData.prepareCount }}
</b>
</el-link>
</span>
</el-col>
<el-col :span="8" class="itemIsCenter">
<span class="default-property">
{{ $t('test_track.review.un_pass') }}
<el-link class="rows-count-number" @click="redirectPage('reviewFail')" target="_blank">
<b>
{{ trackCountData.unPassCount }}
</b>
</el-link>
</span>
</el-col>
<el-col :span="8">
<span class="main-property" style="float: right">
{{ $t('test_track.review.pass') }}
<el-link class="rows-count-number" @click="redirectPage('reviewSuccess')" target="_blank">
<b>
{{ trackCountData.passCount }}
</b>
</el-link>
</span>
</el-col>
</el-row>
</el-main>
</el-container>
</div>
</div>
</el-card>
</template>
<script>
import MsInstructionsIcon from "metersphere-frontend/src/components/MsInstructionsIcon";
import CountRectangleChart from "metersphere-frontend/src/components/chart/CountRectangleChart";
import caseCountChart from "@/business/home/components/chart/CaseCountChart";
import hoverCard from "@/business/home/components/card/HoverCard";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getTrackCount} from "@/api/track";
import {formatNumber} from "@/api/track"
export default {
name: "CaseCountCard",
components: {CountRectangleChart, MsInstructionsIcon},
props: {
trackCountData: {},
},
components: {caseCountChart, hoverCard},
data() {
return {
result: {}
loading: false,
loadError: false,
caseReviewRangeToolTip: this.$t('api_test.home_page.formula.review'),
caseFinishedReviewPassRageToolTip: this.$t('home.dashboard.case_finished_review_pass_tip'),
trackData: {
allCaseCountNumber: 0,
allRelevanceCaseCount: 0,
apiCaseCount: 0,
apiCaseCountStr: "",
coverageCount: 0,
coverageRage: "0%",
p0CaseCountNumber: 0,
p1CaseCountNumber: 0,
p2CaseCountNumber: 0,
p3CaseCountNumber: 0,
passCount: 0,
performanceCaseCount: 0,
performanceCaseCountStr: "",
prepareCount: 0,
reviewRage: " 0%",
reviewPassRage: " 0%",
scenarioCaseCount: 0,
scenarioCaseStr: "",
thisWeekAddedCount: 0,
unPassCount: 0,
uncoverageCount: 0
},
}
},
activated() {
this.search();
},
methods: {
search() {
this.loading = true;
this.loadError = false;
let selectProjectId = getCurrentProjectID();
getTrackCount(selectProjectId)
.then(r => {
this.loading = false;
this.loadError = false;
this.trackData = r.data;
this.$refs.countChart.reload();
}).catch(() => {
this.loading = false;
this.loadError = true;
this.$refs.countChart.reload();
});
},
formatAmount(number) {
return formatNumber(number);
},
redirectPage(clickType) {
this.$emit("redirectPage", "testCase", "case", clickType);
}
@ -131,57 +169,4 @@ export default {
</script>
<style scoped>
.el-aside {
line-height: 100px;
text-align: center;
}
.rows-count-number {
font-family: 'ArialMT', 'Arial', sans-serif;
font-size: 14px;
color: var(--count_number) !important;
}
.detail-container {
margin-top: 30px;
}
.default-property {
font-size: 14px;
}
.main-property {
color: #F39021;
font-size: 14px
}
.el-card :deep( .el-card__header ) {
border-bottom: 0px solid #EBEEF5;
}
.count-info-div {
margin: 3px;
}
.count-info-div :deep( p ) {
font-size: 10px;
}
.info-tool-tip {
position: absolute;
top: 0;
}
.rows-count-number {
font-family: 'ArialMT', 'Arial', sans-serif;
font-size: 14px;
color: var(--count_number) !important;
}
.itemIsCenter {
display: flex;
justify-content: center; /*主轴上居中*/
align-items: center; /*侧轴上居中*/
}
</style>

View File

@ -1,29 +1,118 @@
<template>
<el-card class="table-card" v-loading="result.loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never" body-style="padding:10px;">
<div slot="header">
<span class="title">
{{ $t('test_track.home.case_maintenance') }}
</span>
</div>
<el-container>
<ms-chart ref="chart1" :options="caseOption" :autoresize="true" style="width: 100%;height: 340px"></ms-chart>
</el-container>
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<el-container>
<ms-chart ref="chart1" :options="caseOption" :autoresize="true" style="width: 100%;height: 323px"></ms-chart>
</el-container>
</div>
</div>
</el-card>
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getTrackCaseBar} from "@/api/track";
import {useStore} from "@/store";
export default {
name: "CaseMaintenance",
components: {MsChart},
props: {
caseOption: {}
},
data() {
return {
result: {}
loading: false,
loadError: false,
caseOption: {}
}
},
activated() {
this.search();
},
methods: {
search() {
this.loading = true;
this.loadError = false;
let selectProjectId = getCurrentProjectID();
getTrackCaseBar(selectProjectId)
.then(r => {
this.loading = false;
this.loadError = false;
let data = r.data;
this.setBarOption(data);
}).catch(() => {
this.loading = false;
this.loadError = true;
});
},
setBarOption(data) {
let xAxis = [];
data.map(d => {
if (!xAxis.includes(d.xAxis)) {
xAxis.push(d.xAxis);
}
});
let yAxis1 = data.filter(d => d.groupName === 'FUNCTIONCASE').map(d => [d.xAxis, d.yAxis]);
let yAxis2 = data.filter(d => d.groupName === 'RELEVANCECASE').map(d => [d.xAxis, d.yAxis]);
let store = useStore();
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'category',
data: xAxis
},
yAxis: {
type: 'value',
axisLine: {
show: false
},
axisTick: {
show: false
}
},
legend: {
data: [this.$t('test_track.home.function_case_count'), this.$t('test_track.home.relevance_case_count')],
orient: 'vertical',
right: '80',
},
series: [
{
name: this.$t('test_track.home.function_case_count'),
data: yAxis1,
type: 'bar',
barWidth: 16,
itemStyle: {
color: '#783887'
}
},
{
name: this.$t('test_track.home.relevance_case_count'),
data: yAxis2,
type: 'bar',
barWidth: 16,
color: '#F9CB2E'
}]
};
this.caseOption = option;
},
}
}
</script>

View File

@ -1,52 +1,56 @@
<template>
<el-card class="table-card" v-loading="loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never" v-loading="loading" body-style="padding:10px;">
<template v-slot:header>
<span class="title">
<span class="table-title">
{{ $t('api_test.home_page.failed_case_list.title') }}
</span>
</template>
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="sortIndex" :label="$t('home.case.index')"
width="100" show-overflow-tooltip/>
<el-table-column prop="caseName" :label="$t('home.case.case_name')"
width="150">
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.caseType,row.id)"
:disabled="(row.caseType === 'apiCase' && apiCaseReadOnly) || (row.caseType === 'scenario' && apiScenarioReadOnly) ||
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<el-table :data="tableData" class="adjust-table table-content"
:header-cell-style="{backgroundColor: '#F5F6F7'}" height="224px">
<el-table-column prop="sortIndex" :label="$t('home.case.index')" fixed show-overflow-tooltip/>
<el-table-column prop="caseName" :label="$t('home.case.case_name')" fixed>
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.caseType,row.id)"
:disabled="(row.caseType === 'apiCase' && apiCaseReadOnly) || (row.caseType === 'scenario' && apiScenarioReadOnly) ||
(row.caseType === 'load' && loadCaseReadOnly) || (row.caseType === 'testCase' && testCaseReadOnly)">
{{ row.caseName }}
</el-link>
</template>
</el-table-column>
<el-table-column
prop="caseType"
column-key="caseType"
:label="$t('home.case.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')"/>
<ms-tag v-if="scope.row.caseType === 'testCase'" effect="plain"
:content="$t('api_test.home_page.failed_case_list.table_value.case_type.functional')"/>
</template>
</el-table-column>
<el-table-column prop="testPlan" :label="$t('home.case.test_plan')">
<template v-slot:default="{row}">
<div>
<el-link type="info" @click="redirect('testPlanEdit',row.testPlanId)">
{{ row.testPlan }}
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="failureTimes" :label="$t('home.case.failure_times')"
width="110" show-overflow-tooltip/>
</el-table>
{{ row.caseName }}
</el-link>
</template>
</el-table-column>
<el-table-column
prop="caseType"
column-key="caseType"
:label="$t('home.case.case_type')"
fixed
show-overflow-tooltip>
<template v-slot:default="scope">
<basic-case-type-label :value="scope.row.caseType"></basic-case-type-label>
</template>
</el-table-column>
<el-table-column prop="testPlan" :label="$t('home.case.test_plan')">
<template v-slot:default="{row}">
<div>
<el-link type="info" @click="redirect('testPlanEdit',row.testPlanId)">
{{ row.testPlan }}
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="failureTimes" :label="$t('home.case.failure_times')"
fixed show-overflow-tooltip/>
</el-table>
<home-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize" layout="prev, pager, next, sizes"
:total="total"/>
</div>
</div>
</el-card>
</template>
@ -55,22 +59,28 @@ import MsTag from "metersphere-frontend/src/components/MsTag";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {homeTestPlanFailureCaseGet} from "@/api/remote/api/api-home";
import {hasPermission} from "@/business/utils/sdk-utils";
import HomePagination from "@/business/home/components/pagination/HomePagination";
import BasicCaseTypeLabel from "@/business/home/components/table/BasicCaseTypeLabel";
export default {
name: "MsFailureTestCaseList",
components: {
MsTag
MsTag, HomePagination, BasicCaseTypeLabel
},
data() {
return {
tableData: [],
loading: false,
loadError: false,
testCaseReadOnly: false,
apiCaseReadOnly: false,
apiScenarioReadOnly: false,
loadCaseReadOnly: false,
currentPage: 1,
pageSize: 5,
total: 0,
}
},
props: {
@ -85,10 +95,16 @@ export default {
search() {
if (this.projectId) {
this.loading = true;
homeTestPlanFailureCaseGet(this.projectId, this.selectFunctionCase, 10)
this.loadError = false;
homeTestPlanFailureCaseGet(this.projectId, this.selectFunctionCase, 10, this.currentPage, this.pageSize)
.then((r) => {
this.loading = false;
this.tableData = r.data;
this.loadError = false;
this.total = r.data.itemCount;
this.tableData = r.data.listObject;
}).catch(() => {
this.loading = false;
this.loadError = true;
});
}
},
@ -106,8 +122,6 @@ export default {
}
}
},
created() {
this.search();
this.testCaseReadOnly = !hasPermission('PROJECT_TRACK_CASE:READ');
@ -122,13 +136,14 @@ export default {
</script>
<style scoped>
.el-table {
cursor: pointer;
.table-title {
color: #1F2329;
font-weight: 500;
font-size: 18px!important;
line-height: 26px;
}
.el-card :deep(.el-card__header) {
.el-card :deep( .el-card__header ) {
border-bottom: 0px solid #EBEEF5;
}
</style>

View File

@ -1,113 +1,129 @@
<template>
<el-card class="table-card" v-loading="result.loading" body-style="padding:10px 5px;">
<el-card class="table-card" shadow="never" body-style="padding:10px 5px;">
<div slot="header">
<span class="title">
{{ $t('test_track.home.relevance_case') }}
</span>
</div>
<!--数值统计-->
<el-container>
<el-aside width="120px">
<count-rectangle-chart :content="relevanceCountData.allRelevanceCaseCount"/>
</el-aside>
<el-main style="padding-left: 0px;padding-right: 0px;display: block">
<div style="width:200px;float:right;margin:0 auto;overflow: auto">
<el-row align="right">
<el-col :span="8"
style="border-right-style: solid;border-right-width: 1px;border-right-color: #ECEEF4;">
<div class="count-info-div" v-html="relevanceCountData.apiCaseCountStr"></div>
</el-col>
<el-col :span="8"
style="border-right-style: solid;border-right-width: 1px;border-right-color: #ECEEF4;">
<div class="count-info-div" v-html="relevanceCountData.scenarioCaseStr"></div>
</el-col>
<el-col :span="8" style="">
<div class="count-info-div" v-html="relevanceCountData.performanceCaseCountStr"></div>
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<relevance-count-chart :relevance-data="relevanceData" ref="countChart" @redirectPage="redirectPage"/>
</div>
<div class="addition-info">
<el-row :gutter="24" style="margin: 0">
<el-col :span="24" style="padding-left: 0">
<hover-card
:title="$t('test_track.home.coverage')"
:main-info="relevanceData.coverageRage"
:tool-tip="coverRangeToolTip"
>
<!--未覆盖已覆盖-->
<template v-slot:mouseOut>
<div style="margin:16px 0px 0px 16px">
<el-row>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.relevance_dashboard.not_cover') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('uncoverage')">
{{ formatAmount(relevanceData.uncoverageCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<span class="addition-info-title">
{{ $t('home.relevance_dashboard.cover') }}
</span>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('coverage')">
{{ formatAmount(relevanceData.coverageCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
</div>
</el-main>
</el-container>
<!-- 本周新增-->
<el-container class="detail-container">
<el-header style="height:20px;padding: 0px;margin-bottom: 0px;font-size: 14px">
<el-row>
<el-col>
{{ $t('api_test.home_page.api_details_card.this_week_add') }}
<el-link type="info" @click="redirectPage('thisWeekRelevanceCount')" target="_blank" style="color: #000000">
{{ relevanceCountData.thisWeekAddedCount }}
</el-link>
{{ $t('api_test.home_page.unit_of_measurement') }}
</el-col>
</el-row>
</el-header>
<el-main style="padding:0px">
<el-row>
<el-col :span="8">&nbsp;</el-col>
</el-row>
</el-main>
</el-container>
<!-- 用例覆盖率率 -->
<el-container class="detail-container">
<el-header style="height:20px;padding: 0px;margin-bottom: 5px;font-size: 14px">
<el-row>
<span style="float: left">
{{ $t('test_track.home.coverage') + ":" }}&nbsp;&nbsp;
</span>
<span style="font-size: 14px">
<b>{{ relevanceCountData.coverageRage }}</b>
<el-tooltip placement="top" class="info-tool-tip">
<div slot="content">{{ $t('api_test.home_page.formula.testplan_coverage') }}</div>
<el-button icon="el-icon-info" style="padding:0px;border: 0px"></el-button>
</el-tooltip>
</span>
</el-row>
</el-header>
<el-main style="padding:0px">
<el-row>
<el-col :span="12">
<span class="default-property">
{{ $t('api_test.home_page.detail_card.uncoverage') }}
<el-link class="rows-count-number" @click="redirectPage('uncoverage')" target="_blank">
<b>
{{ relevanceCountData.uncoverageCount }}
</b>
</el-link>
</span>
</el-col>
<el-col :span="12">
<span class="main-property" style="float: right">
{{ $t('api_test.home_page.detail_card.coverage') }}
<el-link class="rows-count-number" @click="redirectPage('coverage')" target="_blank">
<b>
{{ relevanceCountData.coverageCount }}
</b>
</el-link>
</span>
</el-col>
</el-row>
</el-main>
</el-container>
</div>
</div>
</el-card>
</template>
<script>
import CountRectangleChart from "metersphere-frontend/src/components/chart/CountRectangleChart";
import relevanceCountChart from "@/business/home/components/chart/RelevanceCountChart";
import hoverCard from "@/business/home/components/card/HoverCard";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getTrackRelevanceCount} from "@/api/track";
import {formatNumber} from "@/api/track"
export default {
name: "RelevanceCaseCard",
components: {CountRectangleChart},
props: {
relevanceCountData: {},
},
components: {relevanceCountChart, hoverCard},
data() {
return {
result: {}
loading: false,
loadError: false,
coverRangeToolTip: this.$t('api_test.home_page.formula.testplan_coverage'),
relevanceData: {
allCaseCountNumber: 0,
allRelevanceCaseCount: 0,
apiCaseCount: 0,
apiCaseCountStr: "",
coverageCount: 0,
coverageRage: "0%",
p0CaseCountNumber: 0,
p1CaseCountNumber: 0,
p2CaseCountNumber: 0,
p3CaseCountNumber: 0,
passCount: 0,
performanceCaseCount: 0,
performanceCaseCountStr: "",
prepareCount: 0,
reviewRage: " 0%",
reviewPassRage: " 0%",
scenarioCaseCount: 0,
scenarioCaseStr: "",
thisWeekAddedCount: 0,
unPassCount: 0,
uncoverageCount: 0
},
}
},
activated() {
this.search();
},
methods: {
search() {
this.loading = true;
this.loadError = false;
let selectProjectId = getCurrentProjectID();
getTrackRelevanceCount(selectProjectId)
.then(r => {
this.loading = false;
this.loadError = false;
this.trackData = r.data;
this.relevanceData = r.data;
}).catch(() => {
this.loading = false;
this.loadError = true;
this.$refs.countChart.reload();
});
},
formatAmount(number) {
return formatNumber(number);
},
redirectPage(clickType) {
this.$emit("redirectPage", "testCase", "relationCase", clickType);
}

View File

@ -1,117 +1,130 @@
<template>
<el-card class="table-card" v-loading="loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never">
<div slot="header">
<span class="title">
<span class="table-title">
{{ $t('test_track.home.case_review') }}
</span>
<ms-table-button v-if="!showMyCreator" icon="el-icon-view"
:content="$t('test_track.review.my_create')" @click="searchMyCreator" style="float: right"/>
<ms-table-button v-if="showMyCreator" icon="el-icon-view"
:content="$t('test_track.review.reviewed_by_me')" @click="searchMyCreator" style="float: right"/>
<div class="btn-group">
<ms-table-button icon="" :class="!showMyCreator ? 'hover' : 'reviewedBtn'" :content="$t('test_track.review.reviewed_by_me')" @click="searchMyCreator" style="border-color: #FFFFFF"/>
<ms-table-button icon="" :class="showMyCreator ? 'hover' : 'createBtn'" :content="$t('test_track.review.my_create')" @click="searchMyCreator" style="border-color: #FFFFFF; margin-left: 3px"/>
</div>
</div>
<el-table
class="adjust-table"
border
:data="tableData"
@row-click="intoPlan"
v-loading="loading" height="300px">
<el-table-column
prop="name"
fixed
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="creator"
fixed
:label="$t('test_track.review.creator')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="reviewerName"
fixed
:label="$t('test_track.review.reviewer')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="status"
:label="$t('test_track.plan.plan_status')">
<template v-slot:default="scope">
<plan-status-table-item :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column
:label="$t('test_track.review.result_distribution')">
<template v-slot:default="scope">
<el-tooltip :content="getResultTip(scope.row.total,scope.row.reviewed,scope.row.pass)"
placement="top" :enterable="false" class="item" effect="dark">
<yan-progress :total="scope.row.total" :done="scope.row.reviewed" :modify="scope.row.pass" :tip="tip"/>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<el-table
class="adjust-table"
:data="tableData"
@row-click="intoPlan"
:header-cell-style="{backgroundColor: '#F5F6F7'}"
v-loading="loading" height="224px">
<el-table-column type="index" fixed :label="$t('home.table.index')" show-overflow-tooltip></el-table-column>
<el-table-column
prop="name"
fixed
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="status"
:label="$t('test_track.plan.plan_status')">
<template v-slot:default="scope">
<basic-status-label :value="scope.row.status"></basic-status-label>
</template>
</el-table-column>
<el-table-column
prop="creator"
fixed
:label="$t('test_track.review.creator')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="reviewerName"
fixed
:label="$t('test_track.review.reviewer')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
:label="$t('test_track.review.result_distribution')">
<template v-slot:default="scope">
<el-tooltip :content="getResultTip(scope.row.total,scope.row.reviewed,scope.row.pass)"
placement="top" :enterable="false" class="item" effect="dark">
<yan-progress :total="scope.row.total" :done="scope.row.reviewed" :modify="scope.row.pass" :tip="tip"/>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<home-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize" layout="prev, pager, next, sizes"
:total="total"/>
</div>
</div>
</el-card>
</template>
<script>
import MsTableOperator from "metersphere-frontend/src/components/MsTableOperator";
import PlanStageTableItem from "../../common/tableItems/plan/PlanStageTableItem";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
import HomeBaseComponent from "./HomeBaseComponent";
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {getRelateTestCaseReview} from "@/api/test-review";
import HomePagination from "@/business/home/components/pagination/HomePagination";
import BasicStatusLabel from "@/business/home/components/table/BasicStatusLabel";
import BasicStatus from "@/business/home/components/table/BasicStatusLabel";
export default {
name: "ReviewList",
components: {MsTableOperator, PlanStageTableItem, PlanStatusTableItem, HomeBaseComponent, MsTableButton},
components: {BasicStatus, MsTableOperator, HomeBaseComponent, MsTableButton, HomePagination, BasicStatusLabel},
data() {
return {
loading: false,
loadError: false,
tableData: [],
showMyCreator: false,
currentPage: 1,
pageSize: 5,
total: 0,
tip: [
{text: "X", fillStyle: '#D3D3D3'},
{text: "X", fillStyle: '#ee4545'},
{text: "X", fillStyle: '#4dcf4d'}
{text: "X", fillStyle: '#1F232926'},
{text: "X", fillStyle: '#F76964'},
{text: "X", fillStyle: '#AA4FBF'}
]
}
},
mounted() {
this.initTableData('');
this.search();
},
methods: {
initTableData(type) {
if (!type) {
type = 'reviewer'
}
search() {
let type = this.showMyCreator ? 'creator' : 'reviewer';
let projectId = getCurrentProjectID();
let workspaceId = getCurrentWorkspaceId();
if (!projectId) {
return;
}
let param = {type, projectId, workspaceId};
this.loading = true;
getRelateTestCaseReview(param)
.then((r) => {
this.loading = false;
this.tableData = r.data;
});
this.loadError = false;
getRelateTestCaseReview(this.currentPage, this.pageSize, param)
.then((r) => {
this.total = r.data.itemCount;
this.tableData = r.data.listObject;
this.loading = false;
this.loadError = false;
}).catch(() => {
this.loading = false;
this.loadError = true;
});
},
intoPlan(row) {
this.$router.push('/track/review/view/' + row.id);
},
searchMyCreator() {
this.showMyCreator = !this.showMyCreator;
if (this.showMyCreator) {
this.initTableData("creator");
} else {
this.initTableData("reviewer");
}
this.search();
},
getResultTip(total, reviewed, pass) {
return '通过: ' + pass + '; ' + '未通过: ' + (reviewed - pass) + '; ' + '未评审: ' + (total - reviewed);
@ -121,7 +134,40 @@ export default {
</script>
<style scoped>
.table-title {
color: #1F2329;
font-weight: 500;
font-size: 18px!important;
line-height: 26px;
}
.el-card :deep( .el-card__header ) {
border-bottom: 0px solid #EBEEF5;
}
.btn-group {
float: right;
background: #FFFFFF;
border: 1px solid #BBBFC4;
border-radius: 4px;
margin: 1px;
}
:deep(button.el-button.el-button--mini.is-plain.hover) {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
}
.reviewedBtn.el-button--mini.is-plain:hover {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
}
.createBtn.el-button--mini.is-plain:hover {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
}
</style>

View File

@ -1,72 +1,74 @@
<template>
<el-card class="table-card" v-loading="loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never">
<template v-slot:header>
<span class="title">
<span class="table-title">
{{ $t('api_test.home_page.running_task_list.title') }}
</span>
</template>
<ms-table
:enable-selection="false"
:condition="condition"
:data="tableData"
@refresh="search"
screen-height="300px">
<el-table-column prop="index" :label="$t('home.table.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">
<div v-loading="loading" element-loading-background="#FFFFFF">
<div v-show="loadError"
style="width: 100%; height: 300px; display: flex; flex-direction: column; justify-content: center;align-items: center">
<img style="height: 100px;width: 100px;"
src="/assets/figma/icon_load_error.svg"/>
<span class="addition-info-title" style="color: #646A73">{{ $t("home.dashboard.public.load_error") }}</span>
</div>
<div v-show="!loadError">
<el-table
:enable-selection="false"
:condition="condition"
:data="tableData"
:header-cell-style="{backgroundColor: '#F5F6F7'}"
@refresh="search" height="224px">
<el-table-column prop="index" :label="$t('home.table.index')" fixed show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('commons.name')" fixed>
<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('home.table.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('home.table.run_rule')" width="120"
show-overflow-tooltip/>
<el-table-column width="100" :label="$t('home.table.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('home.table.next_execution_time')">
<template v-slot:default="scope">
<span>{{ scope.row.nextExecutionTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column prop="creator" :label="$t('home.table.create_user')"
width="100" show-overflow-tooltip/>
<el-table-column width="170" :label="$t('home.table.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
</ms-table>
<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('home.table.task_type')" fixed>
<template v-slot:default="scope">
<basic-task-type-label :value="scope.row.taskGroup"></basic-task-type-label>
</template>
</ms-table-column>
<el-table-column prop="rule" :label="$t('home.table.run_rule')" fixed show-overflow-tooltip/>
<el-table-column fixed :label="$t('home.table.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 fixed :label="$t('home.table.next_execution_time')">
<template v-slot:default="scope">
<span>{{ scope.row.nextExecutionTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column prop="creator" :label="$t('home.table.create_user')"
fixed show-overflow-tooltip/>
<el-table-column fixed :label="$t('home.table.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
</el-table>
<home-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize" layout="prev, pager, next, sizes"
:total="total"/>
</div>
</div>
</el-card>
</template>
@ -77,14 +79,12 @@ import MsTable from "metersphere-frontend/src/components/table/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import {getTrackRunningTask} from "@/api/track";
import {updatePlanSchedule} from "@/api/remote/plan/test-plan";
import HomePagination from "@/business/home/components/pagination/HomePagination";
import BasicTaskTypeLabel from "@/business/home/components/table/BasicTaskTypeLabel";
export default {
name: "MsRunningTaskList",
components: {
MsTableColumn,
MsTable,
MsTag
},
components: { MsTableColumn, MsTable, MsTag, HomePagination, BasicTaskTypeLabel },
props: {
callFrom: String,
},
@ -94,6 +94,10 @@ export default {
tableData: [],
visible: false,
loading: false,
loadError: false,
currentPage: 1,
pageSize: 5,
total: 0,
typeFilters: [],
condition: {
filters: {}
@ -126,14 +130,19 @@ export default {
}
if (this.projectId) {
this.loading = true;
getTrackRunningTask(this.projectId, this.condition)
this.loadError = false;
getTrackRunningTask(this.projectId, this.currentPage, this.pageSize, this.condition)
.then((r) => {
this.loading = false;
this.tableData = r.data;
this.loadError = false;
this.total = r.data.itemCount;
this.tableData = r.data.listObject;
}).catch(() => {
this.loading = false;
this.loadError = true;
});
}
},
closeTaskConfirm(row) {
let flag = row.taskStatus;
row.taskStatus = !flag; //switch
@ -174,6 +183,12 @@ export default {
</script>
<style scoped>
.table-title {
color: #1F2329;
font-weight: 500;
font-size: 18px!important;
line-height: 26px;
}
.el-table {
cursor: pointer;
@ -182,5 +197,4 @@ export default {
.el-card :deep( .el-card__header ) {
border-bottom: 0px solid #EBEEF5;
}
</style>

View File

@ -0,0 +1,71 @@
<template>
<div class="hover-card-main"
@mouseleave="isHover=false">
<transition name="el-zoom-in-top">
<div v-show="!isHover" class="transition-box">
<div style="margin:16px 0 0 16px">
<span class="addition-info-title"> {{ title }}</span>
<el-tooltip class="item" effect="light" :content="toolTip" placement="top-start">
<img style="height: 14px;width: 14px;margin-left: 4px" src="/assets/figma/icon_question.svg"/>
</el-tooltip>
<div class="common-amount" @mouseenter="isHover=true">
<span class="addition-info-text">
{{ mainInfo }}
</span>
</div>
</div>
</div>
</transition>
<transition name="el-zoom-in-bottom">
<div v-show="isHover" class="transition-box-hover">
<slot name="mouseOut"></slot>
</div>
</transition>
</div>
</template>
<script>
export default {
name: "HoverCard",
data() {
return {
isHover: false,
}
},
props: {
title: String,
mainInfo: [String, Number],
toolTip: String,
},
methods: {}
}
</script>
<style scoped>
.hover-card-main {
box-sizing: border-box;
height: 86px;
background-color: #FFFFFF;
position: relative;
}
.transition-box {
height: 100%;
width: 100%;
background-color: #FFFFFF;
box-sizing: border-box;
position: absolute;
border: 1px solid #DEE0E3;
border-radius: 4px;
}
.transition-box-hover {
height: 100%;
width: 100%;
background-color: #F5F6F7;
box-sizing: border-box;
position: absolute;
border: 1px solid #DEE0E3;
border-radius: 4px;
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div class="main-info-card">
<div style="margin:16px">
<span class="main-info-card-title">
{{ title }}
</span>
<div style="margin-top: 4px;height: 40px">
<span v-if="isExecuteInfo" class="addition-num">{{ countData.executedTimesInWeek }}</span>
<span v-else class="main-num">{{ countData.total }}</span>
</div>
<div style="margin-top: 32px">
<div v-if="isExecuteInfo">
<span class="main-info-card-title">{{ $t("home.dashboard.public.executed_times") }}</span>
<div class="common-amount">
<span class="addition-num">
{{ countData.executedTimes }}
</span>
</div>
</div>
<div v-else>
<el-row>
<el-col :span="12">
<span class="main-info-card-title">{{ $t("home.dashboard.public.this_week") }}</span>
<div class="common-amount">
<el-button class="common-amount-button" @click="redirect('createdInWeek')">
<span type="num" class="addition-num">
+{{ countData.createdInWeek }}
</span>
<img class="main-info-card-right" src="/assets/figma/icon_right_outlined.svg" alt="">
</el-button>
</div>
</el-col>
<el-col :span="12">
<span class="main-info-card-title">{{ $t("home.dashboard.public.fake_error") }}</span>
<div class="common-amount">
<el-button class="common-amount-button" @click="redirect('fakeError')">
<span class="addition-num">
{{ countData.fakeErrorCount }}
</span>
<img class="main-info-card-right" src="/assets/figma/icon_right_outlined.svg" alt="">
</el-button>
</div>
</el-col>
</el-row>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "MainInfoCard",
props: {
title: String,
isExecuteInfo: Boolean,
countData: Object,
redirectPageName: String,
redirectDataType: String,
},
methods: {
redirect(seletDataType) {
this.$emit('redirectPage', this.redirectPageName, this.redirectDataType, seletDataType, null);
}
}
}
</script>
<style scoped>
.main-info-card {
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.addition-num {
font-size: 20px;
font-weight: 500;
line-height: 28px;
}
.main-num {
font-size: 32px;
font-weight: 500;
line-height: 40px;
}
.common-amount-button {
padding: 0 4px 0 4px;
border: 0;
margin: 0 -4px 0 -8px;
}
.common-amount-button :deep(.addition-num) {
margin: 0 -4px 0 4px;
}
.main-info-card-right {
height: 12px;
width: 12px;
}
.common-amount-button:focus img {
transform: translateY(-999999px);
filter: drop-shadow(#783887 0px 999999px);
}
.common-amount-button:hover img {
transform: translateY(-999999px);
filter: drop-shadow(#783887 0px 999999px);
}
</style>

View File

@ -0,0 +1,242 @@
<template>
<div v-if="reloadOver">
<el-row type="flex" justify="left" align="left">
<div style="height: 184px;width: 184px;margin-left: 30px;margin-right: 30px;">
<ms-chart :options="options"
:height="184"
:width="184"
:autoresize="true"/>
</div>
<!-- 总数统计 -->
<div style="margin: auto;width: 260px;padding-right: 30px">
<div class="count-row">
<span class="ms-point-p3"/>
<span class="count-title">P3</span>
<span class="count-value">
{{ formatAmount(bugData.unClosedP3Size) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-p2"/>
<span class="count-title">P2</span>
<span class="count-value">
{{ formatAmount(bugData.unClosedP2Size) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-p1"/>
<span class="count-title">P1</span>
<span class="count-value">
{{ formatAmount(bugData.unClosedP1Size) }}
</span>
</div>
<div class="count-row">
<div>
<span class="ms-point-p0"/>
<span class="count-title">P0</span>
<span class="count-value">
{{ formatAmount(bugData.unClosedP0Size) }}
</span>
</div>
</div>
</div>
</el-row>
</div>
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {getUUID} from "metersphere-frontend/src/utils";
import {formatNumber} from "@/api/track";
export default {
name: "CountChart",
components: {MsChart},
props: {
bugData: Object,
totalTime: Number,
isExport: {
type: Boolean,
default: false,
}
},
data() {
return {
reloadOver: true,
pieChartStyle: {
amountFontSize: 32,
},
}
},
created() {
},
methods: {
reload() {
this.reloadOver = false;
this.$nextTick(() => {
this.reloadOver = true;
});
},
getTotal() {
let total = 0;
if (this.bugData.unClosedP0Size) {
total += this.bugData.unClosedP0Size;
}
if (this.bugData.unClosedP1Size) {
total += this.bugData.unClosedP1Size;
}
if (this.bugData.unClosedP2Size) {
total += this.bugData.unClosedP2Size;
}
if (this.bugData.unClosedP3Size) {
total += this.bugData.unClosedP3Size;
}
return total;
},
getAmount() {
let total = this.getTotal();
if (total > 999999999) {
this.pieChartStyle.amountFontSize = 20;
} else if (total > 99999999) {
this.pieChartStyle.amountFontSize = 22;
} else if (total > 9999999) {
this.pieChartStyle.amountFontSize = 24;
} else if (total > 999999) {
this.pieChartStyle.amountFontSize = 26;
} else {
this.pieChartStyle.amountFontSize = 32;
}
total = this.formatAmount(total);
return total;
},
formatAmount(param) {
return formatNumber(param);
}
},
computed: {
options() {
let protocolData = [{value: 0}];
let colorArr = ['#DEE0E3'];
if (this.getTotal() > 0) {
colorArr = ['#F54A45', '#FFD131', '#C1A9C8', '#10CECE',]
protocolData = [
{value: this.bugData.unClosedP3Size, name: 'P3'},
{value: this.bugData.unClosedP2Size, name: 'P2'},
{value: this.bugData.unClosedP1Size, name: 'P1'},
{value: this.bugData.unClosedP0Size, name: 'P0'},
];
}
let optionData = {
color: colorArr,
tooltip: {
trigger: 'item'
},
title: {
text: "{mainTitle|" + this.$t("home.bug_dashboard.un_closed_bug_count") + "}\n\n{number|" + this.getAmount() + "}\n\n",
// subtext: this.$t("home.dashboard.public.this_week") + ": +" + this.relevanceData.thisWeekAddedCount + " >",
top: "center",
left: "center",
textStyle: {
rich: {
mainTitle: {
color: '#646A73',
fontSize: 12,
},
number: {
fontSize: this.pieChartStyle.amountFontSize,
fontWeight: 500,
fontStyle: "normal",
fontFamily: "PinfFang SC",
margin: "112px 0px 0px 2px0",
},
}
},
// sublink: "/#/track/case/all/" + getUUID() + "/case/thisWeekRelevanceCount",
// subtextStyle: {
// color: "#1F2329",
// fontSize: 12,
// width: 105,
// ellipsis: '... >',
// overflow: "truncate",
// },
itemGap: -60,
},
series: [
{
type: 'pie',
radius: ['75%', '96%'],
avoidLabelOverlap: false,
hoverAnimation: true,
label: {
show: false,
},
itemStyle: {
borderColor: "#FFF",
borderWidth: 3,
borderRadius: 1,
},
labelLine: {
show: false
},
data: protocolData,
}
]
};
return optionData;
},
},
}
</script>
<style scoped>
.count-row {
padding: 8px 0px 8px 0px;
}
.count-title {
color: #646A73;
font-size: 14px;
font-weight: 400;
}
.count-value {
color: #646A73;
float: right;
font-size: 14px;
font-weight: 500;
}
.ms-point-p3 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #F54A45;
}
.ms-point-p2 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #FFD131;
}
.ms-point-p1 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #C1A9C8;
}
.ms-point-p0 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #10CECE;
}
</style>

View File

@ -0,0 +1,241 @@
<template>
<div v-if="reloadOver">
<el-row type="flex" justify="left" align="left">
<div style="height: 184px;width: 184px;margin-left: 30px;margin-right: 30px;">
<ms-chart :options="options"
:height="184"
:width="184"
:autoresize="true"/>
</div>
<!-- 总数统计 -->
<div style="margin: auto;width: 260px;padding-right: 30px">
<div class="count-row">
<div>
<span class="ms-point-p0"/>
<span class="count-title">P0</span>
<span class="count-value">
{{ formatAmount(trackData.p0CaseCountNumber) }}
</span>
</div>
</div>
<div class="count-row">
<span class="ms-point-p1"/>
<span class="count-title">P1</span>
<span class="count-value">
{{ formatAmount(trackData.p1CaseCountNumber) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-p2"/>
<span class="count-title">P2</span>
<span class="count-value">
{{ formatAmount(trackData.p2CaseCountNumber) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-p3"/>
<span class="count-title">P3</span>
<span class="count-value">
{{ formatAmount(trackData.p3CaseCountNumber) }}
</span>
</div>
</div>
</el-row>
</div>
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {getUUID} from "metersphere-frontend/src/utils";
import {formatNumber} from "@/api/track";
export default {
name: "CaseCountChart",
components: {MsChart},
props: {
trackData: Object,
totalTime: Number,
isExport: {
type: Boolean,
default: false,
}
},
data() {
return {
reloadOver: true,
pieChartStyle: {
amountFontSize: 32,
},
}
},
created() {
},
methods: {
reload() {
this.reloadOver = false;
this.$nextTick(() => {
this.reloadOver = true;
});
},
getTotal() {
let total = 0;
if (this.trackData.p0CaseCountNumber) {
total += this.trackData.p0CaseCountNumber;
}
if (this.trackData.p0CaseCountNumber) {
total += this.trackData.p1CaseCountNumber;
}
if (this.trackData.p0CaseCountNumber) {
total += this.trackData.p2CaseCountNumber;
}
if (this.trackData.p0CaseCountNumber) {
total += this.trackData.p3CaseCountNumber;
}
return total;
},
getAmount() {
let total = this.getTotal();
if (total > 999999999) {
this.pieChartStyle.amountFontSize = 20;
} else if (total > 99999999) {
this.pieChartStyle.amountFontSize = 22;
} else if (total > 9999999) {
this.pieChartStyle.amountFontSize = 24;
} else if (total > 999999) {
this.pieChartStyle.amountFontSize = 26;
} else {
this.pieChartStyle.amountFontSize = 32;
}
total = this.formatAmount(total);
return total;
},
formatAmount(param) {
return formatNumber(param);
}
},
computed: {
options() {
let protocolData = [{value: 0}];
let colorArr = ['#DEE0E3'];
if (this.getTotal() > 0) {
colorArr = ['#AA4FBF', '#FFD131', '#10CECE', '#4261F6',]
protocolData = [
{value: this.trackData.p0CaseCountNumber, name: 'P0'},
{value: this.trackData.p1CaseCountNumber, name: 'P1'},
{value: this.trackData.p2CaseCountNumber, name: 'P2'},
{value: this.trackData.p3CaseCountNumber, name: 'P3'},
];
}
let optionData = {
color: colorArr,
tooltip: {
trigger: 'item'
},
title: {
text: "{mainTitle|" + this.$t("home.case_review_dashboard.case_count") + "}\n\n{number|" + this.getAmount() + "}\n\n",
subtext: this.$t("home.dashboard.public.this_week") + ": +" + this.trackData.thisWeekAddedCount + " >",
top: "center",
left: "center",
textStyle: {
rich: {
mainTitle: {
color: '#646A73',
fontSize: 12,
},
number: {
fontSize: this.pieChartStyle.amountFontSize,
fontWeight: 500,
fontStyle: "normal",
fontFamily: "PinfFang SC",
margin: "112px 0px 0px 2px0",
},
}
},
sublink: "/#/track/case/all/" + getUUID() + "/case/thisWeekCount",
subtextStyle: {
color: "#1F2329",
fontSize: 12,
width: 105,
ellipsis: '...',
overflow: "truncate",
},
itemGap: -60,
},
series: [
{
type: 'pie',
radius: ['70%', '96%'],
avoidLabelOverlap: false,
hoverAnimation: true,
label: {
show: false,
},
itemStyle: {
borderColor: "#FFF",
borderWidth: 3,
borderRadius: 1,
},
labelLine: {
show: false
},
data: protocolData,
}
]
};
return optionData;
},
},
}
</script>
<style scoped>
.count-row {
padding: 8px 0px 8px 0px;
}
.count-title {
color: #646A73;
font-size: 14px;
font-weight: 400;
}
.count-value {
color: #646A73;
float: right;
font-size: 14px;
font-weight: 500;
}
.ms-point-p0 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #AA4FBF;
}
.ms-point-p1 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #FFD131;
}
.ms-point-p2 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #10CECE;
}
.ms-point-p3 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #4261F6;
}
</style>

View File

@ -0,0 +1,223 @@
<template>
<div v-if="reloadOver">
<el-row type="flex" justify="left" align="left">
<div style="height: 184px;width: 184px;margin-left: 30px;margin-right: 30px;">
<ms-chart :options="options"
:height="184"
:width="184"
:autoresize="true"/>
</div>
<!-- 总数统计 -->
<div style="margin: auto;width: 260px;padding-right: 30px">
<div class="count-row">
<div>
<span class="ms-point-api"/>
<span class="count-title">{{ $t('home.relevance_dashboard.api_case') }}</span>
<span class="count-value">
{{ formatAmount(relevanceData.apiCaseCount) }}
</span>
</div>
</div>
<div class="count-row">
<span class="ms-point-scenario"/>
<span class="count-title">{{ $t('home.relevance_dashboard.scenario_case') }}</span>
<span class="count-value">
{{ formatAmount(relevanceData.scenarioCaseCount) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-performance"/>
<span class="count-title">{{ $t('home.relevance_dashboard.performance_case') }}</span>
<span class="count-value">
{{ formatAmount(relevanceData.performanceCaseCount) }}
</span>
</div>
</div>
</el-row>
</div>
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {getUUID} from "metersphere-frontend/src/utils";
import {formatNumber} from "@/api/track";
export default {
name: "RelevanceCountChart",
components: {MsChart},
props: {
relevanceData: Object,
totalTime: Number,
isExport: {
type: Boolean,
default: false,
}
},
data() {
return {
reloadOver: true,
pieChartStyle: {
amountFontSize: 32,
},
}
},
created() {
},
methods: {
reload() {
this.reloadOver = false;
this.$nextTick(() => {
this.reloadOver = true;
});
},
getTotal() {
let total = 0;
if (this.relevanceData.apiCaseCount) {
total += this.relevanceData.apiCaseCount;
}
if (this.relevanceData.scenarioCaseCount) {
total += this.relevanceData.scenarioCaseCount;
}
if (this.relevanceData.performanceCaseCount) {
total += this.relevanceData.performanceCaseCount;
}
return total;
},
getAmount() {
let total = this.getTotal();
if (total > 999999999) {
this.pieChartStyle.amountFontSize = 20;
} else if (total > 99999999) {
this.pieChartStyle.amountFontSize = 22;
} else if (total > 9999999) {
this.pieChartStyle.amountFontSize = 24;
} else if (total > 999999) {
this.pieChartStyle.amountFontSize = 26;
} else {
this.pieChartStyle.amountFontSize = 32;
}
total = this.formatAmount(total);
return total;
},
formatAmount(param) {
return formatNumber(param);
}
},
computed: {
options() {
let protocolData = [{value: 0}];
let colorArr = ['#DEE0E3'];
if (this.getTotal() > 0) {
colorArr = ['#AA4FBF', '#FFD131', '#10CECE', '#4261F6',]
protocolData = [
{value: this.relevanceData.apiCaseCount, name: this.$t('home.relevance_dashboard.api_case')},
{value: this.relevanceData.scenarioCaseCount, name: this.$t('home.relevance_dashboard.scenario_case')},
{value: this.relevanceData.performanceCaseCount, name: this.$t('home.relevance_dashboard.performance_case')}
];
}
let optionData = {
color: colorArr,
tooltip: {
trigger: 'item'
},
title: {
text: "{mainTitle|" + this.$t("home.relevance_dashboard.relevance_case_count") + "}\n\n{number|" + this.getAmount() + "}\n\n",
subtext: this.$t("home.dashboard.public.this_week") + ": +" + this.relevanceData.thisWeekAddedCount + " >",
top: "center",
left: "center",
textStyle: {
rich: {
mainTitle: {
color: '#646A73',
fontSize: 12,
},
number: {
fontSize: this.pieChartStyle.amountFontSize,
fontWeight: 500,
fontStyle: "normal",
fontFamily: "PinfFang SC",
margin: "112px 0px 0px 2px0",
},
}
},
sublink: "/#/track/case/all/" + getUUID() + "/case/thisWeekRelevanceCount",
subtextStyle: {
color: "#1F2329",
fontSize: 12,
width: 105,
ellipsis: '...',
overflow: "truncate",
},
itemGap: -60,
},
series: [
{
type: 'pie',
radius: ['70%', '96%'],
avoidLabelOverlap: false,
hoverAnimation: true,
label: {
show: false,
},
itemStyle: {
borderColor: "#FFF",
borderWidth: 3,
borderRadius: 1,
},
labelLine: {
show: false
},
data: protocolData,
}
]
};
return optionData;
},
},
}
</script>
<style scoped>
.count-row {
padding: 8px 0px 8px 0px;
}
.count-title {
color: #646A73;
font-size: 14px;
font-weight: 400;
}
.count-value {
color: #646A73;
float: right;
font-size: 14px;
font-weight: 500;
}
.ms-point-api {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #AA4FBF;
}
.ms-point-scenario {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #FFD131;
}
.ms-point-performance {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #10CECE;
}
</style>

View File

@ -0,0 +1,113 @@
<template>
<el-row type="flex" justify="end">
<div class="table-page">
<el-pagination
class="home-pagination"
background
:pager-count="5"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="pageSizes"
:page-size="pageSize"
:layout="layout"
:total="total">
</el-pagination>
</div>
</el-row>
</template>
<script>
export default {
name: "MsTablePagination",
props: {
page: Object,
currentPage: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 5
},
pageSizes: {
type: Array,
default: function () {
return [5, 10, 20, 50]
}
},
total: {
type: Number,
default: 0
},
layout: {
type: String,
default() {
return 'total, sizes, prev, pager, next, jumper';
}
},
change: Function
},
methods: {
handleSizeChange: function (size) {
this.$emit('update:pageSize', size)
this.change();
},
handleCurrentChange(current) {
this.$emit('update:currentPage', current)
this.change();
}
}
}
</script>
<style scoped>
.table-page {
padding-top: 10px;
}
.table-page {
padding-top: 10px;
}
:deep(.home-pagination.el-pagination.is-background .el-pager li) {
background-color: #FFFFFF;
color: #1F2329;
border: 1px solid #BBBFC4;
width: 28px;
height: 28px;
box-sizing: border-box;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
line-height: 26px;
}
:deep(.home-pagination.el-pagination.is-background .el-pager li:not(.disabled).active) {
background-color: #FFFFFF;
color: #783887;
border: 1px solid #783887;
}
:deep(.home-pagination.el-pagination.is-background .btn-next,) {
background-color: #FFFFFF;
border: 1px solid #BBBFC4;
width: 28px;
height: 28px;
box-sizing: border-box;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
line-height: 26px;
}
:deep(.home-pagination.el-pagination.is-background .btn-prev) {
background-color: #FFFFFF;
border: 1px solid #BBBFC4;
width: 28px;
height: 28px;
box-sizing: border-box;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
line-height: 26px;
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<span>
<el-tag v-if="value === 'apiCase'" class="status-label api">
{{ $t('api_test.home_page.failed_case_list.table_value.case_type.api') }}
</el-tag>
<el-tag v-if="value === 'scenario'" class="status-label scene">
{{ $t('api_test.home_page.failed_case_list.table_value.case_type.scene') }}
</el-tag>
<el-tag v-if="value === 'load'" class="status-label load">
{{ $t('api_test.home_page.failed_case_list.table_value.case_type.load') }}
</el-tag>
<el-tag v-if="value === 'testCase'" class="status-label functional">
{{ $t('api_test.home_page.failed_case_list.table_value.case_type.functional') }}
</el-tag>
</span>
</template>
<script>
export default {
name: "BasicStatus",
components: {},
props: {
value: {
type: String
}
}
}
</script>
<style scoped>
.status-label {
height: 24px;
font-weight: 400;
font-size: 14px;
line-height: 22px;
padding: 1px 6px;
border-radius: 2px;
border-color: transparent;
}
.api {
background-color: rgba(52, 199, 36, 0.2);
color: #2EA121;
}
.scene {
background-color: rgba(52, 199, 36, 0.2);
color: #2EA121;
}
.load {
background-color: rgba(255, 136, 0, 0.2);
color: #DE7802;
}
.functional {
background-color: rgba(51, 112, 255, 0.2);
color: #245BDB;
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<span>
<el-tag v-if="value === 'Prepare'" class="status-label prepare">
{{ $t('test_track.plan.plan_status_prepare') }}
</el-tag>
<el-tag v-if="value === 'Underway'" class="status-label underway">
{{ $t('test_track.plan.plan_status_running') }}
</el-tag>
<el-tag v-if="value === 'Finished'" class="status-label finished">
{{ $t('test_track.plan.plan_status_finished') }}
</el-tag>
<el-tag v-if="value === 'Completed'" class="status-label completed">
{{ $t('test_track.plan.plan_status_completed') }}
</el-tag>
<el-tag v-if="value === 'Trash'" class="status-label trash">
{{ $t('test_track.plan.plan_status_trash') }}
</el-tag>
<el-tag v-if="value === 'Archived'" class="status-label archived">
{{ $t('test_track.plan.plan_status_archived') }}
</el-tag>
</span>
</template>
<script>
export default {
name: "BasicStatus",
components: {},
props: {
value: {
type: String
}
}
}
</script>
<style scoped>
.status-label {
height: 24px;
font-weight: 400;
font-size: 14px;
line-height: 22px;
padding: 1px 6px;
border-radius: 2px;
border-color: transparent;
}
.completed {
background-color: rgba(52, 199, 36, 0.2);
color: #34C724;
}
.underway {
background-color: rgba(120, 56, 135, 0.2);
color: #783887;
}
.prepare {
background-color: rgba(31, 35, 41, 0.1);
color: #646A73;
}
.finished {
background-color: rgba(0, 214, 185, 0.2);
color: #078372;
}
.trash {
background-color: rgba(245, 74, 69, 0.2);
color: #D83931;
}
.archived {
background-color: rgba(255, 136, 0, 0.2);
color: #DE7802;
}
</style>

View File

@ -0,0 +1,52 @@
<template>
<span>
<el-tag v-if="value === 'API_SCENARIO_TEST'" class="status-label api">
{{ $t('api_test.home_page.running_task_list.scenario_schedule') }}
</el-tag>
<el-tag v-if="value === 'TEST_PLAN_TEST'" class="status-label plan">
{{ $t('api_test.home_page.running_task_list.test_plan_schedule') }}
</el-tag>
<el-tag v-if="value === 'SWAGGER_IMPORT'" class="status-label swagger">
{{ $t('api_test.home_page.running_task_list.swagger_schedule') }}
</el-tag>
</span>
</template>
<script>
export default {
name: "BasicStatus",
components: {},
props: {
value: {
type: String
}
}
}
</script>
<style scoped>
.status-label {
height: 24px;
font-weight: 400;
font-size: 14px;
line-height: 22px;
padding: 1px 6px;
border-radius: 2px;
border-color: transparent;
}
.api {
background-color: rgba(52, 199, 36, 0.2);
color: #2EA121;
}
.swagger {
background-color: rgba(255, 136, 0, 0.2);
color: #DE7802;
}
.plan {
background-color: rgba(51, 112, 255, 0.2);
color: #245BDB;
}
</style>

View File

@ -19,6 +19,45 @@ const message = {
case_type: "Case Type",
test_plan: "Test Plan",
failure_times: "Failure times",
},
rate: {
case_review: "Review rate",
case_review_pass: "Review pass rate",
cover: "Cover rate",
legacy: "Legacy rate",
legacy_issue: "Percentage of legacy defects",
},
dashboard: {
public: {
this_week: "This week ",
load_error: "Loading failure",
},
case_finished_review_pass_tip: "Reviewed cases/All reviewed cases *100%"
},
case_review_dashboard: {
case_count: "Case count",
not_review: "Not reviewed",
finished_review: "Reviewed",
not_pass: "Not pass",
pass: "Pass",
},
relevance_dashboard: {
api_case: "Api case",
scenario_case: "Scenario case",
performance_case: "Performance case",
relevance_case_count: "Relevance case count",
not_cover: "Not cover",
cover: "Cover",
},
bug_dashboard: {
un_closed_bug_count: "Unclosed bug count",
un_closed_range: "Unclosed bug range",
un_closed_range_tips: "Unclosed bugs/all associated bugs *100%",
un_closed_bug_case_range: "Unclosed bug case range",
un_closed_bug_case_range_tips: "Unclosed bugs/all associated cases *100%",
un_closed_count: "Unclosed bug count",
total_count: "Bug total",
case_count: "Case count",
}
},
plan: {

View File

@ -19,6 +19,45 @@ const message = {
case_type: "用例类型",
test_plan: "所属测试计划",
failure_times: "失败次数",
},
rate: {
case_review: "评审率",
case_review_pass: "评审通过率",
cover: "覆盖率",
legacy: "遗留率",
legacy_issue: "遗留缺陷占比",
},
dashboard: {
public: {
this_week: "本周",
load_error: "加载失败",
},
case_finished_review_pass_tip: "已评审通过的案例/所有完成评审的案例*100%"
},
case_review_dashboard: {
case_count: "用例数量",
not_review: "未评审",
finished_review: "已评审",
not_pass: "未通过",
pass: "已通过",
},
relevance_dashboard: {
api_case: "接口用例",
scenario_case: "场景用例",
performance_case: "性能用例",
relevance_case_count: "关联用例数量",
not_cover: "未覆盖",
cover: "已覆盖",
},
bug_dashboard: {
un_closed_bug_count: "遗留缺陷",
un_closed_range: "遗留率",
un_closed_range_tips: "未关闭缺陷/所有关联的缺陷*100%",
un_closed_bug_case_range: "遗留缺陷占比",
un_closed_bug_case_range_tips: "未关闭缺陷/所有关联的用例*100%",
un_closed_count: "遗留缺陷数",
total_count: "缺陷总数",
case_count: "用例总数",
}
},
plan: {

View File

@ -19,6 +19,45 @@ const message = {
case_type: "用例類型",
test_plan: "所屬測試計劃",
failure_times: "失敗次數",
},
rate: {
case_review: "評審率",
case_review_pass: "評審通過率",
cover: "覆蓋率",
legacy: "遺留率",
legacy_issue: "遺留缺陷佔比",
},
dashboard: {
public: {
this_week: "本週",
load_error: "加載失敗",
},
case_finished_review_pass_tip: "已評審通過的案例/所有完成評審的案例*100%"
},
case_review_dashboard: {
case_count: "用例數量",
not_review: "未評審",
finished_review: "已評審",
not_pass: "未通過",
pass: "已通過",
},
relevance_dashboard: {
api_case: "接口用例",
scenario_case: "場景用例",
performance_case: "性能用例",
relevance_case_count: "關聯用例數量",
not_cover: "未覆蓋",
cover: "已覆蓋",
},
bug_dashboard: {
un_closed_bug_count: "遺留缺陷",
un_closed_range: "遺留率",
un_closed_range_tips: "未關閉缺陷/所有關聯的缺陷*100%",
un_closed_bug_case_range: "遺留缺陷佔比",
un_closed_bug_case_range_tips: "未關閉缺陷/所有關聯的用例*100%",
un_closed_count: "遺留缺陷數",
total_count: "缺陷總數",
case_count: "用例總數",
}
},
plan: {