需求页面基本完成,日志初步开始
parent
6032c814e2
commit
f9a402e991
Binary file not shown.
After Width: | Height: | Size: 606 B |
Binary file not shown.
After Width: | Height: | Size: 775 B |
Binary file not shown.
After Width: | Height: | Size: 804 B |
|
@ -198,9 +198,49 @@ export const taskApi = {
|
|||
|
||||
}
|
||||
export const demandApi = {
|
||||
getDemandTree: (data) => request({
|
||||
getVersionTree: (data) => request({
|
||||
url: `/projectVersion/tree`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
addVersion: (data) => request({
|
||||
url: '/projectVersion/insert',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
// 删除版本号
|
||||
delVersion: (data) => request({
|
||||
url: `/projectVersion/${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
editVersion: (data) => request({
|
||||
url: '/projectVersion/update',
|
||||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
getDemandList: (data) => request({
|
||||
url: `/demand/list`,
|
||||
method: 'get',
|
||||
params: data,
|
||||
}),
|
||||
addDemand: (data) => request({
|
||||
url: '/demand/insert',
|
||||
method: 'post',
|
||||
data: data,
|
||||
}),
|
||||
eidtDemand: (data) => request({
|
||||
url: '/demand/update',
|
||||
method: 'put',
|
||||
data: data,
|
||||
}),
|
||||
delDemand: (data) => request({
|
||||
url: `/demand//${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
delDemandBatch: (data) => request({
|
||||
url: `/demand/remove/batch?ids=${data}`,
|
||||
method: 'delete',
|
||||
}),
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,928 @@
|
|||
<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>
|
|
@ -0,0 +1,415 @@
|
|||
<template>
|
||||
<div
|
||||
class="sidebarTree"
|
||||
:class="{
|
||||
hidenLeft: !showFlag,
|
||||
}"
|
||||
>
|
||||
<i
|
||||
class="el-icon-arrow-right"
|
||||
style="font-size: 24px; margin-top: 20px; cursor: pointer"
|
||||
v-show="!showFlag"
|
||||
@click.stop="changeShow()"
|
||||
></i>
|
||||
|
||||
<!-- 树形菜单 -->
|
||||
<div class="treeTitle">版本列表</div>
|
||||
<div class="topBox" v-show="showFlag">
|
||||
<div class="topText" @click.stop="changeOpen">
|
||||
<i v-show="openFlag" class="el-icon-caret-bottom"></i>
|
||||
<i v-show="!openFlag" class="el-icon-caret-right"></i>
|
||||
<img src="@/assets/demand/list.png" />
|
||||
<span>全部版本</span>
|
||||
</div>
|
||||
<div class="topBtn">
|
||||
<i
|
||||
class="el-icon-plus"
|
||||
@click.stop="dialogVisible = true"
|
||||
style="font-size: 20px"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-arrow-left"
|
||||
style="font-size: 20px"
|
||||
@click.stop="changeShow()"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<el-tree
|
||||
v-show="showFlag"
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
:default-expanded-keys="defaultExpend"
|
||||
node-key="nodeId"
|
||||
class="tree"
|
||||
ref="treeRef"
|
||||
@node-click="handleNodeClick"
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div
|
||||
class="treeNode"
|
||||
@mousemove="data.hover = true"
|
||||
@mouseout="data.hover = false"
|
||||
>
|
||||
<!-- 展开/收起图标 -->
|
||||
|
||||
<!-- 节点图标 -->
|
||||
<img
|
||||
v-if="
|
||||
(data.nodeId === selectedId ? 'selected' : '') && data.type == 0
|
||||
"
|
||||
src="@/assets/demand/treeIcon.png"
|
||||
class="nodeIcon"
|
||||
/>
|
||||
<img
|
||||
v-if="
|
||||
(data.nodeId !== selectedId ? 'selected' : '') &&
|
||||
data.id != 0 &&
|
||||
data.type == 0
|
||||
"
|
||||
src="@/assets/demand/treeIcon1.png"
|
||||
class="nodeIcon"
|
||||
/>
|
||||
<!-- 节点文本 -->
|
||||
<span
|
||||
:class="['nodeLabel', data.nodeId === selectedId ? 'selected' : '']"
|
||||
>
|
||||
{{ data.title }}
|
||||
</span>
|
||||
|
||||
<!-- 右侧数字标记 -->
|
||||
<span
|
||||
v-if="data.type == 0 && !data.hover"
|
||||
:class="[
|
||||
'count',
|
||||
data.nodeId === selectedId ? 'selectedCount' : '',
|
||||
]"
|
||||
>
|
||||
{{ data.childrenList.length }}
|
||||
</span>
|
||||
<el-dropdown
|
||||
v-show="data.type == 0 && data.hover"
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
@command="
|
||||
(val) => {
|
||||
changeRow(val, data);
|
||||
}
|
||||
"
|
||||
>
|
||||
<i class="el-icon-more" style="font-size: 20px;"></i>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="add">新建需求</el-dropdown-item>
|
||||
<el-dropdown-item command="edit">编辑</el-dropdown-item>
|
||||
<el-dropdown-item command="del" style="color: #dd242a"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<!-- 右侧操作图标 -->
|
||||
<img
|
||||
v-if="data.actionIcon"
|
||||
:src="data.actionIcon"
|
||||
class="actionIcon"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
<el-dialog title="添加版本号" :visible.sync="dialogVisible" width="30%">
|
||||
<el-form>
|
||||
<el-form-item label="版本号">
|
||||
<el-input v-model="demandData.name"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddNode">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { demandApi } from "@/utils/api";
|
||||
export default {
|
||||
name: "SidebarTree",
|
||||
props: {
|
||||
projectId: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedId: "",
|
||||
treeData: [],
|
||||
defaultProps: {
|
||||
children: "childrenList",
|
||||
label: "title",
|
||||
},
|
||||
dialogVisible: false,
|
||||
demandData: {
|
||||
name: "",
|
||||
},
|
||||
openFlag: false,
|
||||
showFlag: true,
|
||||
defaultExpend: [],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleNodeClick(data) {
|
||||
this.selectedId = data.nodeId;
|
||||
this.changeVersion(data);
|
||||
},
|
||||
getVersionTree(defaultId) {
|
||||
demandApi
|
||||
.getVersionTree({
|
||||
projectId: this.projectId,
|
||||
demandStatusList: [],
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.data.length) {
|
||||
return;
|
||||
}
|
||||
res.data = res.data.map((ele) => {
|
||||
ele.nodeId = ele.id + "_" + ele.type;
|
||||
ele.hover = false;
|
||||
return ele;
|
||||
});
|
||||
if (defaultId && defaultId != "all") {
|
||||
if (defaultId == -1) {
|
||||
this.defaultExpend = res.data[res.data.length - 1].nodeId;
|
||||
} else {
|
||||
this.defaultExpend = [defaultId];
|
||||
}
|
||||
this.selectedId = this.defaultExpend[0];
|
||||
this.changeVersion(res.data.find((ele) => ele.nodeId == defaultId));
|
||||
} else if (defaultId == "all") {
|
||||
} else {
|
||||
this.defaultExpend = [res.data[0].nodeId];
|
||||
this.selectedId = this.defaultExpend[0];
|
||||
this.setVersionList(res.data.filter((ele) => ele.type == 0));
|
||||
this.changeVersion(
|
||||
res.data.find((ele) => ele.nodeId == this.defaultExpend[0])
|
||||
);
|
||||
}
|
||||
this.treeData = res.data;
|
||||
});
|
||||
},
|
||||
changeVersion(data) {
|
||||
this.$emit("changeVersion", data);
|
||||
},
|
||||
setVersionList(data) {
|
||||
this.$emit("setVersionList", data);
|
||||
},
|
||||
confirmAddNode() {
|
||||
if (!this.demandData.name) {
|
||||
this.$message({
|
||||
message: "请填写版本号",
|
||||
type: "warning",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let param = {
|
||||
projectId: this.projectId,
|
||||
versionNumber: this.demandData.name,
|
||||
};
|
||||
if (this.demandData.id) {
|
||||
param.id = this.demandData.id;
|
||||
demandApi.editVersion(param).then((res) => {
|
||||
this.resetAdd(0);
|
||||
});
|
||||
} else {
|
||||
demandApi.addVersion(param).then((res) => {
|
||||
this.resetAdd(1);
|
||||
});
|
||||
}
|
||||
},
|
||||
resetAdd(add) {
|
||||
this.$message({
|
||||
message: add ? "添加成功" : "修改成功",
|
||||
type: "success",
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.demandData.name = "";
|
||||
this.demandData.id = "";
|
||||
this.getVersionTree();
|
||||
},
|
||||
editDemand(data) {
|
||||
this.demandData = {
|
||||
name: data.title,
|
||||
id: data.id,
|
||||
};
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
delVersion(data) {
|
||||
this.$confirm("此操作将永久删除该版本号, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
demandApi.delVersion(data.id).then((res) => {
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "删除成功!",
|
||||
});
|
||||
this.getVersionTree();
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
changeOpen() {
|
||||
this.openFlag = !this.openFlag;
|
||||
if (this.openFlag) {
|
||||
this.defaultExpend = this.treeData.map((ele) => ele.nodeId);
|
||||
} else {
|
||||
this.defaultExpend = [];
|
||||
}
|
||||
this.getVersionTree("all");
|
||||
},
|
||||
changeShow() {
|
||||
this.showFlag = !this.showFlag;
|
||||
},
|
||||
changeRow(type, row) {
|
||||
if (type == "add") {
|
||||
this.$emit("addDemand");
|
||||
} else if (type == "edit") {
|
||||
this.editDemand(row);
|
||||
} else if (type == "del") {
|
||||
this.delVersion(row);
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
projectId(newVal) {
|
||||
this.$nextTick(() => {
|
||||
this.getVersionTree();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sidebarTree {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
min-height: 100vh;
|
||||
background-color: inherit;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.hidenLeft {
|
||||
width: 40px;
|
||||
}
|
||||
.tree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.treeNode {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
padding: 0 20px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
::v-deep .el-tree-node__children {
|
||||
padding-left: 15px !important;
|
||||
}
|
||||
.expandIcon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.nodeIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.nodeLabel {
|
||||
flex: 1;
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.selected {
|
||||
color: #4096ff;
|
||||
}
|
||||
|
||||
.count {
|
||||
font-family: "PingFang SC";
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 26px;
|
||||
color: #999;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.selectedCount {
|
||||
color: #4096ff;
|
||||
}
|
||||
|
||||
.actionIcon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.treeTitle {
|
||||
padding: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
/* 选中状态背景色 */
|
||||
// .treeNode:has(.selected) {
|
||||
// background-color: #f6faff;
|
||||
// }
|
||||
|
||||
::v-deep .el-tree-node {
|
||||
min-height: 42px;
|
||||
.el-tree-node__content {
|
||||
height: 42px;
|
||||
}
|
||||
.el-tree-node__expand-icon{
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
.topText {
|
||||
gap: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
.topBtn {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
// /* <EFBFBD><EFBFBD>浮效果 */
|
||||
// .treeNode:hover {
|
||||
// background-color: #f5f5f5;
|
||||
// }
|
||||
</style>
|
|
@ -1,337 +1,87 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<!-- 左侧区域 -->
|
||||
<transition name="slide">
|
||||
<div class="left-panel" v-show="isTreeVisible">
|
||||
<!-- 操作栏 -->
|
||||
<div class="tree-controls">
|
||||
<span>根节点数量: {{ rootNodeCount }}</span>
|
||||
<div class="buttons">
|
||||
<el-button size="mini" @click="addNode">添加</el-button>
|
||||
<el-button size="mini" @click="collapseAll">收起所有节点</el-button>
|
||||
<el-button size="mini" @click="toggleTreeVisibility">
|
||||
{{ isTreeVisible ? "隐藏树形" : "显示树形" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 树形表格 -->
|
||||
<div class="tree-view">
|
||||
<el-table
|
||||
:data="treeData"
|
||||
row-key="id"
|
||||
border
|
||||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
|
||||
ref="treeTable"
|
||||
>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="需求名称"
|
||||
></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 右侧表格 -->
|
||||
<div
|
||||
class="right-panel"
|
||||
:style="{ flex: isTreeVisible ? '1' : '0 0 100%' }"
|
||||
>
|
||||
<el-button
|
||||
v-show="!isTreeVisible"
|
||||
size="mini"
|
||||
@click="toggleTreeVisibility"
|
||||
>
|
||||
显示树形
|
||||
</el-button>
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column
|
||||
prop="version"
|
||||
label="版本号"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
label="需求状态"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="owner"
|
||||
label="负责人"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="estimatedTime"
|
||||
label="预计工时"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="creationTime"
|
||||
label="创建时间"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="endTime"
|
||||
label="结束时间"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
prop="priority"
|
||||
label="优先级"
|
||||
|
||||
></el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" @click="handleEdit(scope.row)"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="danger"
|
||||
@click="handleDelete(scope.row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 添加节点弹框 -->
|
||||
<el-dialog title="添加节点" :visible.sync="dialogVisible">
|
||||
<el-form :model="newNode">
|
||||
<el-form-item label="节点名称">
|
||||
<el-input v-model="newNode.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-input v-model="newNode.status"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddNode">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<div class="layout">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<SidebarTree
|
||||
:projectId="projectId"
|
||||
class="sidebar"
|
||||
@changeVersion="changeVersion"
|
||||
@setVersionList="setVersionList"
|
||||
@addDemand="addDemand"
|
||||
ref="treeRef"
|
||||
/>
|
||||
<!-- 右侧表格和筛选 -->
|
||||
<MainContentTable
|
||||
:projectId="projectId"
|
||||
class="main-content"
|
||||
:version="version"
|
||||
:projectName="projectName"
|
||||
@refreshTree="refreshTree"
|
||||
:versionList="versionList"
|
||||
ref="rightRef"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { demandApi } from "@/utils/api";
|
||||
// 导入子组件
|
||||
import SidebarTree from "./components/SidebarTree.vue";
|
||||
import MainContentTable from "./components/MainContentTable.vue";
|
||||
|
||||
export default {
|
||||
name: "demandManage",
|
||||
components: {
|
||||
SidebarTree,
|
||||
MainContentTable,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 树形表格数据
|
||||
treeData: [
|
||||
{
|
||||
id: 1,
|
||||
name: "产品需求1.0",
|
||||
status: "进行中",
|
||||
children: [
|
||||
{
|
||||
id: 11,
|
||||
name: "用户模块",
|
||||
status: "已完成",
|
||||
children: [
|
||||
{
|
||||
id: 111,
|
||||
name: "登录功能",
|
||||
status: "已完成",
|
||||
},
|
||||
{
|
||||
id: 112,
|
||||
name: "注册功能",
|
||||
status: "已完成",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: "订单模块",
|
||||
status: "进行中",
|
||||
children: [
|
||||
{
|
||||
id: 121,
|
||||
name: "购物车",
|
||||
status: "进行中",
|
||||
},
|
||||
{
|
||||
id: 122,
|
||||
name: "支付功能",
|
||||
status: "待开始",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "产品需求2.0",
|
||||
status: "待开始",
|
||||
children: [
|
||||
{
|
||||
id: 21,
|
||||
name: "数据分析模块",
|
||||
status: "待开始",
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
name: "报表模块",
|
||||
status: "待开始",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
// 右侧表格数据
|
||||
tableData: [
|
||||
{
|
||||
version: "v1.0.0",
|
||||
status: "进行中",
|
||||
owner: "张三",
|
||||
estimatedTime: "40h",
|
||||
creationTime: "2024-01-01",
|
||||
endTime: "2024-01-15",
|
||||
priority: "高",
|
||||
},
|
||||
{
|
||||
version: "v1.0.1",
|
||||
status: "已完成",
|
||||
owner: "李四",
|
||||
estimatedTime: "20h",
|
||||
creationTime: "2024-01-16",
|
||||
endTime: "2024-01-25",
|
||||
priority: "中",
|
||||
},
|
||||
{
|
||||
version: "v1.1.0",
|
||||
status: "待开始",
|
||||
owner: "王五",
|
||||
estimatedTime: "60h",
|
||||
creationTime: "2024-02-01",
|
||||
endTime: "2024-02-28",
|
||||
priority: "高",
|
||||
},
|
||||
{
|
||||
version: "v1.2.0",
|
||||
status: "规划中",
|
||||
owner: "赵六",
|
||||
estimatedTime: "80h",
|
||||
creationTime: "2024-03-01",
|
||||
endTime: "2024-03-31",
|
||||
priority: "低",
|
||||
},
|
||||
],
|
||||
dialogVisible: false,
|
||||
newNode: {
|
||||
name: "",
|
||||
status: "",
|
||||
},
|
||||
isTreeVisible: true,
|
||||
projectId: "",
|
||||
projectName: "",
|
||||
versionList: [],
|
||||
version: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rootNodeCount() {
|
||||
return this.treeData.length;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isTreeVisible(newVal) {
|
||||
this.$refs.treeTable.doLayout();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
addNode() {
|
||||
this.dialogVisible = true;
|
||||
changeVersion(data) {
|
||||
this.version = data;
|
||||
},
|
||||
confirmAddNode() {
|
||||
if (this.newNode.name && this.newNode.status) {
|
||||
this.treeData.push({
|
||||
id: Date.now(),
|
||||
name: this.newNode.name,
|
||||
status: this.newNode.status,
|
||||
children: [],
|
||||
});
|
||||
this.dialogVisible = false;
|
||||
this.newNode.name = "";
|
||||
this.newNode.status = "";
|
||||
}
|
||||
setVersionList(data) {
|
||||
this.versionList = data;
|
||||
},
|
||||
collapseAll() {
|
||||
this.treeData.forEach((node) => {
|
||||
this.$refs.treeTable.toggleRowExpansion(node,false);
|
||||
});
|
||||
refreshTree(defaultId) {
|
||||
this.$refs.treeRef.getVersionTree(defaultId);
|
||||
},
|
||||
toggleTreeVisibility() {
|
||||
this.isTreeVisible = !this.isTreeVisible;
|
||||
addDemand() {
|
||||
console.log(566);
|
||||
|
||||
this.$refs.rightRef.handleAdd();
|
||||
},
|
||||
handleEdit(row) {
|
||||
console.log("编辑行:", row);
|
||||
},
|
||||
handleDelete(row) {
|
||||
console.log("删除行:", row);
|
||||
},
|
||||
getDemandTree(){
|
||||
demandApi.getDemandTree({
|
||||
projectId:this.projectId,
|
||||
demandStatusList:[]
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.projectId=this.$route.query.id
|
||||
this.getDemandTree()
|
||||
}
|
||||
mounted() {
|
||||
this.projectId = this.$route.query.id;
|
||||
this.projectName = this.$route.query.projectName;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
<style scoped lang="scss">
|
||||
.layout {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.left-panel {
|
||||
width: 20%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
.sidebar {
|
||||
flex-grow: 1;
|
||||
max-width: 300px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.tree-controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.tree-view {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
flex: 1;
|
||||
transition: width 0.3s ease;
|
||||
padding: 10px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.slide-enter-active,
|
||||
.slide-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-enter, .slide-leave-to /* .slide-leave-active in <2.1.8 */ {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
.main-content {
|
||||
flex-grow: 3;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -204,7 +204,7 @@ export default {
|
|||
handleDemand(row){
|
||||
this.$router.push({
|
||||
path: "/demandManage",
|
||||
query: { id: row.projectId },
|
||||
query: { id: row.projectId,projectName:row.projectName },
|
||||
});
|
||||
|
||||
},
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<div class="leftBox">
|
||||
<div class="topBox">
|
||||
<el-date-picker
|
||||
v-model="selectedDate"
|
||||
type="month"
|
||||
format="yyyy年MM月"
|
||||
:clearable="false"
|
||||
style="margin-bottom: 10px; width: 150px"
|
||||
@change="changeMonth"
|
||||
prefix-icon="false"
|
||||
/>
|
||||
<div>
|
||||
<i
|
||||
class="el-icon-arrow-left"
|
||||
style="font-size: 18px; font-weight: bold"
|
||||
@click="preMonth"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-arrow-right"
|
||||
style="font-size: 18px; font-weight: bold"
|
||||
@click="nextMonth"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-calendar v-model="selectedDate" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { workLogApi, projectApi } from "@/utils/api";
|
||||
|
||||
export default {
|
||||
name: "LeftMonth",
|
||||
data() {
|
||||
return {
|
||||
selectedDate: this.moment(new Date()).format("YYYY-MM-DD"),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeMonth() {},
|
||||
preMonth() {
|
||||
this.selectedDate = this.moment(this.selectedDate)
|
||||
.subtract(1, "months")
|
||||
.format("YYYY-MM-DD");
|
||||
},
|
||||
nextMonth() {
|
||||
this.selectedDate = this.moment(this.selectedDate)
|
||||
.add(1, "months")
|
||||
.format("YYYY-MM-DD");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.leftBox ::v-deep .el-calendar__header {
|
||||
display: none;
|
||||
}
|
||||
.leftBox ::v-deep .el-date-editor {
|
||||
.el-input__inner {
|
||||
border: none;
|
||||
padding-left: 0;
|
||||
font-size: 18px;
|
||||
color: #333333;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.leftBox ::v-deep .el-calendar__body {
|
||||
padding: 0 !important;
|
||||
thead {
|
||||
th {
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
font-size: 16px;
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
td {
|
||||
border: none;
|
||||
padding: 10px 0;
|
||||
|
||||
.el-calendar-day {
|
||||
text-align: center;
|
||||
padding: 0 !important;
|
||||
font-size: 16px;
|
||||
span {
|
||||
font-family: cursive !important;
|
||||
}
|
||||
}
|
||||
&.next,
|
||||
&.prev {
|
||||
color: #333 !important;
|
||||
}
|
||||
&.is-selected .el-calendar-day {
|
||||
border-radius: 50% !important;
|
||||
background-color: #4096ff;
|
||||
color: #fff;
|
||||
}
|
||||
.el-calendar-day {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
line-height: 40px;
|
||||
|
||||
border-radius: 50% !important;
|
||||
&:hover {
|
||||
background-color: #88bdfd;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
i {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,237 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="topBox">
|
||||
<div>
|
||||
<div class="topTitle">当日日志</div>
|
||||
<div class="topText">总计填报工时:<span>1</span>天</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon class="el-icon-plus" />
|
||||
添加日志
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
class="tableBox"
|
||||
>
|
||||
<el-table-column label="序号" width="70" prop="id" />
|
||||
<el-table-column label="项目名称" prop="versionNumber">
|
||||
<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="title">
|
||||
<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="项目需求">
|
||||
<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="操作" 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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "RightTable",
|
||||
data() {
|
||||
return {
|
||||
statusList: [],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 20px;
|
||||
background-color: #ffffff;
|
||||
min-width: 1200px;
|
||||
}
|
||||
.topBox {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
.topTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.topText {
|
||||
color: #999999;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
span {
|
||||
margin: 0 5px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div class="layout">
|
||||
<!-- 左侧树形菜单 -->
|
||||
<LeftMonth class="sidebar" ref="leftRef" />
|
||||
<!-- 右侧表格和筛选 -->
|
||||
<RightTable class="main-content" ref="rightRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 导入子组件
|
||||
import LeftMonth from "./components/leftMonth.vue";
|
||||
import RightTable from "./components/rightTable.vue";
|
||||
|
||||
export default {
|
||||
name: "worklog",
|
||||
components: {
|
||||
LeftMonth,
|
||||
RightTable,
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
mounted() {},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
flex-grow: 1;
|
||||
max-width: 360px;
|
||||
width: 360px;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex-grow: 3;
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue