测试计划概览前端及后端代码完善

This commit is contained in:
pfl
2025-06-06 17:19:18 +08:00
parent eee51078b5
commit 5cc2cdfc34
12 changed files with 708 additions and 83 deletions

View File

@@ -10,9 +10,12 @@ import com.test.test.domain.qo.IDQO;
import com.test.test.domain.qo.TestPlanAddQO;
import com.test.test.domain.qo.TestPlanListQO;
import com.test.test.domain.vo.TestPlanListVO;
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
import com.test.test.domain.vo.TestPlanOverviewVO;
import com.test.test.service.ITestPlanService;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
@@ -79,4 +82,20 @@ public class TestPlanController extends BaseController {
public AjaxResult edit(@RequestBody TestPlan testPlan) {
return toAjax(testPlanService.updateTestPlan(testPlan));
}
/**
* 获取测试计划概览信息
*/
@PostMapping("/planOverview")
public TestPlanOverviewVO selectPlanOverview(@RequestBody IDQO qo) {
return testPlanService.selectPlanOverview(qo.getId());
}
/**
* 获取测试计划用例执行趋势数据
*/
@PostMapping("/planCaseTrendData")
public List<TestPlanOverviewTrendDataVO> getPlanCaseTrendData(@RequestBody IDQO qo) {
return testPlanService.getCaseTrendData(qo.getId());
}
}

View File

@@ -0,0 +1,22 @@
package com.test.test.domain.vo;
import java.io.Serializable;
import lombok.Data;
@Data
public class TestPlanOverviewTrendDataVO implements Serializable {
private static final long serialVersionUID = -6444876575138174061L;
private String caseTrendDates;
private Integer notExecuted;
private Integer passed;
private Integer failed;
private Integer blocked;
private Integer skipped;
}

View File

@@ -0,0 +1,146 @@
package com.test.test.domain.vo;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
@Data
public class TestPlanOverviewVO implements Serializable {
private static final long serialVersionUID = -2634301979620606338L;
/**
* 用例总数
*/
private Integer caseCount;
/**
* 已执行用例数
*/
private Integer executedCaseCount;
/**
* 用例通过数
*/
private Integer passedCaseCount;
/**
* 用例失败数
*/
private Integer failedCaseCount;
/**
* 用例阻塞数
*/
private Integer blockedCaseCount;
/**
* 用例跳过数
*/
private Integer skippedCaseCount;
/**
* 用例未执行数
*/
private Integer notExecutedCaseCount;
/**
* 缺陷数
*/
private Integer defectCount;
/**
* 待确认缺陷状态数
*/
private Integer unconfirmedDefectCount;
/**
* 修复中缺陷状态数
*/
private Integer fixingDefectCount;
/**
* 待验证缺陷状态数
*/
private Integer unverifiedDefectCount;
/**
* 无效缺陷状态数
*/
private Integer invalidDefectCount;
/**
* 挂起缺陷状态数
*/
private Integer suspendedDefectCount;
/**
* 无效等级缺陷数
*/
private Integer invalidLevelDefectCount;
/**
* 轻微等级缺陷数
*/
private Integer minorLevelDefectCount;
/**
* 一般等级缺陷数
*/
private Integer normalLevelDefectCount;
/**
* 严重等级缺陷数
*/
private Integer seriousLevelDefectCount;
/**
* 致命等级缺陷数
*/
private Integer fatalLevelDefectCount;
/**
* 功能逻辑类型缺陷数
*/
private Integer logicDefectCount;
/**
* UI交互类型缺陷数
*/
private Integer uiDefectCount;
/**
* 性能问题类型缺陷数
*/
private Integer performanceDefectCount;
/**
* 兼容性问题类型缺陷数
*/
private Integer compatibilityDefectCount;
/**
* 配置错误类型缺陷数
*/
private Integer configurationDefectCount;
/**
* 安全问题类型缺陷数
*/
private Integer securityDefectCount;
/**
* 安装部署类型缺陷数
*/
private Integer installationDefectCount;
/**
* 其他类型缺陷数
*/
private Integer otherDefectCount;
/**
* 用例执行时间
*/
private List<String> caseTrendDates;
}

View File

@@ -3,8 +3,9 @@ package com.test.test.mapper;
import com.test.test.domain.TestPlan;
import com.test.test.domain.qo.TestPlanListQO;
import com.test.test.domain.vo.TestPlanListVO;
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
import com.test.test.domain.vo.TestPlanOverviewVO;
import java.util.List;
public interface TestPlanMapper {
/**
@@ -41,4 +42,18 @@ public interface TestPlanMapper {
* @return
*/
Long selectPlanId(String serialNumber);
/**
* 查询测试计划概览信息
* @param id
* @return
*/
TestPlanOverviewVO selectPlanOverview(Long id);
/**
* 查询测试计划用例执行趋势数据
* @param id
* @return
*/
List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id);
}

View File

@@ -4,6 +4,8 @@ import com.test.test.domain.TestPlan;
import com.test.test.domain.qo.TestPlanAddQO;
import com.test.test.domain.qo.TestPlanListQO;
import com.test.test.domain.vo.TestPlanListVO;
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
import com.test.test.domain.vo.TestPlanOverviewVO;
import java.util.List;
/**
@@ -41,4 +43,18 @@ public interface ITestPlanService {
* @return
*/
public int updateTestPlan(TestPlan testPlan);
/**
* 获取测试计划概览信息
* @param id
* @return
*/
TestPlanOverviewVO selectPlanOverview(Long id);
/**
* 获取测试计划用例趋势数据
* @param id
* @return
*/
List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id);
}

View File

@@ -6,10 +6,15 @@ import com.test.test.domain.TestPlan;
import com.test.test.domain.qo.TestPlanAddQO;
import com.test.test.domain.qo.TestPlanListQO;
import com.test.test.domain.vo.TestPlanListVO;
import com.test.test.domain.vo.TestPlanOverviewTrendDataVO;
import com.test.test.domain.vo.TestPlanOverviewVO;
import com.test.test.mapper.TestPlanMapper;
import com.test.test.mapper.TestProjectPlanMapper;
import com.test.test.service.ITestPlanService;
import jakarta.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -82,4 +87,39 @@ public class TestPlanServiceImpl implements ITestPlanService {
testPlan.setUpdateTime(DateUtils.getNowDate());
return testPlanMapper.updateTestPlan(testPlan);
}
/**
* 获取测试计划概览信息
* @param id
* @return
*/
@Override
public TestPlanOverviewVO selectPlanOverview(Long id) {
TestPlanOverviewVO overviewVO = testPlanMapper.selectPlanOverview(id);
overviewVO.setCaseTrendDates(getCaseTrendDates());
return overviewVO;
}
/**
* 获取测试计划用例趋势数据
* @param id
* @return
*/
@Override
public List<TestPlanOverviewTrendDataVO> getCaseTrendData(Long id) {
return testPlanMapper.getCaseTrendData(id);
}
private List<String> getCaseTrendDates() {
List<String> dates = new ArrayList<>();
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
for (int i = 6; i >= 0; i--) {
LocalDate date = today.minusDays(i);
dates.add(date.format(formatter));
}
return dates;
}
}

View File

@@ -203,4 +203,56 @@
<select id="selectPlanId" resultType="Long">
select id from test_plan where serial_number = #{serialNumber} and del_flag = '0'
</select>
<select id="selectPlanOverview" resultType="TestPlanOverviewVO">
SELECT
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0') AS caseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result != '0') AS executedCaseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '1') AS passedCaseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '2') AS failedCaseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '0') AS notExecutedCaseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '3') AS blockedCaseCount,
(SELECT COUNT(1) FROM test_plan_case tpc WHERE tpc.plan_id = #{id} AND tpc.del_flag = '0' AND tpc.execute_result = '4') AS skippedCaseCount,
(SELECT COUNT(1) FROM test_plan_defect tpd WHERE tpd.plan_id = #{id} AND tpd.del_flag = '0') AS defectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '0' AND tpd.del_flag = '0') AS unconfirmedDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '1' AND tpd.del_flag = '0') AS fixingDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '5' AND tpd.del_flag = '0') AS unverifiedDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '3' AND tpd.del_flag = '0') AS invalidDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.status = '4' AND tpd.del_flag = '0') AS suspendedDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '0' AND tpd.del_flag = '0') AS invalidLevelDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '1' AND tpd.del_flag = '0') AS minorLevelDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '2' AND tpd.del_flag = '0') AS normalLevelDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '3' AND tpd.del_flag = '0') AS seriousLevelDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.level = '4' AND tpd.del_flag = '0') AS fatalLevelDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '0' AND tpd.del_flag = '0') AS logicDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '1' AND tpd.del_flag = '0') AS uiDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '2' AND tpd.del_flag = '0') AS performanceDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '3' AND tpd.del_flag = '0') AS compatibilityDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '4' AND tpd.del_flag = '0') AS configurationDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '5' AND tpd.del_flag = '0') AS securityDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '6' AND tpd.del_flag = '0') AS installationDefectCount,
(SELECT COUNT(1) FROM test_plan_defect tpd LEFT JOIN test_defect td ON td.id = tpd.defect_id WHERE tpd.plan_id = #{id} AND td.type = '7' AND tpd.del_flag = '0') AS otherDefectCount
FROM test_plan tp
WHERE tp.id = #{id}
AND tp.del_flag = '0'
</select>
<select id="getCaseTrendData" resultType="TestPlanOverviewTrendDataVO">
SELECT
DATE_FORMAT(tpc.execute_time, '%Y-%m-%d') AS caseTrendDates,
SUM(CASE WHEN tpc.execute_result = '0' THEN 1 ELSE 0 END) AS notExecuted,
SUM(CASE WHEN tpc.execute_result = '1' THEN 1 ELSE 0 END) AS passed,
SUM(CASE WHEN tpc.execute_result = '2' THEN 1 ELSE 0 END) AS failed,
SUM(CASE WHEN tpc.execute_result = '3' THEN 1 ELSE 0 END) AS blocked,
SUM(CASE WHEN tpc.execute_result = '4' THEN 1 ELSE 0 END) AS skipped
FROM test_plan_case tpc
LEFT JOIN test_plan tp ON tp.id = tpc.plan_id
WHERE tpc.del_flag = '0'
AND tpc.plan_id = #{id}
AND tp.del_flag = '0'
AND tpc.execute_time >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(tpc.execute_time, '%Y-%m-%d')
ORDER BY DATE_FORMAT(tpc.execute_time, '%Y-%m-%d') ASC
</select>
</mapper>

View File

@@ -46,6 +46,7 @@
"js-cookie": "3.0.1",
"jsencrypt": "3.0.0-rc.1",
"json-editor-vue": "^0.17.3",
"moment": "^2.30.1",
"nprogress": "0.2.0",
"quill": "2.0.2",
"screenfull": "5.0.2",

View File

@@ -8,7 +8,9 @@ const api = {
testPlanDetail: 'test/testPlan/planDetail',
testPlanProjectList: '/test/testPlanProject/list',
addTestReport: 'test/testReport/addTestReport',
getTestReportList: 'test/testReport/reportList'
getTestReportList: 'test/testReport/reportList',
getPlanOverview:'test/testPlan/planOverview',
getPlanCaseTrendData: 'test/testPlan/planCaseTrendData',
}
export function getTestPlanList(data) {
@@ -74,3 +76,19 @@ export function getTestReportList(data) {
params: data
})
}
export function getPlanOverview(id) {
return request({
url: api.getPlanOverview,
method: 'post',
data: {id}
})
}
export function getPlanCaseTrendData(id) {
return request({
url: api.getPlanCaseTrendData,
method: 'post',
data: {id}
})
}

View File

@@ -375,6 +375,9 @@ export default {
{id: row.id,
name: row.name,
testStatus: row.status,
startPlanTime: row.startPlanTime,
endPlanTime: row.endPlanTime,
manager: row.manager,
});
},
handleTabClick(tab) {

View File

@@ -30,7 +30,7 @@
</el-row>
<!-- 测试计划界面 -->
<probably-view :planId="planId" v-show="activeIndex === '1'"></probably-view>
<probably-view :overViewData="overViewData" :planId="planId" v-show="activeIndex === '1'"></probably-view>
<case-execute :planId="planId" :name="testTitle" v-show="activeIndex === '2'"></case-execute>
<case-report :planId="planId" v-show="activeIndex === '3'"></case-report>
<test-defects :planId="planId" :name="testTitle" v-show="activeIndex === '4'"></test-defects>
@@ -66,6 +66,7 @@ export default {
'3': '已终止'
},
planId: '',
overViewData: {},
testStatus: '',
testTitle: '',
activeTab: 'overview',// 默认选中的标签页
@@ -114,6 +115,11 @@ export default {
this.planId = this.$route.query.id;
this.testTitle = this.$route.query.name
this.testStatus = this.$route.query.testStatus
this.overViewData = {
startTime: this.$route.query.startPlanTime,
endTime: this.$route.query.endPlanTime,
manager: this.$route.query.manager
}
}
};
</script>

View File

@@ -17,14 +17,13 @@
</el-row>
<el-row :gutter="10">
<el-col :span="5">
<!-- <span>起止时间{{ startToEndTime }}</span>-->
<span>起止时间2020-04-01 ~ 2020-04-30</span>
<span>起止时间{{ overViewData.startTime }} ~ {{ overViewData.endTime }}</span>
</el-col>
<el-col :span="7">
<span>当前测试计划已经开始 3 ,距离截止时间还有 52 </span>
<span>当前测试计划已经开始 {{ daysElapsed }} ,距离截止时间还有 {{ daysRemaining }} </span>
</el-col>
<el-col :span="5">
<span>负责人{{ }}</span>
<span>负责人{{ overViewData.manager }}</span>
</el-col>
</el-row>
</div>
@@ -34,10 +33,10 @@
<div class="progress-content">
<div id="executionProgressChart" style="width: 70%; height: 200px;"></div>
<div class="progress-text">
<p style="font-size: 40px;margin: auto">100%</p>
<p style="font-size: 40px;margin: auto">{{ executionProgress || 0 }}%</p>
<p>执行进度</p>
<p>用例总数 1</p>
<p><span style="color: #409EFF"></span> 已执行用例 1</p>
<p>用例总数 {{ viewData.caseCount }}</p>
<p><span style="color: #6e92ef"></span> 已执行用例 {{ viewData.executedCaseCount }}</p>
</div>
</div>
</el-card>
@@ -47,13 +46,13 @@
<div class="progress-content">
<el-row :gutter="10">
<el-col class="rate-content" :span="12">
<p>100%</p>
<el-progress :percentage="100" status="success"></el-progress>
<p>{{ executionPassRate || 0 }}%</p>
<el-progress :percentage="(viewData.passedCaseCount / viewData.executedCaseCount).toFixed(2) * 100 || 0" status="success"></el-progress>
<p>执行用例通过率</p>
</el-col>
<el-col class="rate-content" :span="12">
<p>100%</p>
<el-progress :percentage="100" status="success"></el-progress>
<p>{{ overallPassRate || 0 }}%</p>
<el-progress :percentage="(viewData.passedCaseCount / viewData.caseCount).toFixed(2) * 100 || 0" status="success"></el-progress>
<p>总体用例通过率</p>
</el-col>
</el-row>
@@ -64,14 +63,14 @@
<el-card shadow="hover">
<div class="progress-content">
<div class="defect-content">
<p>0</p>
<p>{{ viewData.defectCount }}</p>
<p>缺陷数</p>
<el-row>
<el-col :span="4">0</el-col>
<el-col :span="4">0</el-col>
<el-col :span="4">0</el-col>
<el-col :span="4">0</el-col>
<el-col :span="4">0</el-col>
<el-col :span="4">{{ viewData.unconfirmedDefectCount }}</el-col>
<el-col :span="4">{{ viewData.fixingDefectCount }}</el-col>
<el-col :span="4">{{ viewData.unverifiedDefectCount }}</el-col>
<el-col :span="4">{{ viewData.invalidDefectCount }}</el-col>
<el-col :span="4">{{ viewData.suspendedDefectCount }}</el-col>
</el-row>
<el-row>
<el-col :span="4">待确认</el-col>
@@ -98,13 +97,13 @@
</div>
<el-card shadow="hover">
<div class="distribution-content">
<el-progress type="circle" :percentage="100" color="#67C23A"></el-progress>
<div id="distributionChart" style="width: 70%; height: 200px;"></div>
<div class="distribution-text">
<p><span style="background-color: #67C23A; display: inline-block; width: 10px; height: 10px;"></span> 通过 1</p>
<p><span style="background-color: #F56C6C; display: inline-block; width: 10px; height: 10px;"></span> 失败 0</p>
<p><span style="background-color: #E6A23C; display: inline-block; width: 10px; height: 10px;"></span> 阻塞 0</p>
<p><span style="background-color: #F56C6C; display: inline-block; width: 10px; height: 10px;"></span> 跳过 0</p>
<p><span style="background-color: #909399; display: inline-block; width: 10px; height: 10px;"></span> 未执行 0</p>
<p><span style="background-color: #9cdab9; display: inline-block; width: 10px; height: 10px;"></span> 通过 {{ viewData.passedCaseCount }}</p>
<p><span style="background-color: #d4836a; display: inline-block; width: 10px; height: 10px;"></span> 失败 {{ viewData.failedCaseCount }}</p>
<p><span style="background-color: #e9c456; display: inline-block; width: 10px; height: 10px;"></span> 阻塞 {{ viewData.blockedCaseCount }}</p>
<p><span style="background-color: #79849e; display: inline-block; width: 10px; height: 10px;"></span> 跳过 {{ viewData.skippedCaseCount }}</p>
<p><span style="background-color: #cdcdcd; display: inline-block; width: 10px; height: 10px;"></span> 未执行 {{ viewData.notExecutedCaseCount }}</p>
</div>
</div>
</el-card>
@@ -117,12 +116,34 @@
</div>
<el-card shadow="hover">
<div class="distribution-text" style="height: 200px">
<line-chart :data="trendData" :chart-data="trendData"></line-chart>
<div id="caseTrendChart" style="width: 100%; height: 200px;"></div>
</div>
</el-card>
</el-card>
</el-col>
</el-row>
<el-row :gutter="10">
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>缺陷等级分布</span>
</div>
<el-card shadow="hover">
<div id="defectLevelChart" style="width: 100%; height: 200px;"></div>
</el-card>
</el-card>
</el-col>
<el-col :span="12">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>缺陷类型分布</span>
</div>
<el-card shadow="hover">
<div id="defectTypeChart" style="width: 100%; height: 200px;"></div>
</el-card>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<div>
关联需求信息
@@ -160,25 +181,29 @@
</template>
<script>
import LineChart from '@/views/dashboard/LineChart';
import * as echarts from 'echarts';
import {getTestPlanProjectList} from "@/api/test/testPlan";
import moment from 'moment';
import {getPlanCaseTrendData, getPlanOverview, getTestPlanProjectList} from "@/api/test/testPlan";
export default {
name: 'probablyView',
components: {
LineChart,
},
props: {
planId: {
type: String,
default: '',
},
overViewData: {
type: Object,
default: () => {
return {
startTime: '',
endTime: '',
manager: '',
}
}
},
},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
mounted() {
this.initExecutionProgressChart();
},
data() {
return {
priorityColor: {
@@ -192,42 +217,84 @@ export default {
list: [],
loading: false,
myChart: null,
startToEndTime: '',
distributionChart: null,
lineChart: null,
viewData: [],
caseTrendData: [],
// 执行进度
executionProgress: 0,
// 执行用例通过率
executionPassRate: 0,
// 总体用例通过率
overallPassRate: 0,
defectSlider: 0,
queryParams: {
planId: '',
pageNum: 1,
pageSize: 10,
},
trendData: {
labels: ['2025-04-01', '2025-04-02', '2025-04-03'],
datasets: [
{
label: '通过',
backgroundColor: '#67C23A',
data: [0, 0.5, 1],
},
{
label: '失败',
backgroundColor: '#F56C6C',
data: [0, 0, 0],
},
{
label: '跳过',
backgroundColor: '#E6A23C',
data: [0, 0, 0],
},
{
label: '阻塞',
backgroundColor: '#909399',
data: [0, 0, 0],
},
],
},
};
},
mounted() {
// 饼图1
const chartDom = document.getElementById('executionProgressChart');
this.myChart = echarts.init(chartDom);
const Observer = new ResizeObserver(() =>{
this.myChart.resize();
})
Observer.observe(chartDom);
// 饼图2
const chartDom2 = document.getElementById('distributionChart');
this.distributionChart = echarts.init(chartDom2);
const Observer2 = new ResizeObserver(() =>{
this.distributionChart.resize();
})
Observer2.observe(chartDom2);
// 折线趋势图
const lineChartDom = document.getElementById('caseTrendChart');
this.lineChart = echarts.init(lineChartDom);
const Observer3 = new ResizeObserver(() =>{
this.lineChart.resize();
})
Observer3.observe(lineChartDom);
// 柱状图1
const barChartDom = document.getElementById('defectLevelChart');
this.barChart = echarts.init(barChartDom);
const Observer4 = new ResizeObserver(() =>{
this.barChart.resize();
})
Observer4.observe(barChartDom);
// 柱状图2
const barChartDom2 = document.getElementById('defectTypeChart');
this.barChart2 = echarts.init(barChartDom2);
const Observer5 = new ResizeObserver(() =>{
this.barChart2.resize();
})
Observer5.observe(barChartDom2);
},
created() {
this.getList();
this.getInfoList();
this.getTrendData();
},
computed: {
daysElapsed() {
if (!this.overViewData.startTime) return 0;
const start = moment(this.overViewData.startTime);
const now = moment();
return now.diff(start, 'days');
},
daysRemaining() {
if (!this.overViewData.endTime) return 0;
const end = moment(this.overViewData.endTime);
const now = moment();
const diffDays = end.diff(now, 'days');
return diffDays > 0 ? diffDays : 0; // 确保不显示负数
},
},
methods: {
getList() {
@@ -239,11 +306,37 @@ export default {
this.loading = false;
})
},
getInfoList() {
getPlanOverview(this.queryParams.planId).then(response => {
this.viewData = response;
this.executionProgress = (this.viewData.executedCaseCount / this.viewData.caseCount).toFixed(2) * 100 || 0;
if (this.viewData.executedCaseCount === 0) {
this.executionPassRate = 0;
} else {
this.executionPassRate = ((this.viewData.passedCaseCount / this.viewData.executedCaseCount).toFixed(2) * 100).toFixed(2) || 0 ;
}
if (this.viewData.caseCount === 0) {
this.overallPassRate = 0;
} else {
this.overallPassRate = ((this.viewData.passedCaseCount / this.viewData.caseCount).toFixed(2) * 100).toFixed(2) || 0;
}
this.initExecutionProgressChart();
this.initDistributionChart();
this.initCaseTrendChart();
this.initDefectLevelChart();
this.initDefectTypeChart();
})
},
getTrendData(){
getPlanCaseTrendData(this.queryParams.planId).then(response => {
this.caseTrendData = response;
this.initCaseTrendChart();
})
},
// 初始化执行进度饼图
initExecutionProgressChart() {
const chartDom = document.getElementById('executionProgressChart');
const myChart = echarts.init(chartDom);
const option = {
tooltip: {
trigger: 'item'
@@ -254,36 +347,230 @@ export default {
},
series: [
{
name: 'Access From',
name: '执行进度',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
formatter: '{b}: {c}%',
position: 'inside'
},
data: [
{value: 1},
{ value: this.executionProgress, name: '执行进度' , itemStyle: { color: '#6e92ef'}},
{ value: 100 - this.executionProgress, name: '剩余进度' , itemStyle: { color: '#f5f5f5'}}
]
}
]
};
myChart.setOption(option);
// 监听窗口大小变化,自动调整图表大小
window.addEventListener('resize', () => {
myChart.resize();
});
option && this.myChart.setOption(option);
},
// 初始化执行分布饼图
initDistributionChart() {
const option = {
tooltip: {
trigger: 'item'
},
series: [
{
name: '用例状态',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
labelLine: {
show: false
},
data: [
{ value: this.viewData.passedCaseCount, itemStyle: { color: '#9cdab9' } },
{ value: this.viewData.failedCaseCount, itemStyle: { color: '#d4836a' } },
{ value: this.viewData.blockedCaseCount, itemStyle: { color: '#e9c456' } },
{ value: this.viewData.skippedCaseCount, itemStyle: { color: '#79849e' } },
{ value: this.viewData.notExecutedCaseCount, itemStyle: { color: '#cdcdcd' } }
]
}
]
};
option && this.distributionChart.setOption(option);
},
// 初始化折线图
initCaseTrendChart() {
const dates = this.caseTrendData.map(item => item.caseTrendDates);
const passedData = this.caseTrendData.map(item => item.passed);
const failedData = this.caseTrendData.map(item => item.failed);
const blockedData = this.caseTrendData.map(item => item.blocked);
const skippedData = this.caseTrendData.map(item => item.skipped);
const notExecutedData = this.caseTrendData.map(item => item.notExecuted);
const option = {
tooltip: {
trigger: 'axis'
},
legend: {
data: ['阻塞', '通过', '跳过', '失败', '未执行'],
left: 'left'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: dates
},
yAxis: {
type: 'value'
},
series: [
{
name: '阻塞',
type: 'line',
data: blockedData
},
{
name: '通过',
type: 'line',
data: passedData
},
{
name: '跳过',
type: 'line',
data: skippedData
},
{
name: '失败',
type: 'line',
data: failedData
},
{
name: '未执行',
type: 'line',
data: notExecutedData
}
]
};
option && this.lineChart.setOption(option);
},
// 初始化柱状图
initDefectLevelChart() {
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
legend: {
data: ['缺陷数']
},
xAxis: [
{
type: 'category',
data: ['致命', '严重', '一般', '轻微', '无效'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '缺陷数',
type: 'bar',
barWidth: '30%',
itemStyle: {
color: '#D68972FF' // 更改柱状图颜色为橙色
},
data: [
this.viewData.fatalLevelDefectCount,
this.viewData.seriousLevelDefectCount,
this.viewData.normalLevelDefectCount,
this.viewData.minorLevelDefectCount,
this.viewData.invalidLevelDefectCount
] // 更新数据以匹配图片
}
]
};
option && this.barChart.setOption(option);
},
// 初始化柱状图
initDefectTypeChart() {
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
legend: {
data: ['缺陷数']
},
xAxis: [
{
type: 'category',
data: ['功能逻辑', 'UI交互', '性能问题', '兼容性问题', '配置错误','安全问题', '安装部署' ,'其他'],
axisTick: {
alignWithLabel: true
}
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '缺陷数',
type: 'bar',
barWidth: '30%',
itemStyle: {
color: '#D68972FF' // 更改柱状图颜色为橙色
},
data: [this.viewData.logicDefectCount,
this.viewData.uiDefectCount,
this.viewData.performanceDefectCount,
this.viewData.compatibilityDefectCount,
this.viewData.configurationDefectCount,
this.viewData.securityDefectCount,
this.viewData.installationDefectCount,
this.viewData.otherDefectCount
] // 更新数据以匹配图片
}
]
};
option && this.barChart2.setOption(option);
},
},
};