Bläddra i källkod

Merge branch 'insist' into http_test

qinzhipeng_v 5 år sedan
förälder
incheckning
cba50d339d

+ 42 - 0
src/api/requirement.js

@@ -180,3 +180,45 @@ export function scheduleDownLoadByRequireId(id) {
     method: 'get'
   })
 }
+
+// 获取需求操作历史记录
+export function operationLogRequire(id) {
+  return request({
+    url: requestIp + `/operationLog/require?requireId=${id}`,
+    method: 'get'
+  })
+}
+
+// 获取需求工作流
+export function requirementGetWorkFlow(id) {
+  return request({
+    url: requestIp + `/requirement/getWorkFlow?id=${id}`,
+    method: 'get'
+  })
+}
+
+// hold需求接口
+export function requirementHold(id, data) {
+  return request({
+    url: requestIp + `/requirement/hold?id=${id}`,
+    method: 'post',
+    data
+  })
+}
+
+// 解hold需求接口
+export function requirementUnhold(id) {
+  return request({
+    url: requestIp + `/requirement/unhold?id=${id}`,
+    method: 'post',
+    id
+  })
+}
+
+// 获取需求状态
+export function configShowRequireStatusEnum(id) {
+  return request({
+    url: requestIp + `/config/showRequireStatusEnum?bizId=${id}`,
+    method: 'get'
+  })
+}

+ 42 - 0
src/api/taskIndex.js

@@ -225,3 +225,45 @@ export function scheduleDownLoadByTaskId(id) {
     method: 'get'
   })
 }
+
+// 获取任务操作历史记录
+export function operationLogTask(id) {
+  return request({
+    url: TeamManagement + `/operationLog/task?taskId=${id}`,
+    method: 'get'
+  })
+}
+
+// 获取任务工作流
+export function taskGetWorkFlow(id) {
+  return request({
+    url: TeamManagement + `/task/getWorkFlow?id=${id}`,
+    method: 'get'
+  })
+}
+
+// hold
+export function taskHold(id, data) {
+  return request({
+    url: TeamManagement + `/task/hold?id=${id}`,
+    method: 'post',
+    data
+  })
+}
+
+// 解hold
+export function taskUnhold(id, data) {
+  return request({
+    url: TeamManagement + `/task/unhold?id=${id}`,
+    method: 'post',
+    data
+  })
+}
+
+// 获取任务状态
+export function configShowTaskStatusEnum(id) {
+  return request({
+    url: TeamManagement + `/config/showTaskStatusEnum?bizId=${id}`,
+    method: 'get'
+  })
+}

+ 1 - 0
src/views/projectManage/components/export.vue

@@ -1,4 +1,5 @@
 <template>
+  <!-- 排期导出 -->
   <div align="left" class="Scheduling" @click="saveFile()"><div class="el-icon-download" /> 导出排期</div>
 </template>
 

+ 80 - 0
src/views/projectManage/components/record.vue

@@ -0,0 +1,80 @@
+<template>
+  <!-- 变更记录 -->
+  <div>
+    <div v-for="(item,index) in changeRecord" :key="index" class="Layout_space_between sign-record">
+      <span>
+        <span class="operatorName">{{ item.operator }} : </span>
+        <!-- <el-tooltip
+          v-if="item.remark.length > 50"
+          class="item"
+          effect="dark"
+          :content="item.remark"
+          placement="top-start"
+          popper-class="tip-style"
+        > -->
+        <span class="remark">{{ item.remark }}</span>
+        <!-- </el-tooltip>
+        <span v-else class="remark">{{ item.remark.length > 50 ? item.remark.substring(0, 50) + '...' : item.remark }}</span> -->
+      </span>
+      <span class="createTime">{{ item.createTime }}</span>
+    </div>
+  </div>
+</template>
+
+<script>
+import '@/styles/PublicStyle/index.scss'
+import { operationLogTask } from '@/api/taskIndex'
+import { operationLogRequire } from '@/api/requirement.js'
+export default {
+  props: {
+    id: { type: [Number, String], default: null },
+    name: { type: String, default: null }
+  },
+  data() {
+    return {
+      changeRecord: [] // 变更记录
+    }
+  },
+  created() {
+    this.operationLogTask()
+  },
+  methods: {
+    async operationLogTask() {
+      if (this.name === '任务') {
+        const res = await operationLogTask(this.id)
+        this.changeRecord = res.data
+      }
+      if (this.name === '需求') {
+        const res = await operationLogRequire(this.id)
+        this.changeRecord = res.data
+      }
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.sign-record {
+  margin: 20px 0;
+  font-size:14px;
+  line-height:20px;
+  opacity:1;
+  font-family:PingFangSC-Regular;
+
+.createTime {
+  min-width:150px;
+  color:rgba(68,68,68,1);
+}
+
+.remark {
+  min-width:500px;
+  text-align: left;
+  color:#444444;
+}
+.operatorName {
+  min-width: 60px;
+  color:rgba(51,59,74,1);
+  margin-right: 10px;
+}
+}
+</style>

+ 231 - 0
src/views/projectManage/components/timeLine.vue

@@ -0,0 +1,231 @@
+<template>
+  <div class="swiper-container">
+    <swiper ref="mySwiper" class="swiper-wrapper timeline" :options="options">
+      <swiper-slide v-for="(item,index) in steps" :key="index" class="swiper-slide">
+        <div class="line" />
+        <div
+          class="center circle"
+          :class="{
+            'circle2': item.createTime !== null && item.createTime !== '今天',
+            'circle3': item.createTime === '今天' && item.statusName !== 'HOLD' && item.statusName !== 'UNHOLD',
+            'circle4': item.createTime === null,
+            'circle5': item.statusName === 'HOLD' || item.statusName === 'UNHOLD'
+          }"
+        >
+          <el-tooltip v-if="item.id === -2 " class="item" effect="dark" :content="'Hold原因:' + item.remark" placement="top">
+            <div
+              :class="{
+                'circle-in': item.createTime !== null && item.createTime !== '今天',
+                'circle-is': item.createTime === '今天',
+                'circle-iq': item.createTime === null,
+                'circle-ia': item.statusName === 'HOLD' || item.statusName === 'UNHOLD'
+              }"
+              class="circle-of center"
+            />
+          </el-tooltip>
+          <div
+            v-else
+            :class="{
+              'circle-in': item.createTime !== null && item.createTime !== '今天',
+              'circle-is': item.createTime === '今天',
+              'circle-iq': item.createTime === null,
+              'circle-ia': item.statusName === 'HOLD' || item.statusName === 'UNHOLD'
+            }"
+            class="circle-of center"
+          />
+        </div>
+        <div :class="[index%2==0?'point1':'point2']" />
+        <div class="content3">
+          {{ item.timeSpan }}
+        </div>
+        <div class="content1">
+          {{ item.createTime }}
+        </div>
+        <div class="content2">
+          {{ item.statusName }}
+        </div>
+      </swiper-slide>
+    </swiper>
+  </div>
+</template>
+
+<script>
+import { Swiper, SwiperSlide, directive } from 'vue-awesome-swiper'
+import 'swiper/css/swiper.css'
+import { taskGetWorkFlow } from '@/api/taskIndex'
+import { requirementGetWorkFlow } from '@/api/requirement.js'
+export default {
+  components: {
+    Swiper,
+    SwiperSlide
+  },
+  directives: {
+    swiper: directive
+  },
+  props: {
+    id: { type: [Number, String], default: null },
+    name: { type: String, default: null }
+  },
+  data() {
+    return {
+      steps: [],
+      options: {
+        pagination: {
+          el: '.swiper-pagination'
+        },
+        slidesPerView: 5,
+        grabCursor: true
+      }
+    }
+  },
+  computed: {
+    swiper() {
+      return this.$refs.mySwiper.$swiper
+    }
+  },
+  created() {
+    this.taskGetWorkFlow()
+  },
+  methods: {
+    async taskGetWorkFlow() {
+      if (this.name === '任务') {
+        const res = await taskGetWorkFlow(this.id)
+        this.steps = res.data.workFlowNodeList
+      }
+      if (this.name === '需求') {
+        const res = await requirementGetWorkFlow(this.id)
+        this.steps = res.data.workFlowNodeList
+      }
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.swiper-container {
+  padding-bottom: 50px;
+  position: relative;
+}
+.timeline {
+  width: 90%;
+  list-style-type: none;
+  display: flex;
+  padding: 0;
+  text-align: center;
+}
+.swiper-slide {
+  height: 150px;
+  width: 200px;
+  display: flex;
+  align-items: center;
+  font-size: 18px;
+  position: relative;
+  .line{
+    width: 100%;
+    height:0px;
+    border: 1px solid rgba(217,217,217,1);
+  }
+  .center {
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%,-50%);
+  }
+  .circle {
+    position: absolute;
+  }
+  .circle2::after{
+    content: '';
+    position: absolute;
+    height: 50px;
+    border: 1px solid rgba(1,86,165,1);
+    left: 50%;
+    bottom: -16px;
+    transform: translate(-50%,33px);
+  }
+  .circle3::after{
+    content: '';
+    display:none;
+    position: absolute;
+    height: 50px;
+    border: 1px solid rgba(1,86,165,1);
+    left: 50%;
+    bottom: -16px;
+    transform: translate(-50%,33px);
+  }
+  .circle4::after{
+    content: '';
+    position: absolute;
+    height: 50px;
+    border:1px solid rgba(153,153,153,1);
+    left: 50%;
+    bottom: -16px;
+    transform: translate(-50%,33px);
+  }
+  .circle5::after{
+    content: '';
+    position: absolute;
+    height: 50px;
+    border:1px solid rgba(214,0,0,1);
+    left: 50%;
+    bottom: -16px;
+    transform: translate(-50%,33px);
+  }
+
+  .circle-of {
+    position: absolute;
+    border-radius: 50%;
+    width: 12px;
+    height: 12px;
+    z-index: 99;
+  }
+
+  .circle-in {
+    background: rgba(24,144,255,1);
+    border: 1px solid rgba(1,86,165,1);
+  }
+  .circle-is {
+    background:rgba(126,211,33,1);
+    border:1px solid rgba(85,163,0,1);
+  }
+  .circle-iq {
+    background:rgba(153,153,153,1);
+    border:1px solid rgba(153,153,153,1);
+  }
+  .circle-ia {
+    background:rgba(245,108,108,1);
+    border:1px solid rgba(214,0,0,1);
+  }
+
+   .content1,.content2 {
+    width: 100%;
+    font-size: 12px;
+    color: rgba(51,51,51,1);
+    position: absolute;
+    left: 50%;
+    transform: translateX(-50%);
+  }
+  .content3 {
+    width: 100%;
+    font-size: 10px;
+    color: rgba(97,175,255,1);
+    position: absolute;
+    left: 0%;
+    top: 58px;
+    transform: translateX(-50%);
+  }
+  .content2 {
+    bottom:5px;
+    font-weight:500;
+  }
+  .content1 {
+    top: 45px;
+  }
+}
+.swiper-slide:nth-child(2n) {
+  width: 40%;
+}
+.swiper-slide:nth-child(3n) {
+  width: 20%;
+}
+</style>
+

+ 10 - 5
src/views/projectManage/projectList/components/needsList.vue

@@ -35,7 +35,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -64,7 +64,7 @@
 </template>
 <script>
 import { requirementQueryRequirementInfoList } from '@/api/projectIndex'
-import { showRequirementEnum, updateRequirementStatus } from '@/api/requirement'
+import { configShowRequireStatusEnum, updateRequirementStatus } from '@/api/requirement'
 import extraUrgent from '@/assets/extraUrgent.png'
 export default {
   data() {
@@ -80,10 +80,15 @@ export default {
   },
   methods: {
     async getTaskStatus() { // 获取需求的所有状态
-      const res = await showRequirementEnum()
-      if (res.code === 200) {
-        this.allStatus = res.data.requirementStatus
+      const res1 = await configShowRequireStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = []
+        this.allStatus = res1.data.requirementStatus
       }
+      // const res = await showRequirementEnum()
+      // if (res.code === 200) {
+      //   this.allStatus = res.data.requirementStatus
+      // }
     },
     async getNeedsList() {
       const res = await requirementQueryRequirementInfoList({ belongingProject: this.$route.query.id })

+ 6 - 3
src/views/projectManage/projectList/components/taskList.vue

@@ -80,7 +80,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -124,7 +124,7 @@ import DailyReport from '@/views/Platform/presentation/Templates/DailyReport' //
 import ClientReport from '@/views/Platform/presentation/Templates/ClientReport' // 准出
 import { taskList } from '@/api/projectIndex'
 import { taskUpdate } from '@/api/projectViewDetails'
-import { configShowTaskEnum } from '@/api/taskIndex'
+import { configShowTaskEnum, configShowTaskStatusEnum } from '@/api/taskIndex'
 import scheduleList from './scheduleList'
 import modifySchedule from './modifySchedule'
 import taskDialog from '@/views/projectManage/taskList/dialog/taskDialog' // 任务状态修改(已上线/已提测/已准出)
@@ -182,9 +182,12 @@ export default {
     async getTaskStatus() { // 获取任务状态列表
       const res = await configShowTaskEnum()
       if (res.code === 200) {
-        this.allStatus = res.data.taskStatus
         this.taskScheduleEvent = res.data.taskScheduleEvent || []
       }
+      const res1 = await configShowTaskStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = res1.data.taskStatus
+      }
     },
     changeCheck(val) {
       if (val) {

+ 6 - 3
src/views/projectManage/requirement/components/taskList.vue

@@ -81,7 +81,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -121,7 +121,7 @@ import DailyReport from '@/views/Platform/presentation/Templates/DailyReport' //
 import ClientReport from '@/views/Platform/presentation/Templates/ClientReport' // 准出
 import { taskList } from '@/api/projectIndex'
 import { taskUpdate } from '@/api/projectViewDetails'
-import { configShowTaskEnum } from '@/api/taskIndex'
+import { configShowTaskEnum, configShowTaskStatusEnum } from '@/api/taskIndex'
 import scheduleList from './scheduleList'
 import modifySchedule from './modifySchedule'
 import '@/styles/PublicStyle/index.scss'
@@ -176,9 +176,12 @@ export default {
     async getTaskStatus() { // 获取任务状态列表
       const res = await configShowTaskEnum()
       if (res.code === 200) {
-        this.allStatus = res.data.taskStatus
         this.taskScheduleEvent = res.data.taskScheduleEvent || []
       }
+      const res1 = await configShowTaskStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = res1.data.taskStatus
+      }
     },
     changeCheck(val) {
       if (val) {

+ 121 - 26
src/views/projectManage/requirement/requirementDetail.vue

@@ -14,7 +14,8 @@
               >{{ form_query.name }}</span>
             </el-tooltip>
           </div>
-          <el-dropdown placement="bottom" @command="updateStatus">
+          <el-button v-show="form_query.status === -2" disabled plain size="mini">Hold</el-button>
+          <el-dropdown v-show="form_query.status !== -2" placement="bottom" @command="updateStatus">
             <el-button size="mini" plainclass="el-dropdown-link drop_down">
               {{ getStatus.msg }}
               <i class="el-icon-arrow-down el-icon--right" />
@@ -53,6 +54,37 @@
           <i class="el-icon-delete icon-delete" @click="deleteVisible = true" />
         </div>
       </el-header>
+      <el-container v-show="activeName === '1'">
+        <section class="main-section">
+          <div class="Layout_space_between">
+            <div class="el-main-title">
+              <div class="title-left-icon" />
+              <div class="title-left-name">工作流</div>
+            </div>
+            <el-popover
+              v-model="visible"
+              placement="bottom-start"
+              width="300px"
+              :visible-arrow="false"
+              trigger="manual"
+            >
+              <el-input
+                v-model="textarea2"
+                type="textarea"
+                rows="5"
+                style="width:300px"
+                placeholder="请输入Hold原因(选填)"
+              />
+              <div style="text-align: right; margin-top: 10px;">
+                <el-button size="mini" type="text" @click="visible = false">取消</el-button>
+                <el-button type="primary" size="mini" @click="requirementHold(textarea2)">确定</el-button>
+              </div>
+              <el-button slot="reference" class="el-btn-size" size="mini" @click="changeBtn">{{ form_query.status === -2 ? HoldTask = '解除 Hold' : HoldTask = 'Hold 任务' }}</el-button>
+            </el-popover>
+          </div>
+          <timeLine :id="requirementId" ref="timeLine1" :name="'需求'" />
+        </section>
+      </el-container>
       <!-- 概览 -->
       <el-container v-show="activeName === '1'" class="is-vertical">
         <section class="main-section">
@@ -72,7 +104,7 @@
                 </el-select>
               </el-form-item>
               <el-form-item label="PM:">
-                <search-people :value.sync="form_query.pm.idap" :clearable="false" @change="changeArea" />
+                <search-people :value.sync="form_query.pm" :clearable="false" @change="changeArea" />
               </el-form-item>
             </el-form>
             <el-form :inline="true" :model="form_query" class="demo-form-inline" label-position="right" label-width="100px">
@@ -162,29 +194,37 @@
         <section class="main-section">
           <div class="el-main-title">
             <div class="title-left-icon" />
-            <div class="title-left-name">评论</div>
-          </div>
-          <div class="detail-info">
-            <ul class="comment-main">
-              <li v-for="(item,index) in comments" :key="'comment'+index">
-                <span class="comment-name">{{ item.commentInfo.name }}</span>
-                <span class="comment-gmtCreater">{{ item.commentInfo.gmtCreater }}</span><br>
-                <span class="comment-content">{{ item.commentInfo.content }}</span>
-              </li>
-            </ul>
-            <el-input
-              v-model="commentContent"
-              type="textarea"
-              placeholder="请输入评论内容"
-              maxlength="300"
-              show-word-limit
-              :autosize="{ minRows: 3, maxRows: 5}"
-              style="margin-bottom: 20px"
-            />
-            <el-row>
-              <el-col :span="2" :offset="22"><el-button type="primary" size="small" @click="addComment">发表评论</el-button></el-col>
-            </el-row>
+            <div class="title-left-name">动态</div>
           </div>
+          <el-tabs v-model="optionName" class="sign-tabs" @tab-click="handleClick">
+            <el-tab-pane label="评论" name="first">
+              <div class="detail-info">
+                <ul class="comment-main">
+                  <li v-for="(item,index) in comments" :key="'comment'+index">
+                    <span class="comment-name">{{ item.commentInfo.name }}</span>
+                    <span class="comment-gmtCreater">{{ item.commentInfo.gmtCreater }}</span><br>
+                    <span class="comment-content">{{ item.commentInfo.content }}</span>
+                  </li>
+                </ul>
+                <el-input
+                  v-model="commentContent"
+                  type="textarea"
+                  placeholder="请输入评论内容"
+                  maxlength="300"
+                  show-word-limit
+                  :autosize="{ minRows: 3, maxRows: 5}"
+                  style="margin-bottom: 20px"
+                />
+                <el-row>
+                  <el-col :span="2" :offset="22"><el-button type="primary" size="small" @click="addComment">发表评论</el-button></el-col>
+                </el-row>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="变更记录" name="second">
+              <record :id="requirementId" :name="'需求'" />
+            </el-tab-pane>
+          </el-tabs>
+
         </section>
       </el-container>
       <!-- 概览 -->
@@ -265,6 +305,9 @@ import {
   getCommentList,
   addComment,
   listByRequire,
+  requirementHold,
+  requirementUnhold,
+  configShowRequireStatusEnum,
   scheduleGetRequireScheduleHistory,
   scheduleGetHistoryScheduleById
 } from '@/api/requirement.js'
@@ -284,6 +327,8 @@ import schedule from '@/views/projectManage/schedule' // 排期锁定弹窗
 import urgent from '@/assets/urgent.png'
 import download from '@/views/projectManage/components/export.vue'
 import '@/styles/PublicStyle/index.scss'
+import record from '@/views/projectManage/components/record.vue'
+import timeLine from '@/views/projectManage/components/timeLine.vue'
 export default {
   components: {
     searchPeople,
@@ -297,7 +342,9 @@ export default {
     scheduleList,
     bugTableDialog,
     schedule,
-    download
+    download,
+    record,
+    timeLine
   },
   filters: {
     ellipsis(value, num) {
@@ -312,6 +359,10 @@ export default {
     return {
       urgent: urgent,
       showunlock: true,
+      textarea2: '',
+      HoldTask: '',
+      optionName: 'first',
+      visible: false, // Hold任务
       ScheduId: '', // 排期ID
       BackToTheLatest: false, // 回到最新
       LockState: {}, // 锁定状态
@@ -366,6 +417,9 @@ export default {
     this.$store.state.data.bizId = false
   },
   methods: {
+    handleClick(tab, event) {
+      console.log(tab, event)
+    },
     async GetRequireScheduleHistory() {
       this.scheduleVisble = false
       const res = await scheduleGetRequireScheduleHistory(this.requirementId)
@@ -420,14 +474,48 @@ export default {
       this.iterationList.unshift({ id: -1, name: '无' })
     },
     async showRequirementEnum() { // 获取需求状态列表,优先级列表,需求来源
+      const res1 = await configShowRequireStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.statusList = []
+        this.statusList = res1.data.requirementStatus
+      }
       const res = await showRequirementEnum()
       if (res.code === 200) {
-        this.statusList = res.data.requirementStatus
         this.priorityList = res.data.priority
         this.sourceTypeList = res.data.sourceType
         this.appClient = res.data.appClient
       }
     },
+    changeBtn() {
+      if (this.HoldTask === 'Hold 任务') {
+        this.visible = !this.visible
+        this.textarea2 = ''
+      }
+      if (this.HoldTask === '解除 Hold') {
+        this.requirementHold()
+      }
+    },
+    async requirementHold(val) { // 锁定Hold
+      if (this.HoldTask === 'Hold 任务') {
+        const res = await requirementHold(this.requirementId, { 'remark': val })
+        if (res.code === 200) {
+          this.getRequirementById()
+          this.showRequirementEnum()
+          this.$refs.timeLine1.taskGetWorkFlow()
+          this.visible = false
+          this.$message({ message: '已修改状态为 Hold', type: 'success', duration: 1000, offset: 150 })
+        }
+      }
+      if (this.HoldTask === '解除 Hold') {
+        const res = await requirementUnhold(this.requirementId)
+        if (res.code === 200) {
+          this.getRequirementById()
+          this.showRequirementEnum()
+          this.$refs.timeLine1.taskGetWorkFlow()
+          this.$message({ message: 'Hold 状态已解除', type: 'success', duration: 1000, offset: 150 })
+        }
+      }
+    },
     async getTaskStatus() { // 获取排期类型
       const res = await configShowTaskEnum()
       if (res.code === 200) {
@@ -646,4 +734,11 @@ export default {
 .paddingLeft {
   padding-left: 0px;
 }
+.sign-tabs {
+  padding: 0 30px;
+}
+.el-btn-size {
+   margin: 10px 30px;
+}
 </style>
+

+ 15 - 12
src/views/projectManage/taskList/taskIndex.vue

@@ -15,7 +15,7 @@
       <div class="Layout" style="padding: 5px 0 0 15px">
         <div>
           <el-form :model="form_task" class="flex_start">
-            <div class="Layout" style="padding-left:15px">
+            <div class="Layout">
               <div class="queryName">任务名称</div>
               <el-input v-model="form_task.name" size="small" clearable style="width:77% !important;" placeholder="请输入标题或ID" @change="get_taskList()" />
             </div>
@@ -202,7 +202,7 @@ import {
   memberQueryMemberInfoByIDAPorName,
   configShowRequirementVersionEnum,
   projectListProject,
-  showTaskListEnum
+  configShowTaskStatusEnum
 } from '@/api/taskIndex' // ajax
 import { settingQueryBizModuleList } from '@/api/defectManage'
 import openDialog from '@/views/projectManage/dialog_vue'
@@ -222,7 +222,7 @@ export default {
       pageSize: 15,
       goodName: '更多筛选',
       options: [],
-      daStatus: [],
+      daStatus: [], // 任务状态筛选option
       healthStage: [],
       list: [],
       userInformation: localStorage.getItem('username'),
@@ -365,20 +365,23 @@ export default {
       this.isToOne = false
       this.get_taskList()
     },
-    get_taskSelect() {
+    async get_taskSelect() {
       // 下拉菜单数据
-      configShowTaskEnum().then(res => {
+      const res = await configShowTaskEnum()
+      if (res.code === 200) {
         this.healthStage = res.data.taskStage
         this.noTest = res.data.noTest // 是否免测
         this.taskSource = res.data.taskSource // 归属需求
         this.appClient = res.data.appClient // 涉及客户端
-      })
-      showTaskListEnum().then(res => {
-        this.daStatus = res.data.taskStatus
-      })
-      projectListProject({ bizId: Number(localStorage.getItem('bizId')) }).then(res => {
-        this.projectList = res.data
-      })
+      }
+      const res1 = await configShowTaskStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.daStatus = res1.data.taskStatus
+      }
+      const res2 = await projectListProject({ bizId: Number(localStorage.getItem('bizId')) })
+      if (res2.code === 200) {
+        this.projectList = res2.data
+      }
     },
     bugDataGet() { // 所属模块
       settingQueryBizModuleList(Number(localStorage.getItem('bizId'))).then(res => {

+ 129 - 33
src/views/projectManage/taskList/taskViewDetail.vue

@@ -25,18 +25,19 @@
               >{{ form_query.name }}</span>
             </el-tooltip>
           </div>
-          <el-dropdown placement="bottom" @command="updateStatus">
+          <el-button v-show="form_query.status === -2" disabled plain size="mini">Hold</el-button>
+          <el-dropdown v-show="form_query.status !== -2" placement="bottom" @command="updateStatus">
             <el-button size="mini" plainclass="el-dropdown-link drop_down">
-              {{ getStatus.msg }}
+              {{ getStatus.name }}
               <i class="el-icon-arrow-down el-icon--right" />
             </el-button>
             <el-dropdown-menu slot="dropdown" align="center">
               <el-dropdown-item
                 v-for="item in allStatus"
-                :key="item.msg"
-                :command="{status:item.code,label:item.msg}"
-                :disabled="form_query.status === item.code? true: false"
-              >{{ item.msg }}</el-dropdown-item>
+                :key="item.name"
+                :command="{status:item.code,label:item.name}"
+                :disabled="form_query.status === item.code ? true : false"
+              >{{ item.name }}</el-dropdown-item>
             </el-dropdown-menu>
           </el-dropdown>
         </div>
@@ -68,6 +69,37 @@
           <i class="el-icon-delete icon-delete" @click="deleteVisible = true" />
         </div>
       </el-header>
+      <el-container v-show="activeName === '1'">
+        <section class="main-section">
+          <div class="Layout_space_between">
+            <div class="el-main-title">
+              <div class="title-left-icon" />
+              <div class="title-left-name">工作流</div>
+            </div>
+            <el-popover
+              v-model="visible"
+              placement="bottom-end"
+              width="300px"
+              :visible-arrow="false"
+              trigger="manual"
+            >
+              <el-input
+                v-model="textarea2"
+                type="textarea"
+                rows="5"
+                style="width:300px"
+                placeholder="请输入Hold原因(选填)"
+              />
+              <div style="text-align: right; margin-top: 10px;">
+                <el-button size="mini" type="text" @click="visible = false">取消</el-button>
+                <el-button type="primary" size="mini" @click="taskHold(textarea2)">确定</el-button>
+              </div>
+              <el-button slot="reference" class="el-btn-size" size="mini" @click="changeBtn">{{ form_query.status === -2 ? HoldTask = '解除 Hold' : HoldTask = 'Hold 任务' }}</el-button>
+            </el-popover>
+          </div>
+          <timeLine :id="taskId" ref="timeLine" :name="'任务'" />
+        </section>
+      </el-container>
       <!-- 概览 -->
       <el-container v-show="activeName === '1'" class="is-vertical">
         <section class="main-section">
@@ -183,29 +215,37 @@
         <section class="main-section">
           <div class="el-main-title">
             <div class="title-left-icon" />
-            <div class="title-left-name">评论</div>
-          </div>
-          <div class="detail-info">
-            <ul class="comment-main">
-              <li v-for="(item,index) in comments" :key="'comment'+index">
-                <span class="comment-name">{{ item.commentInfo.name }}</span>
-                <span class="comment-gmtCreater">{{ item.commentInfo.gmtCreater }}</span><br>
-                <span class="comment-content">{{ item.commentInfo.content }}</span>
-              </li>
-            </ul>
-            <el-input
-              v-model="commentContent"
-              type="textarea"
-              placeholder="请输入评论内容"
-              maxlength="300"
-              show-word-limit
-              :autosize="{ minRows: 3, maxRows: 5}"
-              style="margin-bottom: 20px"
-            />
-            <el-row>
-              <el-col :span="2" :offset="22"><el-button type="primary" size="small" @click="addComment">发表评论</el-button></el-col>
-            </el-row>
+            <div class="title-left-name">动态</div>
           </div>
+          <el-tabs v-model="tabPosition" class="sign-tabs" @tab-click="handleClick">
+            <el-tab-pane label="评论" name="first">
+              <div class="detail-info">
+                <ul class="comment-main">
+                  <li v-for="(item,index) in comments" :key="'comment'+index">
+                    <span class="comment-name">{{ item.commentInfo.name }}</span>
+                    <span class="comment-gmtCreater">{{ item.commentInfo.gmtCreater }}</span><br>
+                    <span class="comment-content">{{ item.commentInfo.content }}</span>
+                  </li>
+                </ul>
+                <el-input
+                  v-model="commentContent"
+                  type="textarea"
+                  placeholder="请输入评论内容"
+                  maxlength="300"
+                  show-word-limit
+                  :autosize="{ minRows: 3, maxRows: 5}"
+                  style="margin-bottom: 20px"
+                />
+                <el-row>
+                  <el-col :span="2" :offset="22"><el-button type="primary" size="small" @click="addComment">发表评论</el-button></el-col>
+                </el-row>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane label="变更记录" name="second">
+              <record :id="taskId" :name="'任务'" />
+            </el-tab-pane>
+          </el-tabs>
+
         </section>
       </el-container>
       <!-- 概览 -->
@@ -287,6 +327,8 @@ import {
   taskUpdate,
   commentCreate,
   commentList,
+  taskHold,
+  taskUnhold,
   configShowRequirementVersionEnum,
   scheduleGetTaskScheduleHistory
 } from '@/api/taskIndex'
@@ -298,7 +340,6 @@ import textArea from '@/components/input/textArea'
 import drawer from '@/views/projectManage/Drawer'
 import image_url from '@/assets/home_images/home_u.png'
 import createdBug from '@/views/projectManage/bugList/file/createdBug'
-// import normalDialog from '@/components/dialog/normalDialog'
 import openDialog from '@/views/projectManage/dialog_vue'
 import createChildren from './childrenTask/createChildren'
 import childrenList from './childrenTask/childrenList'
@@ -316,10 +357,11 @@ import bugTableDialog from '@/views/projectManage/bugList/details/bugTableDialog
 import '@/styles/PublicStyle/index.scss'
 import schedule from '@/views/projectManage/schedule' // 排期锁定弹窗
 import download from '@/views/projectManage/components/export.vue'
+import record from '@/views/projectManage/components/record.vue'
+import timeLine from '@/views/projectManage/components/timeLine.vue'
 export default {
   components: {
     searchPeople,
-    // normalDialog,
     textArea,
     drawer,
     createdBug,
@@ -335,7 +377,9 @@ export default {
     taskDialog,
     bugTableDialog,
     schedule,
-    download
+    download,
+    record,
+    timeLine
   },
   filters: {
     ellipsis(value, num) {
@@ -348,6 +392,10 @@ export default {
   },
   data() {
     return {
+      tabPosition: 'first',
+      textarea2: '',
+      HoldTask: '',
+      visible: false, // Hold任务
       showunlock: true,
       ScheduId: '', // 排期ID
       LockState: {}, // 锁定状态
@@ -392,7 +440,7 @@ export default {
   },
   computed: {
     getStatus() {
-      return this.allStatus.find(item => item.code === this.form_query.status) || { msg: null }
+      return this.allStatus.find(item => item.code === this.form_query.status) || { name: null }
     }
   },
   created() {
@@ -410,6 +458,9 @@ export default {
     this.$store.state.data.bizId = false
   },
   methods: {
+    handleClick(tab, event) {
+      console.log(tab, event)
+    },
     async getScheduleGetTaskScheduleHistory() {
       this.scheduleVisble = false
       const res = await scheduleGetTaskScheduleHistory(this.taskId)
@@ -450,6 +501,38 @@ export default {
         this.scheduleVisble = true
       }
     },
+
+    changeBtn() {
+      if (this.HoldTask === 'Hold 任务') {
+        this.visible = !this.visible
+        this.textarea2 = ''
+      }
+      if (this.HoldTask === '解除 Hold') {
+        this.taskHold()
+      }
+    },
+
+    async taskHold(val) { // 锁定Hold
+      if (this.HoldTask === 'Hold 任务') {
+        const res = await taskHold(this.taskId, { 'remark': val })
+        if (res.code === 200) {
+          this.taskGet()
+          this.$refs.timeLine.taskGetWorkFlow()
+          this.visible = false
+          this.$message({ message: '已修改状态为 Hold', type: 'success', duration: 1000, offset: 150 })
+        }
+      }
+      if (this.HoldTask === '解除 Hold') {
+        const res = await taskUnhold(this.taskId)
+        if (res.code === 200) {
+          this.taskGet()
+          this.allStatus = []
+          this.allStatus.push({ code: -2, msg: 'Hold' })
+          this.$refs.timeLine.taskGetWorkFlow()
+          this.$message({ message: 'Hold 状态已解除', type: 'success', duration: 1000, offset: 150 })
+        }
+      }
+    },
     async changeArea(e) { // area修改
       const taskInfoDO = _.cloneDeep(this.form_query)
       const user = {
@@ -478,7 +561,6 @@ export default {
     async getTaskStatus() { // 获取任务状态列表,跟版客户端列表
       const res = await configShowTaskEnum()
       if (res.code === 200) {
-        this.allStatus = res.data.taskStatus
         this.taskScheduleEvent = res.data.taskScheduleEvent || []
         this.appClient = res.data.appClient.map(item => {
           return {
@@ -492,6 +574,7 @@ export default {
       const res = await taskGet(this.$route.query.id)
       if (res.code === 200) {
         this.form_query = res.data
+        this.allStatus = res.data.availableStatusList
       }
     },
     async getCommentList() { // 获取任务评论
@@ -773,4 +856,17 @@ export default {
    border:1px solid #409EFF !important;
    color:#409EFF !important;
 }
+.sign-tabs {
+  padding: 0 30px;
+}
+.el-btn-size {
+   margin: 10px 30px;
+}
+</style>
+
+<style>
+ .el-main-title .el-popper[x-placement^=bottom] {
+    margin: 12px 10px 0 0;
+    width: 300px;
+}
 </style>

+ 6 - 3
src/views/projectManage/version/components/taskList.vue

@@ -70,7 +70,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -128,7 +128,7 @@ import TestReport from '@/views/Platform/presentation/Templates/TestReport' // 
 import DailyReport from '@/views/Platform/presentation/Templates/DailyReport' // 日报
 import ClientReport from '@/views/Platform/presentation/Templates/ClientReport' // 准出
 import { taskUpdate } from '@/api/projectViewDetails'
-import { configShowTaskEnum } from '@/api/taskIndex'
+import { configShowTaskEnum, configShowTaskStatusEnum } from '@/api/taskIndex'
 import scheduleList from './scheduleList'
 import modifySchedule from './modifySchedule'
 import taskDialog from '@/views/projectManage/taskList/dialog/taskDialog' // 任务状态修改(已上线/已提测/已准出)
@@ -208,9 +208,12 @@ export default {
     async getTaskStatus() { // 获取任务状态列表
       const res = await configShowTaskEnum()
       if (res.code === 200) {
-        this.allStatus = res.data.taskStatus
         this.taskScheduleEvent = res.data.taskScheduleEvent || []
       }
+      const res1 = await configShowTaskStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = res1.data.taskStatus
+      }
     },
     changeCheck(val) {
       if (val) {

+ 10 - 5
src/views/workbench/team/components/needsList.vue

@@ -42,7 +42,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -74,7 +74,7 @@ import {
   requirementSelfList,
   requirementTeamList
 } from '@/api/workSchedule'
-import { showRequirementEnum, updateRequirementStatus } from '@/api/requirement'
+import { configShowRequireStatusEnum, updateRequirementStatus } from '@/api/requirement'
 import extraUrgent from '@/assets/extraUrgent.png'
 export default {
   props: {
@@ -139,10 +139,15 @@ export default {
       this.getNeedsList()
     },
     async getTaskStatus() { // 获取需求的所有状态
-      const res = await showRequirementEnum()
-      if (res.code === 200) {
-        this.allStatus = res.data.requirementStatus
+      const res1 = await configShowRequireStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = []
+        this.allStatus = res1.data.requirementStatus
       }
+      // const res = await showRequirementEnum()
+      // if (res.code === 200) {
+      //   this.allStatus = res.data.requirementStatus
+      // }
     },
     async getNeedsList() { // 获取需求列表
       const params = {

+ 6 - 3
src/views/workbench/team/components/taskList.vue

@@ -74,7 +74,7 @@
             size="mini"
             @change="changeStatus(scope.row)"
           >
-            <el-option v-for="item in allStatus" :key="item.code" :label="item.msg" :value="item.code" />
+            <el-option v-for="item in scope.row.availableStatusList" :key="item.code" :label="item.name" :value="item.code" />
           </el-select>
         </template>
       </el-table-column>
@@ -127,7 +127,7 @@ import DailyReport from '@/views/Platform/presentation/Templates/DailyReport' //
 import ClientReport from '@/views/Platform/presentation/Templates/ClientReport' // 准出
 import { taskSelfList, taskTeamList } from '@/api/workSchedule'
 import { taskUpdate } from '@/api/projectViewDetails'
-import { configShowTaskEnum } from '@/api/taskIndex'
+import { configShowTaskEnum, configShowTaskStatusEnum } from '@/api/taskIndex'
 import modifySchedule from '@/views/projectManage/projectList/components/modifySchedule'
 import taskDialog from '@/views/projectManage/taskList/dialog/taskDialog' // 任务状态修改(已上线/已提测/已准出)
 export default {
@@ -235,9 +235,12 @@ export default {
     async getTaskStatus() { // 获取任务状态列表
       const res = await configShowTaskEnum()
       if (res.code === 200) {
-        this.allStatus = res.data.taskStatus
         this.taskScheduleEvent = res.data.taskScheduleEvent || []
       }
+      const res1 = await configShowTaskStatusEnum(localStorage.getItem('bizId'))
+      if (res1.code === 200) {
+        this.allStatus = res1.data.taskStatus
+      }
     },
     changeCheck(val) {
       if (val) {