pms-front/src/views/project/demandManage/components/MainContentTable.vue

929 lines
24 KiB
Vue
Raw Normal View History

<template>
<div class="container">
<div class="topTitle">需求列表</div>
<!-- 搜索筛选区域 -->
<div class="search-filters">
<el-input
v-model="filters.title"
placeholder="请输入"
class="filter-input"
>
<template #prefix>
<div>标题名称</div>
</template>
</el-input>
<el-input
v-model="filters.responsiblePersonName"
placeholder="请选择负责人"
class="filter-input"
readonly
@focus="openUser('search')"
>
<template #prefix>负责人</template>
</el-input>
<el-select
v-model="filters.demandStatus"
placeholder="不限"
class="filter-select"
>
<template #prefix>需求状态</template>
<el-option
v-for="item in statusList"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
<el-select
v-model="filters.priority"
placeholder="不限"
class="filter-select"
>
<template #prefix>优先级</template>
<el-option
v-for="item in priorityList"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
<div class="filter-buttons">
<el-button type="primary" @click="handleSearch"></el-button>
<el-button @click="handleReset"></el-button>
</div>
</div>
<!-- 操作按钮区域 -->
<div class="table-operations">
<el-button type="primary" @click="handleAdd">
<el-icon class="el-icon-plus" />
新建需求
</el-button>
<el-button @click="handleBatchDelete"></el-button>
</div>
<!-- 表格区域 -->
<el-table
:data="tableData"
style="width: 100%"
@selection-change="handleSelectionChange"
class="tableBox"
>
<el-table-column type="selection" width="55" />
<el-table-column label="序号" width="70" prop="id" />
<el-table-column label="版本号" prop="versionNumber" />
<el-table-column label="标题" prop="title" />
<el-table-column label="需求状态">
<template #default="scope">
<div>
<el-dropdown
trigger="click"
placement="bottom"
@command="
(data) => {
changeRow(data, 'demandStatus', scope.row);
}
"
>
<el-radio
v-model="scope.row.demandStatus"
:label="scope.row.demandStatus"
:class="['status-tag', getStatusClass(scope.row.demandStatus)]"
>
{{
statusList[scope.row.demandStatus]
? statusList[scope.row.demandStatus].dictLabel
: ""
}}</el-radio
>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in statusList"
:key="item.dictValue"
:command="item.dictValue"
:class="{
tableSelect1: true,
selectedItem1: item.dictValue == scope.row.demandStatus,
}"
><el-radio
v-model="scope.row.demandStatus"
:label="scope.row.demandStatus"
:class="['status-tag', getStatusClass(item.dictValue)]"
>
{{
statusList[item.dictValue]
? statusList[item.dictValue].dictLabel
: ""
}}</el-radio
></el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</el-table-column>
<el-table-column label="负责人" prop="responsiblePersonName">
<template #default="scope">
<div @click="openUser('change', scope.row)" style="cursor: pointer">
{{ scope.row.responsiblePersonName }}
</div>
</template>
</el-table-column>
<el-table-column label="预计工时(天)" prop="estimatedWorkHours" />
<el-table-column label="开始时间" prop="createTime">
<template #default="scope">
{{ scope.row.createTime.split(" ")[0] }}
</template>
</el-table-column>
<el-table-column label="结束时间" prop="endTime">
<template #default="scope">
{{ scope.row.endTime.split(" ")[0] }}
</template>
</el-table-column>
<el-table-column label="优先级" prop="priority">
<template #default="scope">
<div>
<el-dropdown
trigger="click"
placement="bottom"
@command="
(data) => {
changeRow(data, 'priority', scope.row);
}
"
>
<span class="el-dropdown-link">
{{
priorityList.find(
(ele) => ele.dictValue == scope.row.priority
)
? priorityList.find(
(ele) => ele.dictValue == scope.row.priority
).dictLabel
: ""
}}
<i class="el-icon-arrow-down"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item
v-for="item in priorityList"
:key="item.dictValue"
:command="item.dictValue"
:class="{
tableSelect2: true,
selectedItem2: item.dictValue == scope.row.priority,
}"
>{{ item.dictLabel }}</el-dropdown-item
>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="scope">
<div>
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
style="color: #666"
>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<span class="total"> {{ total }} </span>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
/>
</div>
<SelectUser
:dialogVisible="userSelectDialogVisible"
:multiSelect="false"
:currentSelectedUser="currentSelectedUser"
@confirm="handleUserConfirm"
@close="handleUserClose"
/>
<el-dialog
:title="checkedRow.id ? '新建需求' : '修改需求'"
:visible.sync="dialogVisibleAdd"
width="40%"
>
<el-form
:inline="true"
:rules="rules"
:model="editData"
label-width="80px"
class="addForm"
ref="ruleForm"
>
<el-form-item label="标题" class="longItem" prop="title">
<el-input v-model="editData.title"></el-input>
</el-form-item>
<el-form-item label="所属项目" prop="projectId">
<el-input v-model="editData.projectName" disabled></el-input>
</el-form-item>
<el-form-item label="所属版本" prop="versionId">
<el-select
v-model="editData.versionId"
placeholder="请选择所属版本"
clearable
>
<el-option
v-for="item in versionList"
:key="item.nodeId"
:label="item.title"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="负责人" prop="responsiblePerson">
<el-input
v-model="editData.responsiblePersonName"
placeholder="请选择负责人"
class="filter-input"
readonly
@focus="openUser('add')"
>
</el-input>
</el-form-item>
<el-form-item label="需求状态" prop="demandStatus">
<el-select
v-model="editData.demandStatus"
placeholder="请选择需求状态"
>
<el-option
v-for="item in statusList"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item label="优先级" prop="priority">
<el-select v-model="editData.priority" placeholder="请选择优先级">
<el-option
v-for="item in priorityList"
:key="item.dictValue"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
</el-form-item>
<el-form-item label="预计工时" prop="estimatedWorkHours">
<el-select
v-model="editData.estimatedWorkHours"
filterable
allow-create
default-first-option
placeholder="请输入预计工时"
@change="changeTime"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="开始时间" prop="createTime">
<el-date-picker
@change="changeTime"
v-model="editData.createTime"
type="date"
placeholder="选择日期"
style="width: 240px"
value-format="yyyy-MM-dd 00:00:00"
>
</el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker
v-model="editData.endTime"
type="date"
placeholder="选择日期"
value-format="yyyy-MM-dd 23:59:59"
>
</el-date-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisibleAdd = false">取消</el-button>
<el-button type="primary" @click="confirmAddDemand"></el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { demandApi, systemApi } from "@/utils/api";
import SelectUser from "@/components/SelectUser.vue";
export default {
name: "MainContentTable",
components: {
SelectUser,
},
props: {
projectId: "",
version: {
type: Object,
default: {},
},
projectName: {
type: String,
default: "",
},
versionList: {
type: Array,
default: [],
},
},
data() {
return {
filters: {
title: "",
responsiblePersonName: "",
demandStatus: "",
priority: "",
responsiblePersonId: "",
},
tableData: [],
selectedRows: [],
checkedRow: {},
currentPage: 1,
pageSize: 10,
total: 0,
priorityList: [],
statusList: [],
userSelectDialogVisible: false,
dialogVisibleAdd: false,
currentSelectedUser: [],
editData: {
id: "",
title: "",
versionId: "",
demandStatus: "",
responsiblePerson: "",
estimatedWorkHours: "",
createTime: "",
endTime: "",
priority: "",
projectId: "",
},
rules: {
title: [
{ required: true, message: "请输入标题", trigger: "blur" },
{
min: 3,
max: 50,
message: "长度在 3 到 50 个字符",
trigger: "blur",
},
],
demandStatus: [
{
required: true,
message: "请选择需求状态",
trigger: "change",
},
],
responsiblePerson: [
{
required: true,
message: "请选择责任人",
trigger: "change",
},
],
estimatedWorkHours: [
{
required: true,
message: "请填写预计工时",
trigger: "change",
},
],
createTime: [
{ required: true, message: "请选择开始时间", trigger: "change" },
],
endTime: [
{ required: true, message: "请选择结束时间", trigger: "change" },
],
priority: [
{ required: true, message: "请选择优先级", trigger: "change" },
],
projectId: [
{ required: true, message: "请选择所属项目", trigger: "blur" },
],
},
options: new Array(10).fill(0).map((ele, index) => ({
value: index + 1,
label: index + 1 + "天",
})),
};
},
watch: {
version(newVal) {
this.$nextTick(() => {
this.getDemandList();
});
},
projectId(newVal) {
this.editData.projectId = newVal;
},
projectName(newVal) {
this.editData.projectName = newVal;
},
},
methods: {
handleSearch() {
// 实现搜索逻辑
this.getDemandList();
},
handleReset() {
this.filters = {
title: "",
responsiblePersonName: "",
responsiblePersonId: "",
demandStatus: "",
priority: "",
};
this.currentPage = 1;
this.pageSize = 10;
this.getDemandList();
},
handleAdd() {
// 实现新增逻辑
this.dialogVisibleAdd = true;
this.editData = {
id: "",
title: "",
versionId: "",
demandStatus: "",
responsiblePerson: "",
estimatedWorkHours: "",
createTime: "",
endTime: "",
priority: "",
projectId: "",
};
this.editData.projectId = this.projectId;
if (this.version.type == 0) {
this.editData.versionId = this.version.id;
}
this.editData.projectName = this.projectName;
},
handleEdit(row) {
this.editData = row;
this.editData.projectId = this.projectId;
this.editData.projectName = this.projectName;
this.dialogVisibleAdd = true;
// 实现编辑逻辑
},
handleDelete(row) {
// 实现删除逻辑
this.$confirm("此操作将永久删除该版本号, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
demandApi.delDemand(row.id).then((res) => {
this.$message({
type: "success",
message: "删除成功!",
});
this.getDemandList();
});
})
.catch(() => {});
},
handleBatchDelete() {
if (!this.selectedRows.length) {
this.$message({
type: "warning",
message: "请选择数据!",
});
return;
}
// 实现批量删除逻辑
this.$confirm("此操作将永久删除该版本号, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}).then(() => {
demandApi
.delDemandBatch(this.selectedRows.map((ele) => ele.id).join(","))
.then((res) => {
this.$message({
type: "success",
message: "删除成功!",
});
this.getDemandList();
});
});
},
handleSelectionChange(selection) {
this.selectedRows = selection;
},
handlePageChange(page) {
this.currentPage = page;
// 实现页码改变逻辑
},
getStatusClass(status) {
const classMap = {
0: "wait",
1: "pending",
2: "in-progress",
3: "completed",
4: "closed",
};
return classMap[status];
},
getDemandList() {
let param = {
...this.filters,
pageSize: this.pageSize,
pageNum: this.currentPage,
};
if (this.version.type == 0) {
param.versionId = this.version.id;
} else {
param.id = this.version.id;
}
demandApi.getDemandList(param).then((res) => {
this.tableData = res.rows;
this.total = res.total;
});
},
openUser(type, data) {
this.selectType = type;
if (type == "search") {
this.currentSelectedUser = [
{ userId: this.filters.responsiblePersonId },
];
} else if (type == "change") {
this.checkedRow = data;
this.currentSelectedUser = [{ userId: data.responsiblePerson }];
} else if (type == "add") {
this.currentSelectedUser = [
{ userId: this.editData.responsiblePerson },
];
}
this.userSelectDialogVisible = true;
},
handleUserConfirm(data) {
if (this.selectType == "search") {
this.filters.responsiblePersonName = data[0].nickName;
this.filters.responsiblePersonId = data[0].userId;
} else if (this.selectType == "change") {
this.checkedRow.responsiblePerson = data[0].userId;
this.checkedRow.responsiblePersonName = data[0].nickName;
this.eidtDemand();
} else if (this.selectType == "add") {
this.editData.responsiblePersonName = data[0].nickName;
this.editData.responsiblePerson = data[0].userId;
}
},
handleUserClose() {
this.userSelectDialogVisible = false;
},
eidtDemandRow() {
demandApi.eidtDemand(this.checkedRow).then((res) => {
this.$message({
message: "修改成功",
type: "success",
});
this.getDemandList();
});
},
async getDictData() {
const res1 = await systemApi.getDictData("demand_status");
const res2 = await systemApi.getDictData("demand_priority");
this.statusList = res1.data;
this.priorityList = res2.data;
},
confirmAddDemand() {
this.$refs.ruleForm.validate((valid) => {
if (valid) {
if (
this.editData.estimatedWorkHours.replace("天", "") !=
parseFloat(this.editData.estimatedWorkHours)
) {
this.$message({
message: "预计工时为数字",
type: "warning",
});
return;
}
if (
(this.editData.estimatedWorkHours * 10) % 5 != 0 ||
this.editData.estimatedWorkHours < 0.5
) {
this.$message({
message: "预计工时最小间隔为0.5天",
type: "warning",
});
return;
}
if (this.editData.id) {
demandApi.eidtDemand(this.editData).then((res) => {
this.$message({
message: "操作成功",
type: "success",
});
this.resetList();
});
} else {
demandApi.addDemand(this.editData).then((res) => {
this.$message({
message: "操作成功",
type: "success",
});
this.resetList();
});
}
}
});
},
resetList() {
let refreshId = "";
if (this.editData.versionId) {
refreshId = this.versionList.find(
(ele) => this.editData.versionId == ele.id
).nodeId;
} else {
refreshId = "-1";
}
this.$emit("refreshTree", refreshId);
this.dialogVisibleAdd = false;
},
changeRow(value, label, row) {
row[label] = value;
this.checkedRow = row;
this.$nextTick(() => {
this.eidtDemandRow(row);
});
},
changeTime(val) {
this.$nextTick(() => {
let day = (this.editData.estimatedWorkHours + "").replace("天", "");
if (day != parseFloat(day)&&day) {
this.$message({
message: "预计工时为数字",
type: "warning",
});
return;
}
if (this.editData.estimatedWorkHours && this.editData.createTime) {
if (day > 1) {
this.editData.endTime = this.moment(
new Date(this.editData.createTime).getTime()
)
.add(Math.ceil(day - 1), "days")
.format("YYYY-MM-DD 23:59:59");
} else {
this.editData.endTime = this.moment(
new Date(this.editData.createTime).getTime()
).format("YYYY-MM-DD 23:59:59");
}
console.log(this.editData.endTime);
}
});
},
},
created() {
this.getDictData();
},
};
</script>
<style scoped lang="scss">
.container {
padding: 0 20px 20px;
background-color: #ffffff;
min-width: 1200px;
}
.search-filters {
display: flex;
gap: 20px;
margin-bottom: 20px;
align-items: center;
flex-wrap: wrap;
}
.filter-input,
.filter-select {
width: 240px;
}
.filter-buttons {
display: flex;
gap: 10px;
}
.table-operations {
margin-bottom: 20px;
display: flex;
gap: 10px;
}
::v-deep .status-tag {
.el-radio__label {
padding-left: 5px !important;
}
}
::v-deep .status-tag .el-radio__inner {
height: 18px !important;
width: 18px !important;
line-height: 18px;
text-align: center;
margin-bottom: 1px;
background: #fff !important;
&::after {
height: 10px;
width: 10px;
}
}
::v-deep .status-tag.pending .el-radio__inner {
border-color: #ff7d00;
&::after {
background-color: #ff7d00 !important;
}
}
::v-deep .status-tag.in-progress .el-radio__inner {
border-color: #4096ff !important;
&::after {
background-color: #4096ff !important;
}
}
::v-deep .status-tag.completed .el-radio__inner {
border-color: #50b6aa !important;
&::after {
background-color: #50b6aa !important;
}
}
::v-deep .status-tag.closed .el-radio__inner,
::v-deep .status-tag.wait .el-radio__inner {
border-color: #999999 !important;
&::after {
background-color: #999999 !important;
}
}
::v-deep .status-tag.pending .el-radio__label {
color: #ff7d00 !important;
// background-color: #fff7e6;
}
::v-deep .status-tag.in-progress .el-radio__label {
color: #4096ff !important;
// background-color: #e6f4ff;
}
::v-deep .status-tag.completed .el-radio__label {
color: #50b6aa !important;
// background-color: #e6f7f5;
}
::v-deep .status-tag.closed .el-radio__label,
::v-deep .status-tag.wait .el-radio__label {
color: #999999 !important;
// background-color: #f5f5f5;
}
.pagination {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.total {
color: #666666;
font-size: 16px;
font-weight: 600;
}
::v-deep .el-button {
border-radius: 4px;
font-weight: 600;
}
::v-deep .el-table {
border: 1px solid #eeeeee;
}
::v-deep .el-table th {
background-color: #fafafa !important;
color: #666666;
font-weight: 600;
}
::v-deep .el-table td {
color: #333333;
}
::v-deep .el-input__prefix {
color: #999999;
line-height: 35px;
padding-left: 10px !important;
font-weight: 600;
}
.search-filters ::v-deep .el-input__inner {
padding-left: 80px !important;
::placeholder {
color: #bbb;
}
}
::v-deep .el-button--primary {
background-color: #4096ff;
}
::v-deep .el-pagination .el-pager li.is-active {
border-color: #4096ff;
background-color: #4096ff;
}
::v-deep .addForm {
.el-input__inner {
// width: 100%;
width: 240px;
}
.el-form-item {
width: 48%;
}
.el-input--prefix .el-input__inner {
padding-left: 40px;
}
}
::v-deep .longItem {
width: 100% !important;
.el-form-item__content {
width: 82.2%;
.el-input__inner {
width: 100%;
}
}
}
.tableSelect1 {
width: 150px;
text-align: left !important;
}
.tableSelect2 {
width: 100px;
text-align: left !important;
}
.selectedItem1 {
&::after {
content: "√";
width: 10px;
height: 10px;
position: absolute;
right: 10px;
color: #4096ff;
}
}
.selectedItem2 {
color: #4096ff;
position: relative;
&::after {
content: "√";
width: 10px;
height: 10px;
position: absolute;
right: 10px;
color: #4096ff;
}
}
.tableBox {
.el-radio {
margin-left: 0;
}
}
.topTitle {
padding: 0 20px 20px;
font-size: 18px;
font-weight: 600;
color: #333;
}
</style>