需求功能前后端代码实现

This commit is contained in:
pfl
2025-04-17 17:13:33 +08:00
parent 7645801fcb
commit 62f9dd359f
10 changed files with 885 additions and 0 deletions

View File

@@ -0,0 +1,419 @@
<template>
<div class="app-container">
<!-- 顶部导航 -->
<el-row :gutter="10">
<el-col :span="24">
<el-header class="header">
<div class="head1">
<span style="font-size: 18px; font-weight: bold; margin-right: 20px;">需求列表</span>
</div>
<div class="head2">
<el-input :placeholder="placeholderText" v-model="query" class="input-with-select" clearable>
<el-select v-model="select" slot="prepend" placeholder="请选择" style="width: 80px;">
<el-option label="概要" value="1"></el-option>
<el-option label="ID" value="2"></el-option>
</el-select>
<el-button slot="append" icon="el-icon-search" @click="handleQuery"></el-button>
</el-input>
<el-button
type="primary"
size="medium"
style="margin-right: 10px;"
@click="handleCollapse(!activeNames.includes('1'))"
>高级筛选
</el-button>
<el-dropdown trigger="click" placement="bottom-end">
<span class="el-dropdown-link">
列表视图<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>列表视图</el-dropdown-item>
<el-dropdown-item>看板视图</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button type="primary" size="medium" style="margin-left: 10px;" @click="addProjectVue">新建需求</el-button>
</div>
</el-header>
</el-col>
</el-row>
<el-collapse v-model="activeNames">
<el-collapse-item class="collapse-search" name="1">
<el-form ref="queryForm" :model="queryParams" label-width="110px">
<el-row :gutter="10" class="high-search">
<el-form-item label="创建时间">
<el-date-picker
v-model="queryParams.createTime"
type="daterange"
range-separator=""
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
></el-date-picker>
</el-form-item>
<el-form-item label="负责人">
<el-select v-model="queryParams.manager" placeholder="请选择" clearable filterable>
<simple-options :options="managerList"/>
</el-select>
</el-form-item>
<el-form-item label="优先级">
<el-select v-model="queryParams.priority" placeholder="请选择" clearable filterable>
<simple-options :options="dict.type.priority_level"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" icon="el-icon-search" @click="handleAdvancedSearch">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetAdvancedFilter">重置</el-button>
</el-form-item>
</el-row>
</el-form>
</el-collapse-item>
</el-collapse>
<!-- 标签页 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick" style="margin-top: 10px;">
<el-tab-pane :label="'全部(' + statusCounts.all + ')'" name="all"></el-tab-pane>
<el-tab-pane :label="'未开始(' + statusCounts.notStarted + ')'" name="0"></el-tab-pane>
<el-tab-pane :label="'进行中(' + statusCounts.inProgress + ')'" name="1"></el-tab-pane>
<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">
<el-table-column type="selection"/>
<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"/>
<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-column label="操作" align="left" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click.native.stop="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-Table>
</el-tabs>
<!-- 操作按钮 -->
<div style="margin-top: 10px; display: flex; justify-content: space-between; align-items: center;">
<div>
<el-button type="primary" size="small">全部导出</el-button>
<el-button size="small">批量导出</el-button>
</div>
<div>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
</div>
</div>
<el-dialog title="新建需求" :visible.sync="addOpen" width="90%">
<el-form ref="form" :rules="rules" :model="form" label-width="110px" label-position="right">
<el-container>
<el-main>
<el-form-item label="需求概要" prop="outline">
<el-input v-model="form.outline" placeholder="请输入"></el-input>
</el-form-item>
<!-- 描述 -->
<el-form-item label="描述" prop="detail">
<el-input type="textarea" v-model="form.detail" :rows="22"></el-input>
</el-form-item>
</el-main>
<el-aside width="450px" class="basic-information">
<h3>基础信息</h3>
<el-form-item label="需求名称" prop="name">
<el-input v-model="form.name" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="需求ID" prop="serialNumber">
<el-input v-model="form.serialNumber" placeholder="请输入"></el-input>
</el-form-item>
<el-form-item label="负责人" prop="manager">
<el-select v-model="form.manager" placeholder="请选择负责人" clearable>
<SimpleOptions :options="managerList"/>
</el-select>
</el-form-item>
<el-form-item label="优先级" prop="priority">
<el-select v-model="form.priority" placeholder="未设置" clearable filterable>
<simple-options :options="dict.type.priority_level"/>
</el-select>
</el-form-item>
<el-form-item label="预期完成时间" prop="estimatedTime">
<el-date-picker
style="width: 100%"
v-model="form.estimatedTime"
type="date"
placeholder="请选择预期完成时间"
></el-date-picker>
</el-form-item>
<el-form-item label="需求来源" prop="source">
<el-select v-model="form.source" placeholder="未设置" clearable>
<simple-options :options="dict.type.project_source"/>
</el-select>
</el-form-item>
<el-form-item label="需求类型" prop="type">
<el-select v-model="form.type" placeholder="未设置" clearable>
<simple-options :options="dict.type.project_type"/>
</el-select>
</el-form-item>
<el-form-item label="版本" prop="version">
<el-input v-model="form.version" placeholder="请输入版本"></el-input>
</el-form-item>
</el-aside>
</el-container>
</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 {addProject, delProject, getProjectList, managerList} from "@/api/test/project";
import SimpleOptions from "@/components/FormItem/option/SimpleOptions.vue";
import {delCase} from "@/api/test/case";
export default {
name: 'project',
components: {SimpleOptions},
dicts: ['priority_level', 'project_source', 'project_type', 'status'],
data() {
return {
statusCounts: {
all: 0,
notStarted: 0,
inProgress: 0,
completed: 0,
terminated: 0
},
query: '',
rules: {
serialNumber: [{required: true, message: '请输入需求ID', trigger: 'blur'}],
detail: [{required: true, message: '请输入描述', trigger: 'blur'}],
name: [{required: true, message: '请输入需求名称', trigger: 'blur'}],
outline: [{required: true, message: '请输入需求概要', trigger: 'blur'}],
manager: [{required: true, message: '请选择负责人', trigger: 'blur'}],
priority: [{required: true, message: '请选择优先级', trigger: 'blur'}],
estimatedTime: [{required: true, message: '请选择预期完成时间', trigger: 'blur'}],
source: [{required: true, message: '请选择需求来源', trigger: 'blur'}],
type: [{required: true, message: '请选择需求类型', trigger: 'blur'}]
},
input3: '',
select: '1',
searchKeyword: '',
activeTab: 'all',
total: 0,
list: [],
// 遮罩层
loading: false,
submitLoading: false,
//新增弹窗
addOpen: false,
managerList: [],
queryParams: {
serialNumber: '',
outline: '',
manager: '',
priority: '',
createTime: [],
pageNum: 1,
pageSize: 10,
},
activeNames: [], // 控制 collapse 的展开状态
form: {
serialNumber: '',
name: '',
outline: '',
detail: '',
manager: '',
priority: '',
estimatedTime: '',
source: '',
type: '',
version: ''
}
};
},
created() {
this.getList();
this.getManagerList();
},
computed: {
placeholderText() {
return this.select === '1' ? '请输入需求概要搜索' : '请输入需求ID搜索';
},
},
methods: {
handleTabClick(tab) {
this.activeTab = tab.name;
this.getList();
},
// 获取负责人列表
getManagerList() {
managerList()
.then((list) => {
this.managerList = (list.rows || []).map((e) => ({value: e.userId, label: e.userName}))
})
},
// 搜索
handleQuery() {
if (this.select === '1') {
this.queryParams.outline = this.query;
this.queryParams.serialNumber = '';
} else if (this.select === '2') {
this.queryParams.serialNumber = this.query;
this.queryParams.outline = '';
}
this.getList();
},
addProjectVue() {
this.addOpen = true;
this.reset();
},
/** 查询列表 */
getList() {
this.loading = true
const [startCreateTime, endCreateTime] = this.queryParams.createTime || []
const queryParams = {
...this.queryParams,
startCreateTime,
endCreateTime,
status: this.activeTab !== 'all' ? this.activeTab : ''
}
getProjectList(queryParams).then(list => {
this.list = list.rows;
this.total = list.total;
this.loading = false
// 统计状态数量
this.statusCounts.all = this.total;
this.statusCounts.notStarted = this.list.filter(item => item.status === '0').length;
this.statusCounts.inProgress = this.list.filter(item => item.status === '1').length;
this.statusCounts.completed = this.list.filter(item => item.status === '2').length;
this.statusCounts.terminated = this.list.filter(item => item.status === '3').length
})
},
// 表单重置
reset() {
const form = {}
Object.keys(this.form).forEach((key) => {
form[key] = ''
})
this.form = form
this.resetForm('form')
},
// 展开/折叠
handleCollapse(val) {
this.activeNames = val ? ['1'] : [];
},
handleAdvancedSearch() {
// 高级筛选条件搜索
this.queryParams.pageNum = 1
this.getList()
},
resetAdvancedFilter() {
// 重置高级筛选条件
this.resetForm('queryFrom')
this.handleAdvancedSearch()
},
submitForm() {
const form = {
...this.form,
}
console.log('submit', form)
this.$refs?.form.validate((valid) => {
if (!valid) {
return
}
this.submitLoading = true
addProject(form)
.then(() => {
this.$message.success('添加成功')
this.addOpen = false
this.getList()
})
.catch(() => {
this.$message.error('添加失败')
})
.finally(() => {
this.submitLoading = false
})
})
},
handleDelete(id) {
this.$modal.confirm('是否确认删除需求?').then(function () {
return delProject(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
});
}
}
};
</script>
<style lang="scss" scoped>
.input-with-select {
background-color: #ffffff;
width: 300px;
}
.collapse-search {
::v-deep .el-collapse-item__header {
display: none;
}
}
.high-search {
display: flex;
justify-content: space-between;
padding-top: 40px;
background-color: rgb(248, 248, 249);
}
.header {
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.head1 {
display: flex;
align-items: center;
}
.head2 {
display: flex;
align-items: center;
flex: 1;
justify-content: flex-end;
column-gap: 10px;
}
.el-dialog .el-form {
width: 100%;
}
.el-dialog .el-form-item {
margin-bottom: 20px;
}
.el-dialog .el-textarea {
width: 100%;
}
.basic-information {
background-color: rgb(248, 248, 249) !important;
}
</style>