refactor(测试跟踪): 优化首页样式及展示面板

This commit is contained in:
song-cc-rock 2022-11-18 11:07:06 +08:00
parent ef3a211e19
commit 202e85f23f
30 changed files with 771 additions and 1031 deletions

View File

@ -351,15 +351,15 @@ export default {
test_rate: "Test Rate",
tested_case: "Tested Case",
review_progress: "Review Progress",
case_count: "Statistics of the number of use cases",
relevance_case: "Related Case",
case_maintenance: "Use case person responsible distribution",
bug_count: "Statistics of test plan remaining defects",
case_count: "Statistics of cases",
relevance_case: "Statistics of related cases",
case_maintenance: "Statistics of case maintenance",
bug_count: "Statistics of test plan unclosed bugs",
case_review: "Use case review",
review_rate: "Reviewed",
coverage: "Coverage",
function_case_count: "Functional Case Count",
relevance_case_count: "Related Case Count",
function_case_count: "Functional Case",
relevance_case_count: "Related Case",
serial_number: "Index",
test_plan_name: "Plan Name",
case_size: "Case Count",

View File

@ -66,11 +66,19 @@ public class IssuesRequest extends BaseQueryRequest {
private List<String> exportIds;
/**
* 本周遗留缺陷
* 本周测试计划遗留缺陷
*/
private Boolean thisWeekUnClosedIssue = false;
private Boolean thisWeekUnClosedTestPlanIssue = false;
/**
* 本周遗留缺陷ID
* 测试计划遗留的缺陷
*/
private List<String> thisWeekUncloseIds;
private Boolean unClosedTestPlanIssue = false;
/**
* 测试计划关联所有缺陷
*/
private Boolean allTestPlanIssue = false;
/**
* 过滤缺陷ID
*/
private List<String> filterIds;
}

View File

@ -48,4 +48,6 @@ public interface ExtIssuesMapper {
Long getThisWeekIssueCount(@Param("ids") List<String> ids, @Param("projectId") String projectId);
List<String> getTestPlanThisWeekIssue(String projectId);
List<String> getTestPlanIssue(String projectId);
}

View File

@ -179,6 +179,15 @@
and tp.project_id = #{projectId}
</select>
<select id="getTestPlanIssue" resultType="java.lang.String">
select distinct tci.issues_id
from test_plan_test_case tptc
join test_plan tp on tp.id = tptc.plan_id
join test_case_issues tci on tptc.id = tci.resource_id
join issues on tci.issues_id = issues.id
where tptc.is_del != 1 and tp.project_id = #{projectId}
</select>
<select id="getIssueCustomFields" resultType="io.metersphere.xpack.track.dto.IssuesDao">
select cfi.field_id as fieldId,
cf.type fieldType,
@ -247,9 +256,9 @@
#{value}
</foreach>
</if>
<if test="request.thisWeekUnClosedIssue and request.thisWeekUncloseIds != null and request.thisWeekUncloseIds.size() > 0">
<if test="request.filterIds != null and request.filterIds.size() > 0">
and issues.id in
<foreach collection="request.thisWeekUncloseIds" item="value" separator="," open="(" close=")">
<foreach collection="request.filterIds" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</if>

View File

@ -0,0 +1,42 @@
package io.metersphere.constants;
import org.apache.commons.lang3.StringUtils;
public enum IssueStatus {
status_new("new", "new"),
status_resolved("resolved", "resolved"),
status_closed("closed", "closed"),
status_active("active", "active"),
status_delete("delete", "delete"),
status_in_progress("in_progress", "in_progress"),
status_rejected("rejected", "rejected"),
status_upcoming("upcoming", "upcoming"),
status_reopened("reopened", "reopened");
private String name;
private String i18nKey;
IssueStatus(String name, String i18nKey) {
this.name = name;
this.i18nKey = i18nKey;
}
public String getName() {
return name;
}
public String getI18nKey() {
return i18nKey;
}
public static IssueStatus getEnumByName(String name) {
IssueStatus[] issueStatus = IssueStatus.values();
for (int i = 0; i < issueStatus.length; i++) {
if (StringUtils.equals(issueStatus[i].getName(), name)) {
return issueStatus[i];
}
}
return null;
}
}

View File

@ -48,9 +48,7 @@ public class IssuesController {
@PostMapping("/list/{goPage}/{pageSize}")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_ISSUE_READ)
public Pager<List<IssuesDao>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody IssuesRequest request) {
if (request.getThisWeekUnClosedIssue()) {
issuesService.setThisWeekUnclosedIds(request);
}
issuesService.setFilterIds(request);
Page<List<Issues>> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, issuesService.list(request));
}

View File

@ -54,10 +54,6 @@ public class TrackController {
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());
statistics.setP3CountStr("P3&nbsp;&nbsp;<br/><br/>" + statistics.getP3CaseCountNumber());
return statistics;
}

View File

@ -5,6 +5,7 @@ import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Getter
@Setter
@ -21,4 +22,5 @@ public class BugStatistics {
private String unClosedRage;
private String bugCaseRage;
private List<TestPlanBugCount> list = new ArrayList<>();
private Map<String, Integer> chartData;
}

View File

@ -1,12 +1,15 @@
package io.metersphere.dto;
import io.metersphere.excel.converter.TestReviewCaseStatus;
import io.metersphere.i18n.Translator;
import io.metersphere.request.testcase.TrackCount;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 用例数量统计数据
@ -107,33 +110,31 @@ public class TrackStatisticsDTO {
*/
private long unPassCount = 0;
/**
* 面板数据
*/
private Map<String, Integer> chartData;
/**
* 按照 Priority 统计
* @param trackCountResults 统计结果
*/
public void countPriority(List<TrackCountResult> trackCountResults) {
Map<String, Integer> chartData = new HashMap<>();
for (TrackCountResult countResult : trackCountResults) {
if (StringUtils.isNotBlank(countResult.getGroupField())) {
switch (countResult.getGroupField().toUpperCase()){
case TrackCount.P0:
this.p0CaseCountNumber += countResult.getCountNumber();
break;
case TrackCount.P1:
this.p1CaseCountNumber += countResult.getCountNumber();
break;
case TrackCount.P2:
this.p2CaseCountNumber += countResult.getCountNumber();
break;
case TrackCount.P3:
this.p3CaseCountNumber += countResult.getCountNumber();
break;
default:
break;
Integer count = chartData.get(countResult.getGroupField());
if (count == null) {
chartData.put(countResult.getGroupField(), (int) countResult.getCountNumber());
} else {
count += (int) countResult.getCountNumber();
chartData.put(countResult.getGroupField(), count);
}
}
this.allCaseCountNumber += countResult.getCountNumber();
}
this.chartData = chartData;
}
public void countStatus(List<TrackCountResult> statusResults) {
@ -149,24 +150,38 @@ public class TrackStatisticsDTO {
}
public void countRelevance(List<TrackCountResult> relevanceResults) {
Map<String, Integer> chartData = new HashMap<>();
for (TrackCountResult countResult : relevanceResults) {
switch (countResult.getGroupField()){
case TrackCount.TESTCASE:
this.apiCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break;
case TrackCount.PERFORMANCE:
this.performanceCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break;
case TrackCount.AUTOMATION:
this.scenarioCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break;
default:
break;
if (StringUtils.equalsIgnoreCase(TrackCount.TESTCASE, countResult.getGroupField())) {
Integer count = chartData.get(Translator.get("api_case"));
if (count == null) {
chartData.put(Translator.get("api_case"), (int) countResult.getCountNumber());
} else {
count += (int) countResult.getCountNumber();
chartData.put(Translator.get("api_case"), count);
}
}
if (StringUtils.equalsIgnoreCase(TrackCount.PERFORMANCE, countResult.getGroupField())) {
Integer count = chartData.get(Translator.get("performance_case"));
if (count == null) {
chartData.put(Translator.get("performance_case"), (int) countResult.getCountNumber());
} else {
count += (int) countResult.getCountNumber();
chartData.put(Translator.get("performance_case"), count);
}
}
if (StringUtils.equalsIgnoreCase(TrackCount.AUTOMATION, countResult.getGroupField())) {
Integer count = chartData.get(Translator.get("scenario_case"));
if (count == null) {
chartData.put(Translator.get("scenario_case"), (int) countResult.getCountNumber());
} else {
count += (int) countResult.getCountNumber();
chartData.put(Translator.get("scenario_case"), count);
}
}
this.allRelevanceCaseCount += countResult.getCountNumber();
}
this.chartData = chartData;
}
public void countCoverage(List<TrackCountResult> coverageResults) {

View File

@ -1456,16 +1456,26 @@ public class IssuesService {
});
}
public void setThisWeekUnclosedIds(IssuesRequest request) {
List<String> issueIds = extIssuesMapper.getTestPlanThisWeekIssue(request.getProjectId());
public void setFilterIds(IssuesRequest request) {
List<String> issueIds = new ArrayList<>();
if (request.getThisWeekUnClosedTestPlanIssue()) {
issueIds = extIssuesMapper.getTestPlanThisWeekIssue(request.getProjectId());
} else if (request.getAllTestPlanIssue() || request.getUnClosedTestPlanIssue()) {
issueIds = extIssuesMapper.getTestPlanIssue(request.getProjectId());
}
Map<String, String> statusMap = customFieldIssuesService.getIssueStatusMap(issueIds, request.getProjectId());
if (MapUtils.isEmpty(statusMap)) {
request.setThisWeekUncloseIds(issueIds);
request.setFilterIds(issueIds);
} else {
List<String> unClosedIds = issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
.collect(Collectors.toList());
request.setThisWeekUncloseIds(unClosedIds);
if (request.getThisWeekUnClosedTestPlanIssue() || request.getUnClosedTestPlanIssue()) {
List<String> unClosedIds = issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
.collect(Collectors.toList());
request.setFilterIds(unClosedIds);
} else {
request.setFilterIds(issueIds);
}
}
}
}

View File

@ -1,15 +1,21 @@
package io.metersphere.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.CustomField;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.TestPlanExample;
import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.constants.IssueStatus;
import io.metersphere.constants.SystemCustomField;
import io.metersphere.dto.BugStatistics;
import io.metersphere.dto.TestPlanBugCount;
import io.metersphere.dto.TestPlanDTOWithMetric;
import io.metersphere.dto.TrackCountResult;
import io.metersphere.i18n.Translator;
import io.metersphere.plan.dto.ChartsData;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.xpack.track.dto.IssuesDao;
@ -37,6 +43,8 @@ public class TrackService {
@Resource
private CustomFieldIssuesService customFieldIssuesService;
@Resource
private BaseCustomFieldService baseCustomFieldService;
@Resource
private TestPlanService testPlanService;
@Resource
@ -110,27 +118,21 @@ public class TrackService {
}
public BugStatistics getBugStatistics(String projectId) {
List<TestPlanBugCount> list = new ArrayList<>();
BugStatistics bugStatistics = new BugStatistics();
TestPlanExample example = new TestPlanExample();
example.createCriteria().andProjectIdEqualTo(projectId);
List<TestPlan> plans = testPlanMapper.selectByExample(example);
List<TestPlanBugCount> list = new ArrayList<>();
BugStatistics bugStatistics = new BugStatistics();
List<String> planIds = plans.stream().map(TestPlan::getId).collect(Collectors.toList());
int index = 1;
int totalUnClosedPlanBugSize = 0;
int totalPlanBugSize = 0;
int newCount = 0;
int resolvedCount = 0;
int rejectedCount = 0;
int unKnownCount = 0;
int thisWeekCount = 0;
Map<String, Integer> bugStatusMap = getPlanBugStatusSize(planIds, projectId);
for (TestPlan plan : plans) {
Map<String, Integer> bugSizeMap = getPlanBugSize(plan.getId(), projectId);
int planBugSize = bugSizeMap.get("total");
int unClosedPlanBugSize = bugSizeMap.get("unClosed");
newCount += bugSizeMap.get("newCount");
resolvedCount += bugSizeMap.get("resolvedCount");
rejectedCount += bugSizeMap.get("rejectedCount");
unKnownCount += bugSizeMap.get("unKnownCount");
thisWeekCount += bugSizeMap.get("thisWeekCount");
totalUnClosedPlanBugSize += unClosedPlanBugSize;
totalPlanBugSize += planBugSize;
@ -138,7 +140,6 @@ public class TrackService {
if (unClosedPlanBugSize == 0) {
continue;
}
TestPlanBugCount testPlanBug = new TestPlanBugCount();
testPlanBug.setIndex(index++);
testPlanBug.setPlanName(plan.getName());
@ -158,12 +159,8 @@ public class TrackService {
float rage = totalPlanBugSize == 0 ? 0 : (float) totalUnClosedPlanBugSize * 100 / totalPlanBugSize;
DecimalFormat df = new DecimalFormat("0.0");
bugStatistics.setUnClosedRage(df.format(rage) + "%");
bugStatistics.setNewCount(newCount);
bugStatistics.setResolvedCount(resolvedCount);
bugStatistics.setRejectedCount(rejectedCount);
bugStatistics.setUnKnownCount(unKnownCount);
bugStatistics.setThisWeekCount(thisWeekCount);
bugStatistics.setChartData(bugStatusMap);
return bugStatistics;
}
@ -177,13 +174,11 @@ public class TrackService {
Map<String, String> statusMap = customFieldIssuesService.getIssueStatusMap(issueIds, projectId);
Map<String, Integer> bugSizeMap = new HashMap<>();
bugSizeMap.put("total", issueIds.size());
// 缺陷是否有状态
List<String> unClosedIds;
if (MapUtils.isEmpty(statusMap)) {
unClosedIds = issueIds;
bugSizeMap.put("unClosed", issueIds.size());
bugSizeMap.put("newCount", issueIds.size());
} else {
unClosedIds = issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
@ -196,31 +191,61 @@ public class TrackService {
thisWeekCount = extIssuesMapper.getThisWeekIssueCount(unClosedIds, projectId).intValue();
}
bugSizeMap.put("thisWeekCount", thisWeekCount);
// 如果没有严重程度字段
int newCount = 0;
int resolvedCount = 0;
int rejectedCount = 0;
int unKnownCount = 0;
for (String unClosedId : unClosedIds) {
String status = statusMap.getOrDefault(unClosedId, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY);
if (StringUtils.equalsIgnoreCase(status, "new")) {
newCount += 1;
} else if (StringUtils.equalsIgnoreCase(status, "resolved")) {
resolvedCount += 1;
} else if (StringUtils.equalsIgnoreCase(status, "rejected")) {
rejectedCount += 1;
} else {
unKnownCount += 1;
}
}
bugSizeMap.put("newCount", newCount);
bugSizeMap.put("resolvedCount", resolvedCount);
bugSizeMap.put("rejectedCount", rejectedCount);
bugSizeMap.put("unKnownCount", unKnownCount);
return bugSizeMap;
}
private Map<String, Integer> getPlanBugStatusSize(List<String> planIds, String projectId) {
CustomField customField = baseCustomFieldService.getCustomFieldByName(projectId, SystemCustomField.ISSUE_STATUS);
JSONArray statusArray = JSONArray.parseArray(customField.getOptions());
Map<String, Integer> bugStatusMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(planIds)) {
planIds.forEach(planId -> {
List<String> issueIds = extTestCaseMapper.getTestPlanBug(planId);
Map<String, String> statusMap = customFieldIssuesService.getIssueStatusMap(issueIds, projectId);
if (MapUtils.isEmpty(statusMap)) {
Integer count = bugStatusMap.get(Translator.get("new"));
if (count == null) {
bugStatusMap.put(Translator.get("new"), issueIds.size());
} else {
count += issueIds.size();
bugStatusMap.put(Translator.get("new"), count);
}
} else {
List<String> unClosedIds = issueIds.stream()
.filter(id -> !StringUtils.equals(statusMap.getOrDefault(id, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY), "closed"))
.collect(Collectors.toList());
for (String unClosedId : unClosedIds) {
String status = statusMap.getOrDefault(unClosedId, StringUtils.EMPTY).replaceAll("\"", StringUtils.EMPTY);
IssueStatus statusEnum = IssueStatus.getEnumByName(status);
if (statusEnum != null) {
Integer count = bugStatusMap.get(Translator.get(statusEnum.getI18nKey()));
if (count == null) {
bugStatusMap.put(Translator.get(statusEnum.getI18nKey()), 1);
} else {
count += 1;
bugStatusMap.put(Translator.get(statusEnum.getI18nKey()), count);
}
} else {
statusArray.forEach(item -> {
JSONObject statusObj = (JSONObject) item;
if (StringUtils.equals(status, statusObj.get("value").toString())) {
Integer count = bugStatusMap.get(statusObj.get("text").toString());
if (count == null) {
bugStatusMap.put(statusObj.get("text").toString(), 1);
} else {
count += 1;
bugStatusMap.put(statusObj.get("text").toString(), count);
}
}
});
}
}
}
});
}
return bugStatusMap;
}
private int getAllUnClosedBugSize(String projectId) {
IssuesRequest req = new IssuesRequest();
req.setProjectId(projectId);

View File

@ -63,6 +63,16 @@ can_not_be_null=Can not be null
excel_field_not_exist=Not exist
options_not_exist=Incorrect option value
format_error=Format error
# issue status
new=new
resolved=resolved
closed=closed
active=active
delete=delete
in_progress=in_progress
rejected=rejected
upcoming=upcoming
reopened=reopened
#project
project_name_is_null=Project name cannot be null
project_name_already_exists=The project name already exists

View File

@ -40,6 +40,16 @@ can_not_be_null=不能为空
excel_field_not_exist=不存在该字段
options_not_exist=选项值有误
format_error=格式有误
# issue status
new=新建
resolved=已解决
closed=已关闭
active=激活
delete=删除
in_progress=接受/处理
rejected=已拒绝
upcoming=待办
reopened=重新打开
#project
project_name_is_null=项目名称不能为空
project_name_already_exists=项目名称已存在

View File

@ -40,6 +40,16 @@ can_not_be_null=不能爲空
excel_field_not_exist=不存在該字段
options_not_exist=選項值有誤
format_error=格式有誤
#issue status
new=新建
resolved=已解決
closed=已關閉
active=激活
delete=刪除
in_progress=接受/處理
rejected=已拒絕
upcoming=待辦
reopened=重新打開
#project
project_name_is_null=項目名稱不能為空
project_name_already_exists=項目名稱已存在

View File

@ -5,3 +5,8 @@ export const TEST_CASE_STATUS_MAP = {
'Completed': i18n.t('test_track.plan.plan_status_completed'),
'Trash': i18n.t('test_track.plan.plan_status_trash')
}
export const DASHBOARD_CHART_COLOR = [
'#AA4FBF', '#FFD131', '#10CECE', '#4E83FD',
'#935AF6', '#50CEFB', '#FFA53D', '#62D256',
];

View File

@ -1,7 +1,7 @@
<template>
<div style="background-color:#F5F6F7">
<div style="background-color:#F5F6F7; overflow: auto">
<ms-container>
<ms-main-container style="overflow-y: hidden">
<ms-main-container style="padding: 0px">
<div class="track-home-layout">
<el-row :gutter="16">
<el-col :span="12">
@ -14,7 +14,7 @@
<el-row :gutter="16" style="margin-top: 16px">
<el-col :span="12">
<bug-count-card />
<bug-count-card @redirectPage="redirectPage"/>
</el-col>
<el-col :span="12">
<case-maintenance />
@ -22,19 +22,19 @@
</el-row>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<el-col style="height: 369px; background-color: #FFFFFF;">
<ms-failure-test-case-list :select-function-case="true" @redirectPage="redirectPage"/>
</el-col>
</el-row>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<el-col style="height: 369px; background-color: #FFFFFF;">
<review-list/>
</el-col>
</el-row>
<el-row style="margin-top: 16px">
<el-col style="background-color: #FFFFFF;">
<el-col style="height: 369px; background-color: #FFFFFF;">
<ms-running-task-list :call-from="'track_home'" @redirectPage="redirectPage"/>
</el-col>
</el-row>
@ -55,7 +55,6 @@ 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";
require('echarts/lib/component/legend');
@ -76,11 +75,6 @@ export default {
return {
}
},
computed: {
projectId() {
return getCurrentProjectID();
},
},
methods: {
redirectPage(page, dataType, selectType, title) {
//api
@ -103,6 +97,8 @@ export default {
case "api":
home = this.$router.resolve('/api/definition/' + uuid + "/" + dataType + "/" + selectType);
break;
case "issue":
home = this.$router.resolve('/track/issue/' + uuid + "/" + dataType + "/" + selectType);
}
if (home) {
window.open(home.href, '_blank');
@ -193,4 +189,8 @@ export default {
color: #1F2329;
line-height: 22px;
}
.track-home-layout :deep(.el-card) {
border: 0;
}
</style>

View File

@ -1,82 +1,86 @@
<template>
<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>
<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 class="dashboard-card">
<el-card shadow="never" class="box-card" style="height: 100%">
<div slot="header" class="clearfix">
<span class="dashboard-title">{{ $t('test_track.home.bug_count') }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<bug-count-chart :bug-data="bugData" ref="countChart" @redirectPage="redirectPage"/>
<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 class="addition-info">
<el-row :gutter="24" style="margin: 0">
<el-col :span="24" 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">
<div v-show="!loadError">
<div class="main-info">
<count-chart :chart-data="bugData.chartData" :main-title="chartMainTitle"
:week-count="bugData.thisWeekAddedCount" :chart-sub-link="chartRedirectLink" 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('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">
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('unClosedRelatedTestPlan')">
{{ 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-row>
<div class="common-amount">
<el-link class="addition-info-num" @click="redirectPage('AllRelatedTestPlan')">
{{ formatAmount(bugData.bugTotalCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
</div>
</div>
</div>
</div>
</el-card>
</el-card>
</div>
</template>
<script>
import bugCountChart from "@/business/home/components/chart/BugCountChart";
import countChart from "@/business/home/components/chart/CountChart";
import hoverCard from "@/business/home/components/card/HoverCard";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getTrackBugCount} from "@/api/track";
import {formatNumber} from "@/api/track"
import {getUUID} from "metersphere-frontend/src/utils";
export default {
name: "BugCountCard",
components: {bugCountChart, hoverCard},
components: {countChart, hoverCard},
data() {
return {
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'),
chartMainTitle: this.$t("home.bug_dashboard.un_closed_bug_count"),
chartRedirectLink: "/#/track/issue/" + getUUID() + "/" + getCurrentProjectID() + "/thisWeekUnClosedIssue",
bugData: {
bugCaseRage:" 0%",
bugTotalCount: 0,
@ -87,7 +91,8 @@ export default {
resolvedCount: 0,
rejectedCount: 0,
unKnownCount: 0,
thisWeekCount: 0
thisWeekCount: 0,
chartData: {}
},
}
},
@ -114,7 +119,8 @@ export default {
return formatNumber(number);
},
redirectPage(clickType) {
this.$emit("redirectPage", "testCase", "relationCase", clickType);
let currentProjectId = getCurrentProjectID();
this.$emit("redirectPage", "issue", currentProjectId, clickType);
}
}
}

View File

@ -1,119 +1,122 @@
<template>
<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>
<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 class="dashboard-card">
<el-card shadow="never" class="box-card" style="height: 100%">
<div slot="header" class="clearfix">
<span class="dashboard-title">{{ $t('test_track.home.case_count') }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<case-count-chart :track-data="trackData" ref="countChart" @redirectPage="redirectPage"/>
<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 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">
<div v-show="!loadError">
<div class="main-info">
<count-chart :chart-data="trackData.chartData" :main-title="chartMainTitle"
:week-count="trackData.thisWeekAddedCount" :chart-sub-link="chartRedirectLink" 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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(trackData.prepareCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(trackData.passCount + trackData.unPassCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
<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="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">
<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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(trackData.unPassCount) }}
</el-link>
</div>
</el-col>
<el-col :span="8">
<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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(trackData.passCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
<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>
</div>
</div>
</div>
</el-card>
</el-card>
</div>
</template>
<script>
import caseCountChart from "@/business/home/components/chart/CaseCountChart";
import countChart from "@/business/home/components/chart/CountChart";
import hoverCard from "@/business/home/components/card/HoverCard";
import {getUUID} from "metersphere-frontend/src/utils";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getTrackCount} from "@/api/track";
import {formatNumber} from "@/api/track"
import {hasPermission} from "@/business/utils/sdk-utils";
export default {
name: "CaseCountCard",
components: {caseCountChart, hoverCard},
components: {countChart, hoverCard},
data() {
return {
loading: false,
loadError: false,
caseReviewRangeToolTip: this.$t('api_test.home_page.formula.review'),
caseFinishedReviewPassRageToolTip: this.$t('home.dashboard.case_finished_review_pass_tip'),
chartMainTitle: this.$t("home.case_review_dashboard.case_count"),
chartRedirectLink: "/#/track/case/all/" + getUUID() + "/case/thisWeekCount",
trackData: {
allCaseCountNumber: 0,
allRelevanceCaseCount: 0,
@ -135,7 +138,8 @@ export default {
scenarioCaseStr: "",
thisWeekAddedCount: 0,
unPassCount: 0,
uncoverageCount: 0
uncoverageCount: 0,
chartData: {},
},
}
},
@ -163,9 +167,6 @@ export default {
return formatNumber(number);
},
redirectPage(clickType) {
if (!hasPermission('PROJECT_TRACK_CASE:READ')) {
return;
}
this.$emit("redirectPage", "testCase", "case", clickType);
}
}

View File

@ -1,25 +1,25 @@
<template>
<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>
<div class="dashboard-card">
<el-card shadow="never" class="box-card" style="height: 100%">
<div slot="header" class="clearfix">
<span class="dashboard-title">{{ $t('test_track.home.case_maintenance') }}</span>
</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 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>
<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>
</el-card>
</div>
</template>
<script>
@ -88,12 +88,21 @@ export default {
show: false
}
},
grid: {
left: 0,
containLabel: true,
bottom: 24,
top: 60,
right: 24,
width: 600,
height: 250
},
legend: {
itemWidth: 8,
itemHeight: 8,
data: [{icon: 'rect', name: this.$t('test_track.home.function_case_count')}, {icon: 'rect', name: this.$t('test_track.home.relevance_case_count')}],
orient: 'horizontal',
left: '30',
left: '0',
top: '10'
},
series: [

View File

@ -1,5 +1,5 @@
<template>
<el-card class="table-card" shadow="never" v-loading="loading" body-style="padding:10px;">
<el-card class="table-card" shadow="never" v-loading="loading">
<template v-slot:header>
<span class="table-title">
{{ $t('api_test.home_page.failed_case_list.title') }}
@ -14,8 +14,8 @@
</div>
<div v-show="!loadError">
<el-table :data="tableData" class="adjust-table table-content"
:header-cell-style="{backgroundColor: '#F5F6F7'}" max-height="224px">
<el-table-column type="index" :label="$t('home.case.index')" show-overflow-tooltip/>
header-cell-class-name="home-table-cell" max-height="226px">
<el-table-column type="index" :label="$t('home.case.index')" width="100" show-overflow-tooltip/>
<el-table-column prop="caseName" :label="$t('home.case.case_name')">
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row.caseType,row.id)"

View File

@ -1,81 +1,85 @@
<template>
<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>
<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 class="dashboard-card">
<el-card shadow="never" class="box-card" style="height: 100%">
<div slot="header" class="clearfix">
<span class="dashboard-title">{{ $t('test_track.home.relevance_case') }}</span>
</div>
<div v-show="!loadError">
<div class="main-info">
<relevance-count-chart :relevance-data="relevanceData" ref="countChart" @redirectPage="redirectPage"/>
<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 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">
<div v-show="!loadError">
<div class="main-info">
<count-chart :chart-data="relevanceData.chartData" :main-title="chartMainTitle"
:week-count="relevanceData.thisWeekAddedCount" :chart-sub-link="chartRedirectLink" 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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(relevanceData.uncoverageCount) }}
</el-link>
</div>
</el-col>
<el-col :span="12">
<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')" v-permission-disable="['PROJECT_TRACK_CASE:READ']">
{{ formatAmount(relevanceData.coverageCount) }}
</el-link>
</div>
</el-col>
</el-row>
</div>
</template>
</hover-card>
</el-col>
</el-row>
<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>
</div>
</div>
</div>
</el-card>
</el-card>
</div>
</template>
<script>
import relevanceCountChart from "@/business/home/components/chart/RelevanceCountChart";
import countChart from "@/business/home/components/chart/CountChart";
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"
import {getUUID} from "metersphere-frontend/src/utils";
export default {
name: "RelevanceCaseCard",
components: {relevanceCountChart, hoverCard},
components: {countChart, hoverCard},
data() {
return {
loading: false,
loadError: false,
coverRangeToolTip: this.$t('api_test.home_page.formula.testplan_coverage'),
chartMainTitle: this.$t("home.relevance_dashboard.relevance_case_count"),
chartRedirectLink: "/#/track/case/all/" + getUUID() + "/case/thisWeekRelevanceCount",
relevanceData: {
allCaseCountNumber: 0,
allRelevanceCaseCount: 0,
@ -97,7 +101,8 @@ export default {
scenarioCaseStr: "",
thisWeekAddedCount: 0,
unPassCount: 0,
uncoverageCount: 0
uncoverageCount: 0,
chartData: {}
},
}
},

View File

@ -6,8 +6,8 @@
</span>
<div class="btn-group">
<ms-table-button icon="" :class="!showMyCreator ? 'hover' : 'reviewedBtn'" :content="$t('test_track.review.reviewed_by_me')" @click="searchMyCreator('false')" style="border-color: #FFFFFF"/>
<ms-table-button icon="" :class="showMyCreator ? 'hover' : 'createBtn'" :content="$t('test_track.review.my_create')" @click="searchMyCreator('true')" style="border-color: #FFFFFF; margin-left: 3px"/>
<ms-table-button icon="" :class="!showMyCreator ? 'hover reviewedBtn' : 'reviewedBtn'" :content="$t('test_track.review.reviewed_by_me')" @click="searchMyCreator('false')" style="border-color: #FFFFFF"/>
<ms-table-button icon="" :class="showMyCreator ? 'hover createBtn' : 'createBtn'" :content="$t('test_track.review.my_create')" @click="searchMyCreator('true')" style="border-color: #FFFFFF; margin-left: 3px"/>
</div>
</div>
<div v-loading="loading" element-loading-background="#FFFFFF">
@ -19,8 +19,8 @@
</div>
<div v-show="!loadError">
<el-table class="adjust-table" :data="tableData" @row-click="intoPlan"
:header-cell-style="{backgroundColor: '#F5F6F7'}" max-height="224px">
<el-table-column type="index" :label="$t('home.table.index')" show-overflow-tooltip />
header-cell-class-name="home-table-cell" max-height="226px">
<el-table-column type="index" width="100" :label="$t('home.table.index')" show-overflow-tooltip />
<el-table-column prop="name" :label="$t('commons.name')" show-overflow-tooltip />
<el-table-column prop="status" :label="$t('test_track.plan.plan_status')">
<template v-slot:default="scope">
@ -146,26 +146,59 @@ export default {
border: 1px solid #BBBFC4;
border-radius: 4px;
margin: 1px;
width: 182px;
height: 30px;
}
:deep(button.el-button.el-button--mini.is-plain.hover) {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
background-color: rgba(120, 56, 135, 0.1);
font-weight: 500;
}
.reviewedBtn.el-button--mini.is-plain:hover {
:deep(button.el-button.el-button--mini.is-plain.createBtn) {
width: 100px;
height: 24px;
position: relative;
left: 4px;
top: 4px;
}
:deep(button.el-button.el-button--mini.is-plain.reviewedBtn) {
width: 72px;
height: 24px;
position: relative;
left: 4px;
top: 4px;
}
:deep(.reviewedBtn.el-button--mini.is-plain:hover) {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
font-weight: 400;
}
.createBtn.el-button--mini.is-plain:hover {
:deep(.createBtn.el-button--mini.is-plain:hover) {
background: rgba(120, 56, 135, 0.1);
border-radius: 4px;
color: #783887;
font-weight: 400;
}
:deep(button.el-button.el-button--mini.is-plain span) {
font-family: 'PingFang SC';
font-style: normal;
font-size: 14px;
line-height: 20px;
text-align: center;
flex: none;
order: 0;
flex-grow: 0;
position: relative;
right: 3px;
bottom: 8px;
}
</style>

View File

@ -17,9 +17,9 @@
:enable-selection="false"
:condition="condition"
:data="tableData"
:header-cell-style="{ backgroundColor: '#F5F6F7' }"
@refresh="search" max-height="224px">
<el-table-column type="index" :label="$t('home.table.index')" show-overflow-tooltip/>
@refresh="search"
header-cell-class-name="home-table-cell" max-height="226px">
<el-table-column type="index" width="100" :label="$t('home.table.index')" show-overflow-tooltip/>
<el-table-column prop="name" :label="$t('commons.name')">
<template v-slot:default="{row}">
<!-- 若为只读用户不可点击之后跳转-->

View File

@ -5,7 +5,7 @@
<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">
<el-tooltip class="item" effect="dark" :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">

View File

@ -1,222 +0,0 @@
<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-new"/>
<span class="count-title">{{ $t('test_track.issue.status_new') }}</span>
<span class="count-value">
{{ formatAmount(bugData.newCount) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-resolved"/>
<span class="count-title">{{ $t('test_track.issue.status_resolved') }}</span>
<span class="count-value">
{{ formatAmount(bugData.resolvedCount) }}
</span>
</div>
<div class="count-row">
<span class="ms-point-rejected"/>
<span class="count-title">{{ $t('test_track.issue.status_rejected') }}</span>
<span class="count-value">
{{ formatAmount(bugData.rejectedCount) }}
</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";
import {getCurrentProjectID} from "@/business/utils/sdk-utils";
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.newCount) {
total += this.bugData.newCount;
}
if (this.bugData.resolvedCount) {
total += this.bugData.resolvedCount;
}
if (this.bugData.rejectedCount) {
total += this.bugData.rejectedCount;
}
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', '#14E1C6', '#FAD355',]
protocolData = [
{value: this.bugData.newCount, name: this.$t('test_track.issue.status_new')},
{value: this.bugData.resolvedCount, name: this.$t('test_track.issue.status_resolved')},
{value: this.bugData.rejectedCount, name: this.$t('test_track.issue.status_rejected')},
];
}
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.bugData.thisWeekCount + " >",
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/issue/" + getUUID() + "/" + getCurrentProjectID() + "/thisWeekUnClosedIssue",
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-rejected {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #FAD355;
}
.ms-point-resolved {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #14E1C6;
}
.ms-point-new {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #AA4FBF;
}
</style>

View File

@ -1,242 +0,0 @@
<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";
import {hasPermission} from "@/business/utils/sdk-utils";
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 = ['#F76964', '#FFD131', '#AA4FBF', '#10CECE']
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: hasPermission('PROJECT_TRACK_CASE:READ') ? "/#/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: #F76964;
}
.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: #AA4FBF;
}
.ms-point-p3 {
height: 8px;
width: 8px;
margin-right: 8px;
display: inline-block;
background-color: #14E1C6;
}
</style>

View File

@ -0,0 +1,228 @@
<template>
<div v-if="reloadOver">
<el-row type="flex" justify="left" align="left">
<div style="height: 184px; width: 100%; margin-left: 30px; margin-right: 30px;">
<ms-chart :options="options"
:height="184"
width="100%"
:autoresize="true"/>
</div>
</el-row>
</div>
</template>
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {formatNumber} from "@/api/track";
import {DASHBOARD_CHART_COLOR} from "@/business/constants/table-constants";
export default {
name: "CountChart",
components: {MsChart},
props: {
chartData: Object,
weekCount: Number,
totalTime: Number,
mainTitle: String,
chartSubLink: String,
isExport: {
type: Boolean,
default: false,
}
},
data() {
return {
reloadOver: true,
pieChartStyle: {
amountFontSize: 32,
},
}
},
created() {
this.reload();
},
methods: {
reload() {
this.reloadOver = false;
this.$nextTick(() => {
this.reloadOver = true;
});
},
getTotal() {
let total = 0;
for (let name in this.chartData) {
total += this.chartData[name];
}
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);
},
getChartData(empty) {
let elementArr = [];
if (empty) {
for (let name in this.chartData) {
let element = {name: name, value: 0};
elementArr.push(element);
}
return elementArr;
} else {
for (let name in this.chartData) {
let element = {name: name, value: this.chartData[name]};
elementArr.push(element);
}
}
return elementArr;
},
getChartColor(size, empty) {
let colorArr = [];
if (empty) {
for (let i = 0; i < size; i++) {
colorArr.push("#DEE0E3");
}
} else {
colorArr = DASHBOARD_CHART_COLOR.slice(0, size);
}
return colorArr;
}
},
computed: {
options() {
let dataIsNotEmpty = false;
let borderWidth = 0;
let elementArr = this.getChartData(true);
let colorArr = this.getChartColor(elementArr.length, true);
if (this.getTotal() > 0) {
borderWidth = 3;
dataIsNotEmpty = true;
elementArr = this.getChartData(false);
colorArr = this.getChartColor(elementArr.length, false);
}
let optionData = {
color: colorArr,
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
icon: "rect",
selectedMode: dataIsNotEmpty,
itemGap: 16,
left: '45%',
y: 'center',
itemHeight: 8,
itemWidth: 8, //icon
itemStyle: {
borderWidth: 0.1
},
textStyle: {
align: 'right',
rich: {
protocol: {
fontSize: 14,
color: '#646A73',
fontWeight: 400,
width: 50,
align: 'left',
lineHeight: 22,
},
num: {
fontSize: 14,
align: 'right',
lineHeight: 22,
color: '#646A73',
fontWeight: 500,
padding: [0, 0, 0, 140]
}
}
},
data: elementArr,
formatter: function (name) {
//name
let singleData = elementArr.filter(function (item) {
return item.name === name
});
let value = singleData[0].value;
return [`{protocol|${name}}`, `{num|${value}}`].join("");
}
},
title: {
text: "{mainTitle|" + this.mainTitle + "}\n\n{number|" + this.getAmount() + "}\n\n",
subtext: this.$t("home.dashboard.public.this_week") + ": +" + formatNumber(this.weekCount) + " >",
top: "center",
left: "86px",
textAlign: 'center',
textStyle: {
rich: {
mainTitle: {
color: '#646A73',
fontSize: 12,
align: 'center'
},
number: {
fontSize: this.pieChartStyle.amountFontSize,
fontWeight: 500,
fontStyle: "normal",
fontFamily: "PinfFang SC",
margin: "112px 0px 0px 2px0",
},
}
},
sublink: this.chartSubLink,
subtextStyle: {
color: "#1F2329",
fontSize: 12,
width: 105,
ellipsis: '...',
overflow: "truncate",
},
itemGap: -60,
},
series: [
{
type: 'pie',
radius: ['70%', '96%'],
center: ['92px', '50%'],
avoidLabelOverlap: false,
hoverAnimation: dataIsNotEmpty,
legendHoverLink: false,
label: {
show: false,
},
itemStyle: {
borderColor: "#FFF",
borderWidth: borderWidth,
borderRadius: 1,
},
labelLine: {
show: false
},
data: elementArr,
}
]
};
return optionData;
},
},
}
</script>
<style scoped>
</style>

View File

@ -1,224 +0,0 @@
<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";
import {hasPermission} from "@/business/utils/sdk-utils";
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: hasPermission('PROJECT_TRACK_CASE:READ') ? "/#/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

@ -381,7 +381,11 @@ export default {
},
getIssues() {
if (this.dataSelectRange === 'thisWeekUnClosedIssue') {
this.page.condition.thisWeekUnClosedIssue = true;
this.page.condition.thisWeekUnClosedTestPlanIssue = true;
} else if (this.dataSelectRange === 'unClosedRelatedTestPlan') {
this.page.condition.unClosedTestPlanIssue = true;
} else if (this.dataSelectRange === 'AllRelatedTestPlan') {
this.page.condition.allTestPlanIssue = true;
}
this.page.condition.projectId = this.projectId;
this.page.condition.workspaceId= this.workspaceId;

View File

@ -29,14 +29,14 @@ const message = {
},
dashboard: {
public: {
this_week: "This week ",
this_week: "Week ",
load_error: "Loading failure",
no_data: "No data",
},
case_finished_review_pass_tip: "Reviewed cases/All reviewed cases *100%"
},
case_review_dashboard: {
case_count: "Case count",
case_count: "Case",
not_review: "Not reviewed",
finished_review: "Reviewed",
not_pass: "Not pass",
@ -46,18 +46,18 @@ const message = {
api_case: "Api case",
scenario_case: "Scenario case",
performance_case: "Performance case",
relevance_case_count: "Relevance case count",
relevance_case_count: "Relevance case",
not_cover: "Not cover",
cover: "Cover",
},
bug_dashboard: {
un_closed_bug_count: "Unclosed bug count",
un_closed_range: "Unclosed bug range",
un_closed_bug_count: "Unclosed bug",
un_closed_range: "Unclosed bug rate",
un_closed_range_tips: "Unclosed bugs/all associated bugs *100%",
un_closed_bug_case_range: "Unclosed bug case range",
un_closed_bug_case_range: "Unclosed bug case rate",
un_closed_bug_case_range_tips: "Unclosed bugs/all associated cases *100%",
un_closed_count: "Unclosed bug count",
total_count: "Bug total",
un_closed_count: "Unclosed bug",
total_count: "All related bug",
case_count: "Case count",
}
},