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

This commit is contained in:
pfl
2025-04-29 21:44:04 +08:00
parent 59aca2920c
commit 0c250601ec
17 changed files with 869 additions and 47 deletions

View File

@@ -3,7 +3,12 @@ import request from '@/utils/request'
const api = {
testPlanList: 'test/testPlan/testPlanList',
addTestPlan: 'test/testPlan/addPlan',
delTestPlan: 'test/testPlan/deletePlan'
delTestPlan: 'test/testPlan/deletePlan',
updateTestPlan: 'test/testPlan/updatePlan',
testPlanDetail: 'test/testPlan/planDetail',
testPlanProjectList: '/test/testPlanProject/list',
addTestReport: 'test/testReport/addTestReport',
getTestReportList: 'test/testReport/reportList'
}
export function getTestPlanList(data) {
@@ -29,3 +34,43 @@ export function delTestPlan(id) {
data: {id}
})
}
export function updateTestPlan(data) {
return request({
url: api.updateTestPlan,
method: 'post',
data: data
})
}
export function getTestPlanDetail(id) {
return request({
url: api.testPlanDetail,
method: 'post',
data: {id}
})
}
export function getTestPlanProjectList(id) {
return request({
url: api.testPlanProjectList,
method: 'post',
data: {id}
})
}
export function addTestReport(data) {
return request({
url: api.addTestReport,
method: 'post',
data: data
})
}
export function getTestReportList(id) {
return request({
url: api.getTestReportList,
method: 'post',
data: {id}
})
}

View File

@@ -130,20 +130,6 @@ export const constantRoutes = [
}
]
},
{
path: '/testplan/execute',
component: Layout,
hidden: true,
children: [
{
path: '',
component: () => import('@/views/test/testplan/execute/index'),
name: 'PlanExecute',
noCache: true,
meta: { title: '计划执行', activeMenu: '/testplan' }
}
]
},
{
path: '/task/detail',
component: Layout,

View File

@@ -406,7 +406,6 @@ export default {
const form = {
...this.form,
}
console.log('submit', form)
this.$refs?.form.validate((valid) => {
if (!valid) {
return

View File

@@ -0,0 +1,160 @@
<template>
<div class="app-container">
<!-- 页面标题 -->
<div class="page-header">
<h3 style="font-size: 20px;color: #000000">测试报告</h3>
</div>
<div v-show="total <= 0" class="button-new">
<el-button icon="el-icon-plus" type="primary" size="medium" @click="addReportVue">
新建报告
</el-button>
</div>
<div v-show="total > 0">
<el-tabs style="margin-top: 10px;">
<el-Table v-loading="loading" :data="list">
<el-table-column prop="name" label="测试报告名称" align="center"/>
<el-table-column prop="result" label="测试结果" align="center">
</el-table-column>
<el-table-column prop="type" label="测试阶段" align="center">
<template #default="{ row }">
<dict-tag :options="dict.type.test_type" :value="row.type"/>
</template>
</el-table-column>
<el-table-column prop="status" label="发送状态" align="center">
</el-table-column>
<el-table-column prop="updateBy" label="最后更新人" align="center"/>
<el-table-column prop="updateTime" label="最后更新时间" align="center"/>
</el-Table>
</el-tabs>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
<el-dialog :title="title" :visible.sync="addOpen" width="40%">
<el-form ref="form" :rules="rules" :model="form" label-width="110px" label-position="right">
<el-form-item label="报告名称:" prop="name">
<el-input v-model="form.name" placeholder="请输入报告名称"></el-input>
</el-form-item>
<el-form-item label="测试阶段:" prop="type">
<el-select v-model="form.type" placeholder="请选择测试阶段" clearable>
<simple-options :options="dict.type.test_type"/>
</el-select>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addOpen = false"> </el-button>
<el-button type="primary" @click="submitForm"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {addTestReport, getTestReportList} from "@/api/test/testPlan";
export default {
name: 'caseReport',
components: {SimpleOptions},
dicts: ['test_type'],
props: {
planId: {
type: String,
default: '',
},
},
data() {
return {
title: '',
list: [],
total: 0,
addOpen: false,
loading: false,
submitLoading: false,
form: {
name: '',
type: '',
planId: '',
},
queryParams: {
planId: '',
pageNum: 1,
pageSize: 10,
},
rules: {
name: [{required: true, message: '请输入报告名称', trigger: 'blur'}],
type: [{required: true, message: '请选择测试阶段', trigger: 'blur'}]
}
}
},
mounted() {
this.getList()
},
methods: {
getList() {
this.loading = true
this.queryParams.planId = this.planId
getTestReportList(this.queryParams.planId).then(res => {
this.list = res.rows
this.total = res.total
this.loading = false
})
},
addReportVue() {
this.title = '新建报告'
this.addOpen = true
this.reset()
},
// 表单重置
reset() {
const form = {}
Object.keys(this.form).forEach((key) => {
form[key] = ''
})
this.form = form
this.resetForm('form')
},
submitForm() {
const form = {
...this.form,
planId: this.planId,
}
this.$refs.form.validate(valid => {
if (!valid) {
return
}
addTestReport(form)
.then(() => {
this.$message.success('新建成功')
this.addOpen = false
this.getList()
})
.catch(() => {
this.$message.error('新建失败')
})
.finally(() => {
this.submitLoading = false
})
})
},
},
}
</script>
<style scoped lang="scss">
.app-container {
.page-header {
margin-bottom: 20px;
}
}
.button-new {
display: flex;
justify-content: center;
align-items: center;
margin-top: 200px;
}
</style>

View File

@@ -83,6 +83,16 @@ export default {
name: 'project',
components: {page1, SimpleOptions, RelateCase},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
props: {
planId: {
type: String,
default: ''
},
name: {
type: String,
default: ''
}
},
data() {
return {
activeTab: '0',
@@ -113,8 +123,8 @@ export default {
};
},
created() {
this.queryParams.planId = this.$route.query.id;
this.title = this.$route.query.name;
this.queryParams.planId = this.planId;
this.title = this.name;
this.getList();
},
methods: {

View File

@@ -255,8 +255,6 @@
<el-table-column prop="defectNum" label="缺陷数" align="left"/>
<el-table-column label="操作" align="left" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click.native.stop="handlePlanRun(scope.row)">计划执行
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
</el-button>
</template>
@@ -311,7 +309,7 @@
</template>
<script>
import {addProject, delProject, getProjectList, managerList, updateProject} from "@/api/test/project";
import {getProjectList, managerList, updateProject} from "@/api/test/project";
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {addTestPlan, delTestPlan, getTestPlanList} from "@/api/test/testPlan";
@@ -373,7 +371,11 @@ export default {
computed: {},
methods: {
handleRowClick(row) {
this.$tab.openPage(`测试计划概览`, "/testplan/overview", {id: row.id});
this.$tab.openPage(`测试计划概览`, "/testplan/overview",
{id: row.id,
name: row.name,
testStatus: row.status,
});
},
handleTabClick(tab) {
this.activeTab = tab.name;
@@ -480,9 +482,6 @@ export default {
this.$modal.msgSuccess("删除成功");
});
},
handlePlanRun(row) {
this.$tab.openPage(`计划执行[${row.name}]`, "/testplan/execute", {id: row.id, name: row.name});
},
editSubmitForm() {
const form = {
...this.editForm,

View File

@@ -2,45 +2,143 @@
<div class="app-container">
<!-- 页面标题 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24">
<el-breadcrumb separator="/">
<el-breadcrumb-item>测试计划</el-breadcrumb-item>
<el-breadcrumb-item>概览</el-breadcrumb-item>
</el-breadcrumb>
<el-col :span="24" class="page-header">
<el-page-header :title="testTitle" @back="goBack">
</el-page-header>
<el-dropdown :value="testStatus" trigger="click" @command="handleCommand">
<span class="el-dropdown-link">{{ dropdownText }}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="(text, key) in statusMap"
:key="key"
:command="key"
:disabled="key === testStatus">
{{ text }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect">
<el-menu-item index="1">概览</el-menu-item>
<el-menu-item index="2">执行</el-menu-item>
<el-menu-item index="3">报告</el-menu-item>
<el-menu-item index="4">缺陷</el-menu-item>
<el-menu-item index="5">设置</el-menu-item>
</el-menu>
</el-col>
</el-row>
<!-- 测试计划详情 -->
<el-card class="box-card">
</el-card>
<!-- 测试用例 -->
<el-card class="box-card">
</el-card>
<!-- 测试计划界面 -->
<probably-view :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 v-show="activeIndex === '4'"></test-defects>
<test-setting v-show="activeIndex === '5'"></test-setting>
</div>
</template>
<script>
import LineChart from '@/views/dashboard/LineChart';
import router from "@/router";
import {getTestPlanDetail, updateTestPlan} from "@/api/test/testPlan";
import ProbablyView from "@/views/test/testplan/probablyView/probablyView.vue";
import CaseReport from "@/views/test/testplan/caseReport/caseReport.vue";
import caseExecute from "@/views/test/testplan/execute/caseExecute.vue";
import TestDefects from "@/views/test/testplan/defects/testDefects.vue";
import TestSetting from "@/views/test/testplan/setting/testSetting.vue";
export default {
name: 'overview',
components: {
LineChart,
TestSetting,
TestDefects,
caseExecute,
CaseReport,
ProbablyView,
},
data() {
return {
editForm: {},
statusMap: {
'0': '未开始',
'1': '进行中',
'2': '已完成',
'3': '已终止'
},
planId: '',
testStatus: '',
testTitle: '',
activeTab: 'overview',// 默认选中的标签页
activeIndex: '1',
};
},
methods: {
// 可以在这里添加更多的方法来处理数据或事件
computed: {
dropdownText() {
return this.statusMap[this.testStatus] || '进行中';
}
},
methods: {
handleCommand(command) {
this.$confirm(`确定切换为【${this.statusMap[command]}】状态?`, '操作确认', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.testStatus = command; // 更新选中状态
this.editForm.status = command;
const form = {
...this.editForm
}
updateTestPlan(form)
.then(() => {
this.$message.success(`切换成功:${this.statusMap[command]}`);
})
// 执行数据刷新等操作
}).catch(() => {
// 用户取消操作时的处理(可选)
});
},
goBack() {
this.$router.back();
// 关闭当前页面
this.$tab.closePage(router.currentRoute);
},
handleSelect(key) {
this.activeIndex = key
},
},
created() {
getTestPlanDetail(this.$route.query.id).then(res => {
this.editForm = res.data
})
this.planId = this.$route.query.id;
console.log(this.planId)
this.testTitle = this.$route.query.name
this.testStatus = this.$route.query.testStatus
}
};
</script>
<style lang="scss" scoped>
.el-dropdown-link {
cursor: pointer;
color: #409EFF;
}
.el-icon-arrow-down {
font-size: 12px;
}
.page-header {
::v-deep.el-page-header__title {
font-size: 20px;
color: #000000;
font-weight: bold;
}
display: flex;
align-items: center;
border-bottom: solid 1px #e6e6e6;
}
</style>

View File

@@ -0,0 +1,335 @@
<template>
<div class="app-container">
<!-- 页面标题 -->
<el-row :gutter="10" class="mb8">
<el-col :span="24" class="page-header">
<h3 style="font-size: 20px;color: #000000">测试计划概览</h3>
</el-col>
</el-row>
<!-- 测试计划详情 -->
<el-row :gutter="10">
<el-col :span="24">
<el-card class="box-card">
<div slot="header" class="clearfix">
<el-row>
<span style="font-size: 18px;color: #3A71A8">测试计划详情</span>
</el-row>
<el-row :gutter="10">
<el-col :span="5">
<!-- <span>起止时间{{ startToEndTime }}</span>-->
<span>起止时间2020-04-01 ~ 2020-04-30</span>
</el-col>
<el-col :span="7">
<span>当前测试计划已经开始 3 ,距离截止时间还有 52 </span>
</el-col>
<el-col :span="5">
<span>负责人{{ manager }}</span>
</el-col>
</el-row>
</div>
<el-row :gutter="10">
<el-col :span="8">
<el-card shadow="hover">
<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>执行进度</p>
<p>用例总数 1</p>
<p><span style="color: #409EFF"></span> 已执行用例 1</p>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<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>执行用例通过率</p>
</el-col>
<el-col class="rate-content" :span="12">
<p>100%</p>
<el-progress :percentage="100" status="success"></el-progress>
<p>总体用例通过率</p>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover">
<div class="progress-content">
<div class="defect-content">
<p>0</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-row>
<el-row>
<el-col :span="4">待确认</el-col>
<el-col :span="4">修复中</el-col>
<el-col :span="4">待验证</el-col>
<el-col :span="4">无效缺陷</el-col>
<el-col :span="4">挂起</el-col>
</el-row>
</div>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
</el-col>
</el-row>
<!-- 测试用例 -->
<el-row :gutter="10">
<el-col :span="10">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>用例执行分布</span>
</div>
<el-card shadow="hover">
<div class="distribution-content">
<el-progress type="circle" :percentage="100" color="#67C23A"></el-progress>
<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>
</div>
</div>
</el-card>
</el-card>
</el-col>
<el-col :span="14">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>用例执行情况趋势</span>
</div>
<el-card shadow="hover">
<div class="distribution-text" style="height: 200px">
<line-chart :data="trendData" :chart-data="trendData"></line-chart>
</div>
</el-card>
</el-card>
</el-col>
</el-row>
<el-card class="box-card">
<div>
关联需求信息
<!-- 标签页 -->
<el-tabs style="margin-top: 10px;">
<el-Table v-loading="loading" :data="list">
<el-table-column prop="serialNumber" label="ID" align="center"/>
<el-table-column prop="version" label="版本" align="center"/>
<el-table-column prop="outline" label="概要" align="center"/>
<el-table-column prop="priority" label="优先级" align="center">
<template slot-scope="scope">
<el-tag :type="priorityColor[scope.row.priority]">{{ scope.row.priority }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" align="center">
<template #default="{ row }">
<dict-tag :options="dict.type.status" :value="row.status"/>
</template>
</el-table-column>
<el-table-column prop="manager" label="负责人" align="center"/>
<el-table-column prop="createTime" label="创建时间" align="center"/>
</el-Table>
</el-tabs>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</el-card>
</div>
</template>
<script>
import LineChart from '@/views/dashboard/LineChart';
import * as echarts from 'echarts';
import {getTestPlanProjectList} from "@/api/test/testPlan";
export default {
name: 'probablyView',
components: {
LineChart,
},
props: {
planId: {
type: String,
default: '',
},
},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
mounted() {
this.initExecutionProgressChart();
},
data() {
return {
priorityColor: {
P0: 'danger', // 红色
P1: 'warning', // 黄色
P2: 'info', // 蓝色
P3: 'success', // 绿色
p4: 'success'
},
total: 0,
list: [],
loading: false,
myChart: null,
startToEndTime: '',
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],
},
],
},
};
},
created() {
this.getList();
},
methods: {
getList() {
this.loading = true;
this.queryParams.planId = this.planId;
getTestPlanProjectList(this.queryParams.planId).then(response => {
this.list = response.rows;
this.total = response.total;
this.loading = false;
})
},
// 初始化执行进度饼图
initExecutionProgressChart() {
const chartDom = document.getElementById('executionProgressChart');
const myChart = echarts.init(chartDom);
const option = {
tooltip: {
trigger: 'item'
},
legend: {
top: '5%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{value: 1},
]
}
]
};
myChart.setOption(option);
// 监听窗口大小变化,自动调整图表大小
window.addEventListener('resize', () => {
myChart.resize();
});
},
},
};
</script>
<style scoped lang="scss">
.app-container {
.page-header {
margin-bottom: 20px;
}
.box-card {
margin-bottom: 20px;
}
.progress-content {
display: flex;
align-items: center;
justify-content: center;
height: 200px;
.progress-text {
margin-left: 20px;
}
}
.rate-content {
}
.defect-content {
text-align: center;
width: 400px;
}
.distribution-content {
display: flex;
align-items: center;
.distribution-text {
margin-left: 20px;
}
}
.trend-content {
}
}
</style>