qinzhipeng_v 5 лет назад
Родитель
Сommit
2f9e4a1f62

+ 1 - 0
src/api/defectManage.js

@@ -191,3 +191,4 @@ export function bugTeamList(data) {
     data
   })
 }
+

+ 18 - 0
src/api/requirement.js

@@ -154,3 +154,21 @@ export function listByRequire(id) {
     method: 'get'
   })
 }
+
+// 需求排期锁定
+export function scheduleLockRequireSchedule(data) {
+  return request({
+    url: requestIp + `/schedule/lockRequireSchedule?id=` + data,
+    method: 'post',
+    data
+  })
+}
+
+// 需求排期解锁
+export function scheduleUnlockRequireSchedule(data, id) {
+  return request({
+    url: requestIp + `/schedule/unlockRequireSchedule?id=` + id,
+    method: 'post',
+    data
+  })
+}

BIN
src/assets/Unlock.png


BIN
src/assets/lock.png


BIN
src/assets/scheduling.png


+ 101 - 1
src/styles/PublicStyle/index.scss

@@ -311,4 +311,104 @@
 .statusName2 {
   border: 1px solid #7ED321;
   color: #7ED321;
-}
+}
+
+// 排期锁定
+.public_image {
+  width: 12.31px;
+  display: inline-table;
+  vertical-align: inherit;
+}
+.SchedulingAside { // 排期变更
+  width:270px !important;
+  max-height: 375px;
+  .SchedulingDiv {
+    display: flex;
+    position: relative;
+    justify-content: flex-start;
+    padding-left: 20px;
+  }
+  .SchedulingTow {
+    width: 20px;
+    left: 0;
+    height: 20px;
+    border: 1px solid #999999;
+    border-radius: 50%;
+    display: flex;
+    position: absolute;
+    z-index: 9;
+    top: 7px;
+    background-color: #ffffff;
+    .image {
+      display: inline-block;
+      width: 8px;
+      margin: 0 auto;
+      align-self: center;
+    }
+  }
+  i {
+    width:0px;
+    height: 100%;
+    border:0.5px solid #CFCFCF;
+    opacity:0.66;
+    position: absolute;
+    left: 9px;
+    z-index: 2;
+    top: 7px;
+  }
+  .btn {
+      width:92px;
+      height:17px;
+      color:rgba(51,51,51,1);
+      background:rgba(230,230,230,1);
+      border-radius:30px;
+      padding: 0px 5px;
+      margin: 0 5px;
+  }
+}
+ .modifyTime {
+    color:rgba(153,153,153,1);
+  }
+  .timeline:hover {
+      color:#409EFF !important;
+      .modifyTime{
+        color:#409EFF !important;
+      }
+      .btn {
+        color:#409EFF !important;
+      }
+  }
+  .titleStatus {
+    font-size:12px;
+    font-family:MicrosoftYaHei;
+    color:rgba(102,102,102,1);
+    opacity:1;
+    margin-left: 20px;
+    cursor: pointer;
+ }
+ .timeline {
+    font-size:12px;
+    font-family:PingFangSC-Regular;
+    line-height:17px;
+    color:rgba(51,51,51,1);
+    opacity:1;
+    padding: 2px;
+    margin: 0 20px;
+    cursor: pointer;
+  }
+  .Scheduling {
+    margin:0px 20px 0px 0px;
+    color:#1890FF;
+    cursor: pointer;
+  }
+  .allTips {
+  display: flex;
+  justify-content: space-between;
+  margin: 0px;
+}
+.tips {
+      margin:0 0 4px 20px;
+      color: #E6A23C;
+      font-size: 12px;
+}
+// 排期锁定截止

+ 9 - 4
src/views/projectManage/requirement/components/scheduleList.vue

@@ -20,17 +20,17 @@
       </el-table-column>
       <el-table-column prop="type" label="类型" min-width="100" align="center">
         <template slot-scope="scope">
-          {{ getType(scope.row.type) }}
+          {{ getType(scope.row.type) }} <img class="public_image" :src="scope.row.isScheduleLocked === 0 ? Unlock : lock">
         </template>
       </el-table-column>
       <el-table-column prop="desc" label="描述" min-width="150" align="left" show-overflow-tooltip />
       <el-table-column prop="seperateDaysNoHoliday" label="排期" min-width="200" align="center" show-overflow-tooltip />
       <el-table-column prop="dayLength" label="时长" min-width="50" align="center" />
-      <el-table-column prop="peopleList" label="参与人员" min-width="100" align="center" />
+      <el-table-column prop="peopleList" label="参与人员" min-width="150" align="center" />
       <el-table-column label="操作" width="200" align="center">
         <template slot-scope="scope">
-          <el-button type="text" size="small" @click="editSchedule(scope.row)">编辑</el-button>
-          <el-button type="text" size="small" @click="deleteSchedule(scope.row)">删除</el-button>
+          <el-button v-if="scope.row.isScheduleLocked === 0" type="text" size="small" @click="editSchedule(scope.row)">编辑</el-button>
+          <el-button v-if="scope.row.isScheduleLocked === 0" type="text" size="small" @click="deleteSchedule(scope.row)">删除</el-button>
         </template>
       </el-table-column>
 
@@ -63,6 +63,9 @@ import { listByRequire } from '@/api/requirement.js'
 import { listByTask, sortForTask } from '@/api/projectViewDetails'
 import modifySchedule from './modifySchedule'
 import move from '@/assets/麻将@2x.png'
+import lock from '@/assets/lock.png' // 排期锁定图标
+import Unlock from '@/assets/Unlock.png' // 排期锁定图标
+import '@/styles/PublicStyle/index.scss' // 通用css
 export default {
   components: {
     modifySchedule
@@ -107,6 +110,8 @@ export default {
   data() {
     return {
       move: move,
+      Unlock: Unlock, // 解锁排期
+      lock: lock, // 锁定排期
       scheduleList: [],
       scheduleDetail: {},
       visibleSchedule: false,

+ 66 - 43
src/views/projectManage/requirement/requirementDetail.vue

@@ -114,33 +114,40 @@
           <div class="el-main-title">
             <div class="title-left-icon" />
             <div class="title-left-name">
-              <div class="require-plan">
-                需求计划
-                <div> 🔒已锁定</div><br>
-                <span><i class="el-icon-warning-outline" />每个任务仅支持一次提测和一次准出,请合理拆解后任务再排期</span>
+              <div>需求计划
+                <el-tooltip class="item" effect="dark" :content="isScheduleLocked === 1? '点击解锁排期' : '点击锁定排期'" placement="top">
+                  <span class="titleStatus" @click="changeSchedule"><img :src="lock" style="width: 12.31px;display: inline-block; vertical-align: text-top;"> {{ isScheduleLocked === 1 ? '已锁定' : '未锁定' }}</span>
+                </el-tooltip>
               </div>
-            </div>
+            </div><br>
           </div>
+          <section class="main-section">
+            <div class="allTips">
+              <div class="tips"><i class="el-icon-warning-outline" />每个任务仅支持一次提测和一次准出,请合理拆解后任务再排期</div>
+              <div v-if="BackToTheLatest" class="Scheduling" @click="GetRequireScheduleHistory"><img :src="scheduling"> 回到最新</div>
+              <div align="left" class="Scheduling" @click="scheduleHiHide"><img :src="scheduling"> 排期变更记录</div>
+            </div>
+          </section>
+
           <el-container>
             <el-main style="padding: 0;">
-              <schedule-list :id="requirementId" :type-list="taskScheduleEvent" :required-list="taskScheduleList" class-name="white" :all="true" :no-move="false" />
+              <schedule-list :id="requirementId" ref="ScheduleEvent" :type-list="taskScheduleEvent" :required-list="taskScheduleList" class-name="white" :all="true" :no-move="false" />
             </el-main>
-            <el-aside>
-              <div align="center" class="Scheduling">排期变更记录</div>
-              <el-timeline style=" padding: 10px 0 0 10px; width: 270px;">
-                <el-timeline-item
-                  v-for="(item, index) in SchedulingContent"
-                  :key="index"
-                  icon="el-icon-goods"
-                  color="#f6f6f6"
-                  class="timeline"
-                  @click.native="clickScheduling(item)"
-                >
-                  <div>{{ item.modifyTime }}</div>
-                  <div>{{ item.operatorObject.name !== null ? item.operatorObject.name : '' }}{{ item.operation }}</div>
-                  <div>{{ item.remark }}</div>
-                </el-timeline-item>
-              </el-timeline>
+            <el-aside v-if="lockHide" class="SchedulingAside">
+              <div v-for="(item, index) in SchedulingContent" :key="index" class="SchedulingDiv" @click="clickScheduling(item)">
+                <div class="SchedulingTow">
+                  <img v-if="item.type === 1" :src="lock" class="image">
+                  <img v-if="item.type === 2" :src="Unlock" class="image">
+                </div>
+                <i v-show="index < SchedulingContent.length - 1" />
+                <div class="timeline">
+                  <div class="modifyTime">{{ item.modifyTime }}</div>
+                  <div>{{ item.operatorObject.name !== null ? item.operatorObject.name : '' }} <span class="btn">{{ item.operation }}</span></div>
+                  <div v-if="item.remarkTypeName"><span class="modifyTime">{{ '解锁原因 : ' }}</span>{{ item.remarkTypeName }}</div>
+                  <div v-if="item.remark"><span class="modifyTime">{{ '具体描述 : ' }}</span>{{ item.remark }}</div>
+                </div>
+              </div>
+              <div v-if="SchedulingContent.length === 0" style="width: 270px; text-align: center;"> 暂无排期变更记录!</div>
             </el-aside>
           </el-container>
         </section>
@@ -222,6 +229,9 @@
       <!-- 删除 -->
       <openDialog v-if="task_open" ref="task_createdUpdata" :no-jump="true" @change="reloadList" />
       <createdBug v-if="bug_open" ref="createdBug" :required="requirementId" @reloadList="reloadList" />
+      <!-- 排期锁定 -->
+      <schedule :visible.sync="scheduleVisble" :name="'需求'" :is-schedule-locked="isScheduleLocked" :require-id="requirementId" @updataData="GetRequireScheduleHistory" />
+      <!-- 排期锁定 -->
       <drawer
         ref="drawer"
         title="需求成员"
@@ -250,6 +260,7 @@ import {
   iterationList,
   getCommentList,
   addComment,
+  listByRequire,
   scheduleGetRequireScheduleHistory,
   scheduleGetHistoryScheduleById
 } from '@/api/requirement.js'
@@ -265,6 +276,10 @@ import tasksList from './components/taskList'
 import dataStatistics from './components/dataStatistics'
 import scheduleList from './components/scheduleList'
 import bugTableDialog from '@/views/projectManage/bugList/details/bugTableDialog' // 缺陷表格
+import scheduling from '@/assets/scheduling.png'
+import lock from '@/assets/lock.png' // 排期锁定图标
+import Unlock from '@/assets/Unlock.png' // 排期锁定图标
+import schedule from '@/views/projectManage/schedule' // 排期锁定弹窗
 export default {
   components: {
     searchPeople,
@@ -276,7 +291,8 @@ export default {
     tasksList,
     dataStatistics,
     scheduleList,
-    bugTableDialog
+    bugTableDialog,
+    schedule
   },
   filters: {
     ellipsis(value, num) {
@@ -289,6 +305,12 @@ export default {
   },
   data() {
     return {
+      BackToTheLatest: false, // 回到最新
+      LockState: {}, // 锁定状态
+      Unlock: Unlock, // 解锁排期
+      lock: lock, // 锁定排期
+      scheduleVisble: false, // 排期锁定
+      scheduling: scheduling, // 排期变更记录图标
       activeName: '1', // 顶部tab切换
       userInformation: localStorage.getItem('username'),
       userNames: localStorage.getItem('realname'),
@@ -314,6 +336,8 @@ export default {
       commentContent: null, // 评论内容
       comments: [], // 评论列表
       taskScheduleList: [], // 排期数据
+      lockHide: false, // 隐藏排期变更记录
+      isScheduleLocked: '', // 锁定状态1锁定0未锁定
       SchedulingContent: [] // 排期历史变更记录
     }
   },
@@ -339,13 +363,26 @@ export default {
   },
   methods: {
     async GetRequireScheduleHistory() {
+      this.scheduleVisble = false
       const res = await scheduleGetRequireScheduleHistory(this.requirementId)
       this.SchedulingContent = res.data
+      const res1 = await listByRequire(this.requirementId)
+      this.BackToTheLatest = false // 回到最新
+      this.lockHide = false // 隐藏排期变更记录
+      this.taskScheduleList = res1.data.schedulDetailResponses
+      this.isScheduleLocked = res1.data.isScheduleLocked // 锁定状态1锁定0未锁定
     },
     async clickScheduling(ele) {
       const res = await scheduleGetHistoryScheduleById(ele.id)
       this.taskScheduleList = res.data.schedulDetailResponses
+      this.BackToTheLatest = true
     },
+    changeSchedule() { // 修改锁定状态
+      this.scheduleVisble = true
+    },
+    // clickBackToTheLatest() {
+    //   this.$refs.ScheduleEvent.rowDrop()
+    // },
     async changeArea(e) { // area修改
       const requirementInfo = _.cloneDeep(this.form_query)
       requirementInfo.pm = requirementInfo.pm.idap
@@ -461,6 +498,10 @@ export default {
         this.$refs.createdBug.init(1)
       })
     },
+    scheduleHiHide() { // 排期变更显示隐藏
+      this.lockHide = !this.lockHide
+      this.lockHide === false ? this.BackToTheLatest = false : ''
+    },
     jump(page, id) { // 跳转
       this.$router.push({ name: page, query: { id: id }})
     },
@@ -500,26 +541,8 @@ export default {
 }
 .main-section {
   @include main-section;
-  .require-plan {
-    display: flex;
-    align-items: center;
-    span {
-      margin-left: 20px;
-      color: #E6A23C;
-      font-size: 12px;
-    }
-  }
-  .Scheduling {
-    margin:0px 30px 20px 0px;
-    cursor: pointer;
-  }
-  .timeline {
-    padding: 2px;
-    cursor: pointer;
-  }
-  .timeline:hover {
-      color:#409EFF !important;
-    }
+  line-height: 20px;
+
   .detail-info {
     padding: 0 34px 20px 34px;
     /deep/.el-input__inner{

+ 114 - 0
src/views/projectManage/schedule.vue

@@ -0,0 +1,114 @@
+<template>
+  <el-dialog :title="isSchedule===1?'排期锁定': '排期解锁'" class="task" :visible.sync="dialogVisible" width="30%" :before-close="handleClose">
+    <div class="blueStripe" />
+    <!-- <div v-if="isSchedule === 1" align="center">是否需要锁定当前需求的排期?</div> -->
+    <div v-if="isSchedule === 0" align="center">
+      <el-form ref="numberValidateForm" :model="numberValidateForm" label-width="100px" class="demo-ruleForm">
+        <el-form-item label="解锁原因" prop="age" :rules="[{ required: true, message: '解锁原因不能为空'}]">
+          <el-select v-model="numberValidateForm.remarkType" style="width:100%;" placeholder="请选择" @change="changeArea">
+            <el-option v-for="(item,index) in scheduleOperateReason" :key="index" :label="item.msg" :value="item.code" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="具体描述">
+          <el-input v-model="numberValidateForm.remark" type="textarea" />
+        </el-form-item>
+      </el-form>
+    </div>
+    <span slot="footer" class="dialog-footer">
+      <el-button size="min" @click="handleClose">取 消</el-button>
+      <el-button size="min" type="primary" @click="isSchedule === 1 ? determine : Unlock(numberValidateForm)">确 定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { scheduleLockRequireSchedule, scheduleUnlockRequireSchedule } from '@/api/requirement.js'
+import { configShowTaskEnum } from '@/api/taskIndex.js'
+export default {
+  props: {
+    visible: { type: Boolean, default: false },
+    requireId: { type: Number, required: true },
+    name: { type: String, required: true },
+    isScheduleLocked: { type: String, required: true }
+  },
+  data() {
+    return {
+      dialogVisible: false, // dialog显示隐藏
+      isSchedule: Number(this.isScheduleLocked),
+      numberValidateForm: { // 解锁原因
+        remarkType: 3 // 默认延期,排期调整
+      },
+      codeName: this.name, // 判断是任务还是需求
+      scheduleOperateReason: [] // 解锁yuanyin
+    }
+  },
+  watch: {
+    visible(val) {
+      this.dialogVisible = val
+    }
+  },
+  created() {
+    this.getTaskEnum()
+  },
+  methods: {
+    async getTaskEnum() { // 获取解锁原因下拉数据
+      const res = await configShowTaskEnum()
+      this.scheduleOperateReason = res.data.scheduleOperateReason
+    },
+    async determine() {
+      if (this.name === '需求') {
+        const res = await scheduleLockRequireSchedule(this.requireId)
+        if (res.code === 200) {
+          this.dialogVisible = false
+          this.$emit('updataData')
+          this.$message({ message: '已锁定当前需求排期', type: 'success', duration: 1000, offset: 150 })
+        }
+      }
+      if (this.name === '任务') {
+        this.$refs.numberValidateForm.validate((valid) => {
+          if (valid) {
+            alert('submit!')
+          } else {
+            console.log('error submit!!')
+            return false
+          }
+        })
+      }
+    },
+    Unlock(ele) {
+      alert()
+      const res = scheduleUnlockRequireSchedule(ele, this.requireId)
+      if (res.code === 200) { alert() }
+      this.$message({ message: '已解锁当前需求排期', type: 'success', duration: 1000, offset: 150 })
+      this.dialogVisible = false
+      this.$emit('updataData')
+      // }
+    },
+    handleClose() {
+      this.dialogVisible = false
+      this.$emit('updataData')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.task {
+  /deep/ .el-dialog__title {
+    line-height: 24px;
+    font-size: 18px;
+    color: #303133;
+    padding-left: 10px;
+}
+
+.blueStripe {
+  width:4px;
+  height:17px;
+  background:#409EFF;
+  border-radius:1px;
+  position: absolute;
+  top: 23px;
+  left: 20px;
+}
+}
+</style>