1、需求详情前后端代码完善

2、缺陷管理详情前后端代码完善
This commit is contained in:
pfl
2025-05-30 17:58:46 +08:00
parent 606c3c15ea
commit c6a73afb76
27 changed files with 1011 additions and 9 deletions

View File

@@ -5,7 +5,9 @@ const api = {
addBug: 'test/defect/addBug',
delBug: 'test/defect/delBug',
getBugDetail: 'test/defect/bugDetail',
updateBug: 'test/defect/editBug'
updateBug: 'test/defect/editBug',
getDefectPlanList: 'testPlan/defect/defectPlanList',
getDefectProjectList: 'testPlan/defect/defectProjectList',
}
export function getBugList(data) {
@@ -49,3 +51,19 @@ export function updateBug(data) {
})
}
export function getDefectPlanList(id) {
return request({
url: api.getDefectPlanList,
method: 'post',
data: {id}
})
}
export function getDefectProjectList(id) {
return request({
url: api.getDefectProjectList,
method: 'post',
data: {id}
})
}

View File

@@ -6,7 +6,10 @@ const api = {
addProject: 'test/project/addProject',
delProject: 'test/project/delProject',
getProjectDetail: 'test/project/projectDetail',
updateProject: 'test/project/editProject'
updateProject: 'test/project/editProject',
relatePlanList: 'test/testPlanProject/relatePlanList',
relateCaseList: 'test/testPlanProject/relateCaseList',
relateDefectList: 'test/testPlanProject/relateDefectList',
}
export function managerList(data) {
@@ -58,3 +61,27 @@ export function updateProject(data) {
})
}
export function getRelatePlanList(id) {
return request({
url: api.relatePlanList,
method: 'post',
data: {id}
})
}
export function getRelateCaseTableList(id) {
return request({
url: api.relateCaseList,
method: 'post',
data: {id}
})
}
export function getRelateDefectList(id) {
return request({
url: api.relateDefectList,
method: 'post',
data: {id}
})
}

View File

@@ -0,0 +1,136 @@
<template>
<div class="app-container">
<el-form ref="detailForm" :model="detailForm" label-width="110px" label-position="right">
<el-container>
<el-main>
<el-form-item label="缺陷概要" prop="outline">
{{ detailForm.outline }}
</el-form-item>
<!-- 描述 -->
<el-form-item label="描述" prop="detail">
{{ detailForm.detail }}
</el-form-item>
</el-main>
<el-aside width="450px" class="basic-information">
<h3>基础信息</h3>
<el-form-item label="缺陷ID" prop="serialNumber">
{{detailForm.serialNumber}}
</el-form-item>
<el-form-item label="缺陷名称" prop="name">
{{ detailForm.name }}
</el-form-item>
<el-form-item label="经办人" prop="manager">
{{ managerName }}
</el-form-item>
<el-form-item label="严重程度" prop="level">
{{ detailForm.level }}
</el-form-item>
<el-form-item label="缺陷类型" prop="type">
{{ detailForm.type }}
</el-form-item>
<el-form-item label="缺陷状态" prop="status">
{{ detailForm.status }}
</el-form-item>
<el-form-item label="能否复现" prop="reappearance">
{{ reappearanceLabel }}
</el-form-item>
<el-form-item label="开发人" prop="dev">
{{ detailForm.dev }}
</el-form-item>
<el-form-item label="测试人" prop="test">
{{ detailForm.test }}
</el-form-item>
<el-form-item label="版本" prop="version">
{{ detailForm.version }}
</el-form-item>
</el-aside>
</el-container>
</el-form>
</div>
</template>
<script>
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {getBugDetail} from "@/api/test/bug";
export default {
name: 'bugDetail',
components: {SimpleOptions},
dicts: ['severity_level', 'bug_type', 'bug_status'],
props: {
defectId: {
type: Number,
default: 0,
},
managerName: {
type: String,
default: ''
},
managerList: {
type: Array,
default: () => []
}
},
data() {
return {
reappearanceOptions: [{
value: '0',
label: '必然复现'
}, {
value: '1',
label: '偶发复现'
}],
managerList: [],
detailForm: {
outline: '',
detail: '',
serialNumber: '',
name: '',
manager: '',
level: '',
type: '',
status: '',
reappearance: '',
dev: '',
test: '',
version: '',
},
}
},
computed: {
reappearanceLabel() {
const option = this.reappearanceOptions.find(
(opt) => opt.value === this.detailForm.reappearance
);
return option ? option.label : '';
},
},
watch: {
defectId(newVal){
this.getDetail()
}
},
methods: {
getDetail() {
getBugDetail(this.defectId).then(res => {
this.detailForm = res.data;
this.detailForm.level = this.dict.type.severity_level.find(e => e.value === res.data.level).label;
this.detailForm.type = this.dict.type.bug_type.find(e => e.value === res.data.type).label;
this.detailForm.status = this.dict.type.bug_status.find(e => e.value === res.data.status).label;
this.detailForm.dev = this.managerList.find(e => e.value === parseInt(res.data.dev,10)).label;
this.detailForm.test = this.managerList.find(e => e.value === parseInt(res.data.test,10)).label;
})
}
}
}
</script>
<style scoped lang="scss">
.basic-information {
background-color: rgb(248, 248, 249) !important;
}
</style>

View File

@@ -0,0 +1,65 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" height="500">
<el-table-column prop="statusLabel" label="状态" width="180"></el-table-column>
<el-table-column prop="name" label="计划名称"></el-table-column>
<el-table-column prop="manager" label="负责人"></el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"></pagination>
</div>
</template>
<script>
import {getDefectPlanList} from "@/api/test/bug";
export default {
name: 'defectPlan',
dicts: ['status'],
props: {
defectId: {
type: Number,
default: '',
},
},
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10
}
}
},
watch: {
defectId(newVal){
this.getList()
}
},
methods: {
getList() {
this.loading = true;
getDefectPlanList(this.defectId).then(response => {
this.list = response.rows.map(item => {
const matched = this.dict.type.status.find(e => e.value === item.status);
return {
...item,
statusLabel: matched ? matched.label : '未知状态'
};
});
this.total = response.total;
this.loading = false;
})
},
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,74 @@
<template>
<div class="app-container">
<el-Table v-loading="loading" :data="list" height="500">
<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>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
</div>
</template>
<script>
import {getDefectProjectList} from "@/api/test/bug";
export default {
name: 'defectProject',
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
props: {
defectId: {
type: Number,
default: '',
},
},
data() {
return {
list: [],
loading: false,
total: 0,
priorityColor: {
'P0': 'danger',
'P1': 'warning',
'P2': 'info',
'P3': 'success',
},
queryParams: {
pageNum: 1,
pageSize: 10,
}
}
},
watch: {
defectId(newVal){
this.getList()
}
},
methods: {
getList() {
this.loading = true
getDefectProjectList(this.defectId).then(response => {
this.list = response.rows
this.total = response.total
this.loading = false
})
}
}
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -62,7 +62,7 @@
</el-collapse>
<!-- 标签页 -->
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange" @row-click="handleRowClick">
<el-table-column type="selection"/>
<el-table-column prop="serialNumber" label="ID" align="center"/>
<el-table-column prop="outline" label="概要" align="center"/>
@@ -80,7 +80,7 @@
<el-table-column prop="createTime" label="创建时间" align="center"/>
<el-table-column label="操作" align="left" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click.native.stop="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
</el-button>
</template>
@@ -237,6 +237,25 @@
<el-button type="primary" @click="editSubmitForm"> </el-button>
</span>
</el-dialog>
<el-dialog :visible.sync="detailOpen" width="90%">
<template #title>
<div class="detail-title">
<el-row>
<span>查看缺陷-{{ title }}</span>
<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>
</el-row>
<bug-detail :managerList="managerList" :managerName="managerName" :defectId="defectId" v-show="activeIndex === '1'"></bug-detail>
<defect-plan :defectId="defectId" v-show="activeIndex === '3'"></defect-plan>
<defect-project :defectId="defectId" v-show="activeIndex === '4'"></defect-project>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -245,10 +264,13 @@ import {managerList} from "@/api/test/project";
import {addBug, delBug, getBugDetail, getBugList, updateBug} from "@/api/test/bug";
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {requestDownload} from "@/utils/request";
import BugDetail from "@/views/test/bug/components/bugDetail.vue";
import DefectPlan from "@/views/test/bug/components/defectPlan.vue";
import DefectProject from "@/views/test/bug/components/defectProject.vue";
export default {
name: 'defect',
components: {SimpleOptions},
components: {DefectProject, DefectPlan, BugDetail, SimpleOptions},
dicts: ['severity_level', 'bug_type', 'bug_status'],
data() {
return {
@@ -294,6 +316,10 @@ export default {
addOpen: false,
//编辑弹窗
editOpen: false,
detailOpen: false,
activeIndex: '1',
defectId: 0,
managerName: '',
selectedRows: [],
selectedData: [],
managerList: [],
@@ -336,6 +362,16 @@ export default {
},
},
methods: {
handleRowClick(row) {
this.defectId = row.id
this.activeIndex = '1'
this.managerName = row.manager
this.detailOpen = true;
this.title = row.serialNumber;
},
handleSelect(key) {
this.activeIndex = key
},
handleSelectionChange(selection) {
this.selectedRows = selection;
},
@@ -437,6 +473,8 @@ export default {
getBugDetail(id).then((res) => {
this.editForm = res.data
this.editForm.manager = parseInt(res.data.manager, 10);
this.editForm.dev = parseInt(res.data.dev, 10);
this.editForm.test = parseInt(res.data.test, 10);
this.editOpen = true
})
},

View File

@@ -71,7 +71,7 @@
<el-tab-pane :label="'已完成(' + statusCounts.completed + ')'" name="2"></el-tab-pane>
<el-tab-pane :label="'已终止(' + statusCounts.terminated + ')'" name="3"></el-tab-pane>
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
<el-Table v-loading="loading" :data="list" @selection-change="handleSelectionChange" @row-click="handleRowClick">
<el-table-column type="selection"/>
<el-table-column prop="serialNumber" label="ID" align="center"/>
<el-table-column prop="version" label="版本" align="center"/>
@@ -90,7 +90,7 @@
<el-table-column prop="createTime" label="创建时间" align="center"/>
<el-table-column label="操作" align="left" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click.native.stop="handleEdit(scope.row.id)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除
</el-button>
</template>
@@ -234,6 +234,32 @@
<el-button type="primary" @click="editSubmitForm"> </el-button>
</span>
</el-dialog>
<el-dialog :visible.sync="detailOpen" width="90%">
<template #title>
<div class="detail-title">
<el-row>
<span>查看需求-{{ title }}</span>
<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-item index="6">评审</el-menu-item>
</el-menu>
</el-row>
<project-detail :managerName="managerName" :projectId="projectId" v-show="activeIndex === '1'"></project-detail>
<relate-plan :projectId="projectId" v-show="activeIndex === '3'"></relate-plan>
<relateCaseTable :projectId="projectId" v-show="activeIndex === '4'"></relateCaseTable>
<relateDefectTable :projectId="projectId" v-show="activeIndex === '5'"></relateDefectTable>
</div>
</template>
<span slot="footer" class="dialog-footer">
<el-button @click="detailOpen = false"> </el-button>
</span>
</el-dialog>
</div>
</template>
@@ -241,10 +267,14 @@
import {addProject, delProject, getProjectDetail, getProjectList, managerList, updateProject} from "@/api/test/project";
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {requestDownload} from "@/utils/request";
import ProjectDetail from "@/views/test/project/projectDetail/projectDetail.vue";
import RelatePlan from "@/views/test/project/projectDetail/relatePlan.vue";
import RelateCaseTable from "@/views/test/project/projectDetail/relateCaseTable.vue";
import RelateDefectTable from "@/views/test/project/projectDetail/relateDefectTable.vue";
export default {
name: 'project',
components: {SimpleOptions},
components: {RelatePlan, ProjectDetail, SimpleOptions , RelateCaseTable , RelateDefectTable},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
data() {
return {
@@ -281,6 +311,8 @@ export default {
total: 0,
list: [],
title: '',
projectId: 0,
managerName: '',
// 遮罩层
loading: false,
editSubmitLoading: false,
@@ -289,9 +321,11 @@ export default {
addOpen: false,
//编辑弹窗
editOpen: false,
detailOpen: false,
selectedRows: [],
selectedData: [],
managerList: [],
activeIndex: '1',
queryParams: {
serialNumber: '',
outline: '',
@@ -327,6 +361,16 @@ export default {
},
},
methods: {
handleRowClick(row) {
this.projectId = row.id
this.activeIndex = '1'
this.managerName = row.manager
this.detailOpen = true;
this.title = row.serialNumber;
},
handleSelect(key) {
this.activeIndex = key
},
handleSelectionChange(selection) {
this.selectedRows = selection;
},

View File

@@ -0,0 +1,109 @@
<template>
<div class="project-detail">
<el-form ref="editForm" :model="detailForm" label-width="110px" label-position="right">
<el-container>
<el-main>
<el-form-item label="需求概要" prop="outline">
{{ detailForm.outline }}
</el-form-item>
<!-- 描述 -->
<el-form-item label="描述" prop="detail">
{{ detailForm.detail }}
</el-form-item>
</el-main>
<el-aside width="450px" class="basic-information">
<h3>基础信息</h3>
<el-form-item label="需求名称" prop="name">
{{ detailForm.name }}
</el-form-item>
<el-form-item label="状态" prop="status">
{{ detailForm.status }}
</el-form-item>
<el-form-item label="负责人" prop="manager">
{{ managerName }}
</el-form-item>
<el-form-item label="优先级" prop="priority">
{{ detailForm.priority }}
</el-form-item>
<el-form-item label="预期完成时间" prop="estimatedTime">
{{ detailForm.estimatedTime }}
</el-form-item>
<el-form-item label="需求来源" prop="source">
{{ detailForm.source }}
</el-form-item>
<el-form-item label="需求类型" prop="type">
{{ detailForm.type }}
</el-form-item>
<el-form-item label="版本" prop="version">
{{ detailForm.version }}
</el-form-item>
</el-aside>
</el-container>
</el-form>
</div>
</template>
<script>
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {getProjectDetail} from "@/api/test/project";
export default {
name: 'projectDetail',
components: {SimpleOptions},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
props: {
projectId: {
type: Number,
default: 0,
},
managerName: {
type: String,
default: ''
}
},
data() {
return {
detailForm: {
outline: '',
detail: '',
name: '',
status: '',
manager: '',
priority: '',
estimatedTime: '',
source: '',
type: '',
version: ''
},
managerList: [],
editOpen: false,
};
},
created() {
},
watch: {
projectId(newVal){
this.getDetail()
}
},
methods: {
getDetail() {
getProjectDetail(this.projectId).then(res => {
this.detailForm = res.data;
this.detailForm.source = this.dict.type.project_source.find(e => e.value === res.data.source).label;
this.detailForm.type = this.dict.type.project_type.find(e => e.value === res.data.type).label;
this.detailForm.status = this.dict.type.status.find(e => e.value === res.data.status).label;
});
}
}
}
</script>
<style lang="scss" scoped>
.basic-information {
background-color: rgb(248, 248, 249) !important;
}
</style>

View File

@@ -0,0 +1,54 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" height="500">
<el-table-column prop="name" label="用例名称"></el-table-column>
<el-table-column prop="status" label="用例状态" :formatter="row => ['','草稿', '通过', '不通过'][row.status]"></el-table-column>
<el-table-column prop="createBy" label="创建人"></el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
</div>
</template>
<script>
import {getRelateCaseTableList} from "@/api/test/project";
export default {
name: 'relateCaseTable',
props: {
projectId: {
type: Number,
default: '',
},
},
data() {
return {
loading: false,
list: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
},
}
},
watch: {
projectId(newVal){
this.getList()
}
},
methods: {
getList() {
this.loading = true
getRelateCaseTableList(this.projectId).then(res => {
this.list = res.rows
this.total = res.total
this.loading = false
})
}
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -0,0 +1,73 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" height="500">
<el-table-column prop="serialNumber" label="ID"></el-table-column>
<el-table-column prop="outline" label="摘要"></el-table-column>
<el-table-column prop="status" label="缺陷状态">
<template #default="{ row }">
<dict-tag :options="dict.type.bug_status" :value="row.status"/>
</template>
</el-table-column>
<el-table-column prop="manager" label="经办人"></el-table-column>
<el-table-column prop="level" label="严重程度">
<template #default="{ row }">
<dict-tag :options="dict.type.severity_level" :value="row.level"/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"/>
</div>
</template>
<script>
import {getRelateDefectList} from "@/api/test/project";
export default {
name: 'relateCaseTable',
props: {
projectId: {
type: Number,
default: '',
},
},
dicts: ['severity_level', 'bug_status'],
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 表格数据
list: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
}
}
},
watch: {
projectId(newVal){
this.getList()
}
},
methods: {
getList() {
this.loading = true
getRelateDefectList(this.projectId).then(response => {
this.list = response.rows
this.total = response.total
this.loading = false
})
}
},
}
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="app-container">
<el-table v-loading="loading" :data="list" height="500">
<el-table-column prop="statusLabel" label="状态" width="180"></el-table-column>
<el-table-column prop="name" label="计划名称"></el-table-column>
<el-table-column prop="manager" label="负责人"></el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList"></pagination>
</div>
</template>
<script>
import {getRelatePlanList} from "@/api/test/project";
export default {
name: 'relatePlan',
dicts: ['status'],
props: {
projectId: {
type: Number,
default: '',
},
},
data() {
return {
list: [],
loading: false,
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
},
}
},
watch: {
projectId(newVal){
this.getList()
}
},
methods: {
getList() {
this.loading = true;
getRelatePlanList(this.projectId).then(response => {
this.list = response.rows.map(item => {
const matched = this.dict.type.status.find(e => e.value === item.status);
return {
...item,
statusLabel: matched ? matched.label : '未知状态'
};
});
this.total = response.total;
this.loading = false;
})
},
}
}
</script>
<style lang="scss" scoped>
</style>