A 2022-07-08 13:36:56 +08:00
commit 7a6c1c455c
7 changed files with 725 additions and 35 deletions

2
src/assets/icons/add.svg Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1657096310370" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6893" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M512 958.016c-119.648 0-232.128-46.368-316.736-130.56C110.624 743.2 64 631.2 64 512c0-119.168 46.624-231.2 131.232-315.424 84.608-84.192 197.088-130.56 316.736-130.56s232.128 46.368 316.704 130.56c84.672 84.224 131.264 196.256 131.264 315.392 0.032 119.2-46.592 231.232-131.264 315.456C744.128 911.616 631.648 958.016 512 958.016zM512 129.984c-102.624 0-199.072 39.744-271.584 111.936C167.936 314.048 128 409.984 128 512c0 102.016 39.904 197.952 112.384 270.048 72.512 72.192 168.96 111.936 271.584 111.936 102.592 0 199.072-39.744 271.584-111.936 72.48-72.16 112.416-168.064 112.384-270.08 0-102.016-39.904-197.92-112.384-270.016C711.072 169.76 614.592 129.984 512 129.984z" p-id="6894" fill="#409EFF"></path><path d="M736 480l-192 0L544 288c0-17.664-14.336-32-32-32s-32 14.336-32 32l0 192L288 480c-17.664 0-32 14.336-32 32s14.336 32 32 32l192 0 0 192c0 17.696 14.336 32 32 32s32-14.304 32-32l0-192 192 0c17.696 0 32-14.336 32-32S753.696 480 736 480z" p-id="6895" fill="#409EFF"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1657203382764" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4182" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M607.897867 768.043004c-17.717453 0-31.994625-14.277171-31.994625-31.994625L575.903242 383.935495c0-17.717453 14.277171-31.994625 31.994625-31.994625s31.994625 14.277171 31.994625 31.994625l0 351.94087C639.892491 753.593818 625.61532 768.043004 607.897867 768.043004z" p-id="4183" fill="#F56C6C"></path><path d="M415.930119 768.043004c-17.717453 0-31.994625-14.277171-31.994625-31.994625L383.935495 383.935495c0-17.717453 14.277171-31.994625 31.994625-31.994625 17.717453 0 31.994625 14.277171 31.994625 31.994625l0 351.94087C447.924744 753.593818 433.647573 768.043004 415.930119 768.043004z" p-id="4184" fill="#F56C6C"></path><path d="M928.016126 223.962372l-159.973123 0L768.043004 159.973123c0-52.980346-42.659499-95.983874-95.295817-95.983874L351.94087 63.989249c-52.980346 0-95.983874 43.003528-95.983874 95.983874l0 63.989249-159.973123 0c-17.717453 0-31.994625 14.277171-31.994625 31.994625s14.277171 31.994625 31.994625 31.994625l832.032253 0c17.717453 0 31.994625-14.277171 31.994625-31.994625S945.73358 223.962372 928.016126 223.962372zM319.946246 159.973123c0-17.545439 14.449185-31.994625 31.994625-31.994625l320.806316 0c17.545439 0 31.306568 14.105157 31.306568 31.994625l0 63.989249L319.946246 223.962372 319.946246 159.973123 319.946246 159.973123z" p-id="4185" fill="#F56C6C"></path><path d="M736.048379 960.010751 288.123635 960.010751c-52.980346 0-95.983874-43.003528-95.983874-95.983874L192.139761 383.591466c0-17.717453 14.277171-31.994625 31.994625-31.994625s31.994625 14.277171 31.994625 31.994625l0 480.435411c0 17.717453 14.449185 31.994625 31.994625 31.994625l448.096758 0c17.717453 0 31.994625-14.277171 31.994625-31.994625L768.215018 384.795565c0-17.717453 14.277171-31.994625 31.994625-31.994625s31.994625 14.277171 31.994625 31.994625l0 479.231312C832.032253 916.835209 789.028725 960.010751 736.048379 960.010751z" p-id="4186" fill="#F56C6C"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1657095084738" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5826" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><defs><style type="text/css">@font-face { font-family: feedback-iconfont; src: url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff2?t=1630033759944") format("woff2"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.woff?t=1630033759944") format("woff"), url("//at.alicdn.com/t/font_1031158_u69w8yhxdu.ttf?t=1630033759944") format("truetype"); }
</style></defs><path d="M853.333333 501.333333c-17.066667 0-32 14.933333-32 32v320c0 6.4-4.266667 10.666667-10.666666 10.666667H170.666667c-6.4 0-10.666667-4.266667-10.666667-10.666667V213.333333c0-6.4 4.266667-10.666667 10.666667-10.666666h320c17.066667 0 32-14.933333 32-32s-14.933333-32-32-32H170.666667c-40.533333 0-74.666667 34.133333-74.666667 74.666666v640c0 40.533333 34.133333 74.666667 74.666667 74.666667h640c40.533333 0 74.666667-34.133333 74.666666-74.666667V533.333333c0-17.066667-14.933333-32-32-32z" p-id="5827" fill="#E6A23C"></path><path d="M405.333333 484.266667l-32 125.866666c-2.133333 10.666667 0 23.466667 8.533334 29.866667 6.4 6.4 14.933333 8.533333 23.466666 8.533333h8.533334l125.866666-32c6.4-2.133333 10.666667-4.266667 14.933334-8.533333l300.8-300.8c38.4-38.4 38.4-102.4 0-140.8-38.4-38.4-102.4-38.4-140.8 0L413.866667 469.333333c-4.266667 4.266667-6.4 8.533333-8.533334 14.933334z m59.733334 23.466666L761.6 213.333333c12.8-12.8 36.266667-12.8 49.066667 0 12.8 12.8 12.8 36.266667 0 49.066667L516.266667 558.933333l-66.133334 17.066667 14.933334-68.266667z" p-id="5828" fill="#E6A23C"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -372,7 +372,7 @@ export default {
method: 'post', method: 'post',
data: form data: form
}).then(response => { }).then(response => {
console.log(response) //console.log(response)
if (response.data.code === 200) { if (response.data.code === 200) {
//console.log(response.data.data.records) //console.log(response.data.data.records)
//that.dialogFormVisible = false //that.dialogFormVisible = false

View File

@ -23,8 +23,8 @@
<el-table-column label="项目进度" min-width="35%"> <el-table-column label="项目进度" min-width="35%">
<template #default="scope"> <template #default="scope">
<el-progress :text-inside="true" :stroke-width="18" <el-progress :text-inside="true" :stroke-width="18"
:status="scope.row.completeNum===scope.row.totalNum ? 'success' : ''" :status="(scope.row.totalNum!==0&&scope.row.completeNum===scope.row.totalNum) ? 'success' : ''"
:percentage="scope.row.completeNum*100/scope.row.totalNum"></el-progress> :percentage="scope.row.totalNum===0?0:scope.row.completeNum*100/scope.row.totalNum"></el-progress>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column min-width="10%" align="right"> <el-table-column min-width="10%" align="right">

View File

@ -49,7 +49,7 @@
this.$store.state.staff === null ? this.$store.state.staff : this.$store.state.staff.staffFullname this.$store.state.staff === null ? this.$store.state.staff : this.$store.state.staff.staffFullname
}}</span> }}</span>
<span style="color: #606266; font-family: 'Segoe UI',sans-serif;font-size: 12px;">{{ <span style="color: #606266; font-family: 'Segoe UI',sans-serif;font-size: 12px;">{{
staffJob projectStaffPosition
}}</span> }}</span>
</div> </div>
@ -57,7 +57,10 @@
</div> </div>
</el-header> </el-header>
<el-main style="overflow: unset"> <el-main style="overflow: unset">
<router-view></router-view> <router-view
:projectAccessLevel="projectAccessLevel"
:projectGroup="projectGroup"
></router-view>
</el-main> </el-main>
</el-container> </el-container>
</el-container> </el-container>
@ -78,7 +81,9 @@ export default {
project: { project: {
projectName: '' projectName: ''
}, },
staffJob: '项目经理', projectGroup: [],
projectStaffPosition: '',
projectAccessLevel: 3,
menuDefaultActive: '1', menuDefaultActive: '1',
} }
}, },
@ -95,8 +100,20 @@ export default {
this.menuDefaultActive = '1'; this.menuDefaultActive = '1';
break break
} }
const that = this
request({
url: 'project/' + this.$route.params.projectId+'/group/'+this.$store.state.staff.staffId,
method: 'get',
}).then(response => {
if (response.data.code === 200) {
that.projectStaffPosition = response.data.data.projectStaffPosition
that.projectAccessLevel = response.data.data.projectAccessLevel
}
}).catch(function (error) {
console.log(error)
})
this.getProjectInfo(); this.getProjectInfo();
this.getProjectGroup()
}, },
methods: { methods: {
getProjectInfo() { getProjectInfo() {
@ -113,6 +130,20 @@ export default {
console.log(error) console.log(error)
}) })
}, },
getProjectGroup() {
const that = this
request({
url: 'project/' + this.$route.params.projectId+'/group',
method: 'get'
}).then(response => {
if (response.data.code === 200) {
that.projectGroup = response.data.data.records
}
}).catch(function (error) {
console.log(error)
})
},
} }
} }
</script> </script>

View File

@ -3,7 +3,59 @@
<div style="margin: 0 40px 0 40px; <div style="margin: 0 40px 0 40px;
display: flex;flex-direction: row;justify-content: space-between"> display: flex;flex-direction: row;justify-content: space-between">
<p style="font-family: 'Segoe UI',sans-serif;font-size: 20px;font-weight: bold;color: #606266">工作项</p> <!-- <p style="font-family: 'Segoe UI',sans-serif;font-size: 20px;font-weight: bold;color: #606266">工作项</p>-->
<el-dropdown>
<span style="font-family: 'Segoe UI',sans-serif;font-size: 20px;font-weight: bold;color: #606266">
工作项
<el-icon class="el-icon--right">
<arrow-down/>
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>所有工作项</el-dropdown-item>
<el-dropdown-item>我的工作项</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-popover
placement="bottom-end"
:width="100"
trigger="click"
>
<div style="display: flex;flex-direction: column">
<el-button
style="padding: 4px;margin-left: 5px" text @click.native="onAddClick({taskId: 0}, '需求')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 24px;height: 24px;"
icon-class="demand"></svg-icon>
</div>
<span style="margin-left: 5px">需求</span>
</el-button>
<el-button
style="padding: 4px;margin-left: 5px" text @click.native="onAddClick({taskId: 0}, '任务')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 20px;height: 20px;"
icon-class="assignment"></svg-icon>
</div>
<span style="margin-left: 5px">任务</span>
</el-button>
<el-button
style="padding: 4px;margin-left: 5px" text @click.native="onAddClick({taskId: 0}, '缺陷')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 20px;height: 20px;"
icon-class="defect"></svg-icon>
</div>
<span style="margin-left: 5px">缺陷</span>
</el-button>
</div>
<template #reference>
<el-button :style="{'visibility': ( projectAccessLevel < 3)?'unset':'hidden'}" type="primary">新增工作项
</el-button>
</template>
</el-popover>
</div> </div>
<div style="flex: 1; margin: 30px 30px 10px 30px; background-color: white; border-radius: 10px;padding: 20px; <div style="flex: 1; margin: 30px 30px 10px 30px; background-color: white; border-radius: 10px;padding: 20px;
display: flex;flex-direction: column;justify-content: space-between"> display: flex;flex-direction: column;justify-content: space-between">
@ -12,10 +64,10 @@
ref="tableRef" ref="tableRef"
class="projectTable" class="projectTable"
row-key="taskId" row-key="taskId"
@row-click="onRowClick"
lazy lazy
:load="loadChildren" :load="loadChildren"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
@row-click="onRowClick"
:height="tableHeight" :height="tableHeight"
:data="workitems"> :data="workitems">
<el-table-column prop="taskName" label="&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp标题" <el-table-column prop="taskName" label="&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp标题"
@ -23,7 +75,7 @@
<template #default="scope"> <template #default="scope">
<div style="display: inline-block;"> <div style="display: inline-block;">
<div style="display: flex;align-items: center"> <div style="display: flex;align-items: center">
<div v-if="!scope.row.hasChildren" style="width: 20px"></div> <div v-if="!scope.row.hasChildren&&scope.row.taskFatherId===0" style="width: 20px"></div>
<div style="width: 24px;display: flex;justify-content: center"> <div style="width: 24px;display: flex;justify-content: center">
<svg-icon v-if="scope.row.taskType===''" style="width: 20px;height: 20px;" <svg-icon v-if="scope.row.taskType===''" style="width: 20px;height: 20px;"
icon-class="assignment"></svg-icon> icon-class="assignment"></svg-icon>
@ -38,59 +90,609 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="taskId" label="编号" min-width="10%"/> <el-table-column prop="taskId" label="编号" min-width="7%"/>
<el-table-column prop="taskStatus" label="状态" min-width="10%"> <el-table-column prop="taskStatus" label="状态" min-width="10%">
<template #default="scope"> <template #default="scope">
<el-select v-model="scope.row.taskStatus" class="m-2" placeholder="Select" size="large"> <el-select
<el-option :disabled="statusDisabled(scope.row)"
label="待进行" v-model="scope.row.taskStatus" placeholder="Select" @change="onTaskStatusChange(scope.row)">
value="待进行" <el-option value="待进行" :disabled="scope.row.taskStatus!=='待进行'"/>
/> <el-option value="进行中" :disabled="scope.row.taskStatus==='已完成'||scope.row.taskStatus==='关闭'"/>
<el-option <el-option value="已完成" :disabled="scope.row.taskStatus==='已完成'||scope.row.taskStatus==='关闭'"/>
label="进行中" <el-option value="关闭" :disabled="scope.row.taskStatus==='已完成'||scope.row.taskStatus==='关闭'"/>
value="进行中"
/>
<el-option
label="已完成"
value="已完成"
/>
<el-option
label="关闭"
value="关闭"
/>
</el-select> </el-select>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="taskHolderId" label="负责人" min-width="10%"/> <el-table-column prop="taskHolderName" label="负责人" min-width="8%"/>
<el-table-column prop="taskCreatedTime" label="创建时间" min-width="10%" :formatter="dateFormatter"/> <el-table-column prop="taskStartTime" label="开始时间" min-width="10%" :formatter="dateFormatter"/>
<el-table-column prop="taskEndTime" label="截止时间" min-width="10%" :formatter="dateFormatter"/> <el-table-column prop="taskEndTime" label="截止时间" min-width="10%" :formatter="dateFormatter"/>
<el-table-column prop="taskPriority" label="优先级" min-width="10%"/> <el-table-column prop="taskPriority" label="优先级" min-width="5%" :formatter="priorityFormatter"/>
<el-table-column prop="operations" min-width="10%">
<template #default="scope">
<div style="width: 100%;display: flex;justify-content: flex-end">
<el-button v-show="scope.row.taskStatus!=='已完成'&&scope.row.taskStatus!=='关闭'&&scope.row.editable" style="padding: 4px" text @click.native="onEditClick(scope.row)">
<svg-icon style="width: 20px;height: 20px;" icon-class="edit"></svg-icon>
</el-button>
<el-popover
placement="bottom-end"
:width="100"
trigger="click"
>
<div style="display: flex;flex-direction: column">
<el-button
v-if="scope.row.taskType==='需求'"
style="padding: 4px;margin-left: 5px" text @click.native="onAddClick(scope.row, '需求')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 24px;height: 24px;"
icon-class="demand"></svg-icon>
</div>
<span style="margin-left: 5px">需求</span>
</el-button>
<el-button
v-if="scope.row.taskType==='需求'||scope.row.taskType==='任务'"
style="padding: 4px;margin-left: 5px" text
@click.native="onAddClick(scope.row, '任务')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 20px;height: 20px;"
icon-class="assignment"></svg-icon>
</div>
<span style="margin-left: 5px">任务</span>
</el-button>
<el-button
v-if="scope.row.taskType==='需求'"
style="padding: 4px;margin-left: 5px" text @click.native="onAddClick(scope.row, '缺陷')">
<div style="width: 24px;display: flex;justify-content: center">
<svg-icon style="width: 20px;height: 20px;"
icon-class="defect"></svg-icon>
</div>
<span style="margin-left: 5px">缺陷</span>
</el-button>
</div>
<template #reference>
<el-button
:style="{'padding':'4px','margin-left': '5px','visibility':
(scope.row.taskType!=='缺陷'&&
!statusDisabled(scope.row))?
'unset':'hidden'}"
text>
<svg-icon style="width: 20px;height: 20px;" icon-class="add"></svg-icon>
</el-button>
</template>
</el-popover>
<el-button
:style="{'padding':'4px','margin-left': '5px','visibility':scope.row.editable?'unset':'hidden'}"
text @click.native="onDeleteClick(scope.row)">
<svg-icon style="width: 20px;height: 20px;" icon-class="delete"></svg-icon>
</el-button>
</div>
</template>
</el-table-column>
</el-table> </el-table>
</div> </div>
</div> </div>
<el-dialog
v-model="dialogVisible"
:title="form.title"
width="40%"
top="60px"
>
<el-form ref="formRef" :model="form" label-position="top" label-width="120px" :rules="rules">
<el-row :gutter="20">
<el-col :span="form.operation==='add'?24:12">
<el-form-item label="标题" prop="taskName">
<el-input v-model="form.taskName" :disabled="form.disabled"/>
</el-form-item>
</el-col>
<el-col :span="12" v-show="form.operation!=='add'" :disabled="form.disabled">
<el-form-item label="状态">
<el-select style="width: 100%" v-model="form.taskStatus" :disabled="form.statusDisabled">
<el-option value="待进行" :disabled="form.taskStatus!=='待进行'"/>
<el-option value="进行中" :disabled="form.taskStatus==='已完成'||form.taskStatus==='关闭'"/>
<el-option value="已完成" :disabled="form.taskStatus==='已完成'||form.taskStatus==='关闭'"/>
<el-option value="关闭" :disabled="form.taskStatus==='已完成'||form.taskStatus==='关闭'"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="负责人" prop="taskHolderId">
<el-select style="width: 100%" v-model="form.taskHolderId" filterable :disabled="form.disabled">
<el-option
v-for="member in projectGroup"
:key="member.staffId"
:label="member.staffFullname"
:value="member.staffId"
>
<span style="float: left">{{ member.staffFullname }}</span>
<span
style="float: right;color: var(--el-text-color-secondary);font-size: 13px;"
>{{ member.staffUsername }}</span
>
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="优先级">
<el-select style="width: 100%" v-model="form.taskPriority" :disabled="form.disabled">
<el-option label="最高" :value="1"/>
<el-option label="较高" :value="2"/>
<el-option label="普通" :value="3"/>
<el-option label="较低" :value="4"/>
<el-option label="最低" :value="5"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开始时间" prop="taskStartTime">
<el-date-picker
v-model="form.taskStartTime"
type="datetime"
placeholder="选择时间"
:default-time="defaultTime"
style="width: 100%;"
:disabled="form.disabled"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="截止时间" prop="taskEndTime">
<el-date-picker
type="datetime"
placeholder="选择时间"
v-model="form.taskEndTime"
:default-time="defaultTime"
style="width: 100%;"
:disabled="form.disabled"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item v-if="form.taskType==='任务'" label="预估工时">
<el-input-number
:disabled="form.disabled"
style="width: 100%" v-model="form.estimatedManHours" :min="1" controls-position="right"/>
</el-form-item>
<el-form-item v-if="form.taskType==='需求'" label="需求来源">
<el-select style="width: 100%" v-model="form.demandSource" :disabled="form.disabled">
<el-option value="产品规划"/>
<el-option value="用户反馈"/>
<el-option value="内部需求"/>
<el-option value="竞品调研"/>
<el-option value="其他"/>
</el-select>
</el-form-item>
<el-form-item v-if="form.taskType==='缺陷'" label="严重程度">
<el-select style="width: 100%" v-model="form.severity" :disabled="form.disabled">
<el-option value="致命"/>
<el-option value="严重"/>
<el-option value="一般"/>
<el-option value="建议"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item v-if="form.taskType==='缺陷'" label="复现概率">
<el-select style="width: 100%" v-model="form.recurrenceProbability" :disabled="form.disabled">
<el-option value="必现"/>
<el-option value="大概率复现"/>
<el-option value="小概率复现"/>
<el-option value="仅出现一次"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label="描述">
<el-input type="textarea" v-model="form.taskDescription" :disabled="form.disabled"/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
:disabled="form.statusDisabled"
type="primary" @click="onSubmit('formRef')"
>确定</el-button
>
</span>
</template>
</el-dialog>
</template> </template>
<script setup> <script setup>
import {ArrowDown} from '@element-plus/icons'
// table
import {onMounted, ref} from "vue";
const tableRef = ref(null);
// table
const tableHeight = ref();
const formRef = ref(null);
onMounted(() => {
// innerHeight-offsetTop-
tableHeight.value = window.innerHeight - tableRef.value.$el.offsetTop - 70;
window.onresize = () => {
tableHeight.value = window.innerHeight - tableRef.value.$el.offsetTop - 70;
};
});
</script> </script>
<script> <script>
import request from "../utils/request"; import request from "../utils/request";
import moment from "moment"; import {ElMessage} from "element-plus";
import router from "../router";
export default { export default {
name: "ProjectWorkitem", name: "ProjectWorkitem",
props: {
projectAccessLevel: Number,
projectGroup: []
},
data() { data() {
return { return {
dialogVisible: false,
form: {},
rules: {
taskName: [
{required: true, message: '请输入标题', trigger: 'blur'}
],
taskHolderId: [
{required: true, message: '请指定负责人', trigger: 'blur'}
],
taskStartTime: [
{required: true, message: '请设置开始时间', trigger: 'blur'},
],
taskEndTime: [
{required: true, message: '请设置截止时间', trigger: 'blur'},
{
validator: (rule, value, callback) => {
if (!this.form.taskStartTime){
return callback();
}
if (this.form.taskEndTime.getTime() > this.form.taskStartTime.getTime())
return callback();
else
return callback(new Error("截止时间须在开始时间之后"));
}, trigger: 'change'
},
]
},
workitems: [],
defaultTime: new Date(1970, 1, 1, 12, 0, 0),
maps: new Map(),
}
},
watch: {
projectAccessLevel: function (val) {
this.getWorkitems() //
},
projectGroup: function (val) {
workitems: []
} }
}, },
created() { created() {
this.getWorkitems() if (this.projectAccessLevel)
this.getWorkitems()
}, },
methods: { methods: {
statusDisabled(row) {
return row.taskStatus==='已完成'||row.taskStatus==='关闭'||!(row.editable ||
(this.$store.state.staff && row.taskHolderId === this.$store.state.staff.staffId))
},
onRowClick(row, column, event) {
if (column.property === "operations")
return
this.form = {
disabled: row.taskStatus==='已完成'||row.taskStatus==='关闭'||!row.editable,
statusDisabled: this.statusDisabled(row),
title: '修改' + row.taskType,
taskId: row.taskId,
taskType: row.taskType,
taskFatherId: row.taskFatherId,
taskName: row.taskName,
taskStatus: row.taskStatus,
taskHolderId: row.taskHolderId,
taskPriority: row.taskPriority,
taskCreatedTime: row.taskCreatedTime,
taskClosedTime: row.taskClosedTime,
taskStartTime: new Date(row.taskStartTime * 1000),
taskEndTime: new Date(row.taskEndTime * 1000),
taskDescription: row.taskDescription
}
if (row.attachedInfo)
switch (this.form.taskType) {
case '需求':
if (row.attachedInfo.demandSource)
this.form['demandSource'] = row.attachedInfo.demandSource
break;
case '任务':
if (row.attachedInfo.estimatedManHours)
this.form['estimatedManHours'] = row.attachedInfo.estimatedManHours
break;
case '缺陷':
if (row.attachedInfo.severity)
this.form['severity'] = row.attachedInfo.severity
if (row.attachedInfo.recurrenceProbability)
this.form['recurrenceProbability'] = row.attachedInfo.recurrenceProbability
break;
}
this.dialogVisible = true
},
onTaskStatusChange(row) {
let submitForm = {
taskType: row.taskType,
taskFatherId: row.taskFatherId,
taskName: row.taskName,
taskHolderId: row.taskHolderId,
taskPriority: row.taskPriority,
taskStartTime: row.taskStartTime,
taskEndTime: row.taskEndTime,
taskDescription: row.taskDescription,
taskId: row.taskId,
taskStatus: row.taskStatus,
taskCreatedTime: row.taskCreatedTime,
taskClosedTime: row.taskClosedTime
}
console.log(submitForm)
const that = this
request({
url: 'project/' + this.$route.params.projectId + '/task/' + submitForm.taskId,
method: 'put',
data: submitForm
}).then(response => {
console.log(response)
if (response.data.code === 200) {
if (submitForm.taskFatherId === 0) {
that.getWorkitems()
} else {
let rtr = that.maps.get(submitForm.taskFatherId);
if (rtr) {
rtr.treeNode.loading = true
that.loadChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
let rtr = that.maps.get(submitForm.taskId);
if (rtr) {
rtr.treeNode.loading = true
that.refreshChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
ElMessage({
message: '修改成功',
type: 'success',
})
that.dialogVisible = false
}
else
Promise.reject(response)
}).catch(function (error) {
console.log(error)
ElMessage({
message: '修改失败',
type: 'error',
})
if (submitForm.taskFatherId === 0) {
that.getWorkitems()
} else {
let rtr = that.maps.get(submitForm.taskFatherId);
if (rtr) {
rtr.treeNode.loading = true
that.loadChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
})
},
onSubmit() {
if( !this.$refs['formRef'])
{
console.log('formRef error')
return
}
this.$refs['formRef'].validate((valid) => {
console.log(valid)
if(valid)
{
let submitForm = {
taskType: this.form.taskType,
taskFatherId: this.form.taskFatherId,
taskName: this.form.taskName,
taskHolderId: parseInt(this.form.taskHolderId),
taskPriority: this.form.taskPriority,
taskStartTime: this.form.taskStartTime.getTime() / 1000,
taskEndTime: this.form.taskEndTime.getTime() / 1000,
taskDescription: this.form.taskDescription
}
switch (this.form.taskType) {
case '需求':
submitForm['attachedInfo'] = {
demandSource: this.form.demandSource
}
break;
case '任务':
submitForm['attachedInfo'] = {
estimatedManHours: this.form.estimatedManHours
}
break;
case '缺陷':
submitForm['attachedInfo'] = {
severity: this.form.severity,
recurrenceProbability: this.form.recurrenceProbability
}
break;
}
console.log(submitForm)
const that = this
if (this.form.operation === 'add') {
request({
url: 'project/' + this.$route.params.projectId + '/task',
method: 'post',
data: submitForm
}).then(response => {
//console.log(response)
if (response.data.code === 200) {
if (submitForm.taskFatherId === 0) {
that.getWorkitems()
} else {
this.form.row.hasChildren = true
let rtr = that.maps.get(submitForm.taskFatherId);
console.log(rtr)
if (rtr) {
rtr.row.hasChildren = true
rtr.treeNode.loading = true
that.loadChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
ElMessage({
message: '新增成功',
type: 'success',
})
that.dialogVisible = false
}
}).catch(function (error) {
console.log(error)
})
} else {
submitForm['taskId'] = this.form.taskId
submitForm['taskStatus'] = this.form.taskStatus
submitForm['taskCreatedTime'] = this.form.taskCreatedTime
submitForm['taskClosedTime'] = this.form.taskClosedTime
request({
url: 'project/' + this.$route.params.projectId + '/task/' + this.form.taskId,
method: 'put',
data: submitForm
}).then(response => {
console.log(response)
if (response.data.code === 200) {
if (submitForm.taskFatherId === 0) {
that.getWorkitems()
} else {
let rtr = that.maps.get(submitForm.taskFatherId);
if (rtr) {
rtr.treeNode.loading = true
that.loadChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
ElMessage({
message: '修改成功',
type: 'success',
})
that.dialogVisible = false
}
}).catch(function (error) {
console.log(error)
})
}
}
})
},
onEditClick(row) {
this.form = {
title: '修改' + row.taskType,
taskId: row.taskId,
taskType: row.taskType,
taskFatherId: row.taskFatherId,
taskName: row.taskName,
taskStatus: row.taskStatus,
taskHolderId: row.taskHolderId,
taskPriority: row.taskPriority,
taskCreatedTime: row.taskCreatedTime,
taskClosedTime: row.taskClosedTime,
taskStartTime: new Date(row.taskStartTime * 1000),
taskEndTime: new Date(row.taskEndTime * 1000),
taskDescription: row.taskDescription
}
if (row.attachedInfo)
switch (this.form.taskType) {
case '需求':
if (row.attachedInfo.demandSource)
this.form['demandSource'] = row.attachedInfo.demandSource
break;
case '任务':
if (row.attachedInfo.estimatedManHours)
this.form['estimatedManHours'] = row.attachedInfo.estimatedManHours
break;
case '缺陷':
if (row.attachedInfo.severity)
this.form['severity'] = row.attachedInfo.severity
if (row.attachedInfo.recurrenceProbability)
this.form['recurrenceProbability'] = row.attachedInfo.recurrenceProbability
break;
}
this.dialogVisible = true
},
onAddClick(row, taskType) {
this.form = {
row: row,
operation: 'add',
title: '新增' + taskType,
taskType: taskType,
taskFatherId: row.taskId
}
this.dialogVisible = true
},
onDeleteClick(row) {
const that = this
request({
url: 'project/' + this.$route.params.projectId + '/task/' + row.taskId,
method: 'delete',
}).then(response => {
console.log(response)
if (response.data.code === 200) {
if (row.taskFatherId === 0) {
that.getWorkitems()
} else {
//console.log(that.$refs.tableRef.store.states.lazyTreeNodeMap)
if (that.$refs.tableRef.store.states.lazyTreeNodeMap.value[row.taskFatherId].length === 1) {
that.$refs.tableRef.store.states.lazyTreeNodeMap.value[row.taskFatherId] = []
}
let rtr = that.maps.get(row.taskFatherId);
//console.log(rtr)
if (rtr) {
rtr.treeNode.loading = true
that.loadChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
ElMessage({
message: '删除成功',
type: 'success',
})
that.dialogVisible = false
}
}).catch(function (error) {
console.log(error)
})
},
getWorkitems() { getWorkitems() {
const that = this; const that = this;
request({ request({
@ -100,6 +702,11 @@ export default {
if (response.data.code === 200) { if (response.data.code === 200) {
//console.log(response.data.records) //console.log(response.data.records)
that.workitems = response.data.data.records that.workitems = response.data.data.records
for (let workitem of that.workitems) {
workitem['editable'] = that.projectAccessLevel < 3
//console.log(workitem)
}
//console.log(that.workitems)
} }
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
@ -107,12 +714,54 @@ export default {
}, },
loadChildren(row, treeNode, resolve) { loadChildren(row, treeNode, resolve) {
const that = this; const that = this;
this.maps.set(row.taskId, {row, treeNode, resolve})
request({ request({
url: 'project/' + this.$route.params.projectId + '/task/' + row.taskId + '/subtask', url: 'project/' + this.$route.params.projectId + '/task/' + row.taskId + '/subtask',
method: 'get', method: 'get',
}).then(response => { }).then(response => {
if (response.data.code === 200) { if (response.data.code === 200) {
resolve(response.data.data.records) let records = response.data.data.records
const editable = row.editable || row.taskHolderId === this.$store.state.staff.staffId
for (let workitem of records) {
workitem['editable'] = editable
//console.log(workitem)
}
resolve(records)
}
}).catch(function (error) {
console.log(error)
})
},
refreshChildren(row, treeNode, resolve) {
const that = this;
this.maps.set(row.taskId, {row, treeNode, resolve})
request({
url: 'project/' + this.$route.params.projectId + '/task/' + row.taskId + '/subtask',
method: 'get',
}).then(response => {
if (response.data.code === 200) {
let records = response.data.data.records
const editable = row.editable || row.taskHolderId === this.$store.state.staff.staffId
for (let workitem of records) {
workitem['editable'] = editable
//console.log(workitem)
}
resolve(records)
for(let workitem of records)
{
let rtr = that.maps.get(workitem.taskId);
if (rtr) {
rtr.treeNode.loading = true
that.refreshChildren(rtr.row, rtr.treeNode, rtr.resolve)
}
}
} }
}).catch(function (error) { }).catch(function (error) {
console.log(error) console.log(error)
@ -127,6 +776,10 @@ export default {
} }
return moment(date * 1000).format("yyyy-MM-DD HH:mm") return moment(date * 1000).format("yyyy-MM-DD HH:mm")
}, },
priorityFormatter(row, column) {
const priority = ['最高', '较高', '普通', '较低', '最低']
return priority[row[column.property] - 1]
}
} }
} }
</script> </script>