wenbobowen 4 年之前
父节点
当前提交
0341c3a5f3
共有 34 个文件被更改,包括 929 次插入120 次删除
  1. 10 1
      src/api/common.js
  2. 11 0
      src/api/dataMarket.js
  3. 11 0
      src/api/qualityMonthlyReport/edit.js
  4. 8 0
      src/api/reportTemplate.js
  5. 3 3
      src/assets/css/index.less
  6. 87 0
      src/components/Tag/TagSearch.vue
  7. 252 0
      src/components/Tag/index.vue
  8. 1 1
      src/components/formInput/index.vue
  9. 13 0
      src/components/searchHeader/searchForm.vue
  10. 7 1
      src/components/select/selectCascader.vue
  11. 2 3
      src/components/select/selectCascaderInfo.vue
  12. 3 3
      src/styles/index.less
  13. 37 14
      src/views/dataBigManage/components/dataItem/index.vue
  14. 42 8
      src/views/dataBigManage/components/drawerModal/drawerModalData.js
  15. 117 11
      src/views/dataBigManage/components/drawerModal/index.vue
  16. 43 20
      src/views/dataBigManage/components/qualityModule/index.vue
  17. 36 8
      src/views/monthlyReport/childrenPage/editReport/components/VarText.vue
  18. 2 2
      src/views/monthlyReport/childrenPage/editReport/components/core.vue
  19. 14 1
      src/views/monthlyReport/childrenPage/editReport/components/historyRecord.vue
  20. 23 23
      src/views/monthlyReport/childrenPage/editReport/components/markingIssues.vue
  21. 10 2
      src/views/projectManage/bugList/bugindex.vue
  22. 7 1
      src/views/projectManage/bugList/details/index.vue
  23. 10 1
      src/views/projectManage/onlineproblem/component/header/searchData.js
  24. 4 0
      src/views/projectManage/onlineproblem/create/index.vue
  25. 59 4
      src/views/projectManage/onlineproblem/detial/component/base.vue
  26. 10 0
      src/views/projectManage/projectList/projectIndex.vue
  27. 10 1
      src/views/projectManage/projectList/projectViewDetails.vue
  28. 6 1
      src/views/projectManage/requirement/components/BasicsCode.vue
  29. 9 1
      src/views/projectManage/requirement/list/index.vue
  30. 10 3
      src/views/projectManage/taskList/taskIndex.vue
  31. 15 4
      src/views/projectManage/taskList/taskViewDetail.vue
  32. 19 1
      src/views/reportManagement/ReleaseReport/releaePreview.vue
  33. 19 1
      src/views/reportManagement/Testing/TestingPreview.vue
  34. 19 1
      src/views/reportManagement/daily/dailyPreview.vue

+ 10 - 1
src/api/common.js

@@ -1,5 +1,5 @@
 import request from '@/utils/request'
-import { envWebUrl } from '@/apiConfig/api'
+import { envWebUrl, projectManagementUrl } from '@/apiConfig/api'
 
 export function feedback(data) {
   return request({
@@ -17,3 +17,12 @@ export function uploadImage(data) {
     data
   })
 }
+
+// 标签
+export function taskGetTag(data) {
+  return request({
+    url: projectManagementUrl + '/task/getTag',
+    method: 'post',
+    data
+  })
+}

+ 11 - 0
src/api/dataMarket.js

@@ -117,6 +117,17 @@ export function getEfficiencyDataRequest(data) {
   })
 }
 
+/* S 质量:半浮层数据接口 */
+// 线上问题-改进项半浮层
+export function dataMarketQualityGetImproveList(data) {
+  return request({
+    url: TeamManagement + '/dataMarket/quality/getImproveList',
+    method: 'post',
+    data
+  })
+}
+/* E 质量:半浮层数据接口 */
+
 /* S 效率:半浮层数据接口 */
 // 需求平均交付周期:/dataMarket/efficiency/getRequireAvgData
 export function getRequireAvgData(data) {

+ 11 - 0
src/api/qualityMonthlyReport/edit.js

@@ -264,3 +264,14 @@ export function getAnalyticFeedback(data) {
     data
   })
 }
+
+// 月报回滚
+export function rollback(data) {
+  return request({
+    url: projectManagementUrl + '/monthlyReport/rollback',
+    // url: 'http://127.0.0.1:4523/mock/368525/monthlyReport/getHistoryDetail',
+    method: 'post',
+    timeout: '100000',
+    data
+  })
+}

+ 8 - 0
src/api/reportTemplate.js

@@ -363,3 +363,11 @@ export function getReportList(data) {
   })
 }
 
+// 自动填充人员
+export function dailyReportGetReportReceiver(data) {
+  return request({
+    url: Presentation + `/dailyReport/getReportReceiver`,
+    method: 'post',
+    data
+  })
+}

+ 3 - 3
src/assets/css/index.less

@@ -71,7 +71,7 @@
 
 @font-face {
   font-family: 'iconfont';  /* Project id 2402558 */
-  src: url('//at.alicdn.com/t/font_2402558_538plilwhpo.woff2?t=1620356015326') format('woff2'),
-       url('//at.alicdn.com/t/font_2402558_538plilwhpo.woff?t=1620356015326') format('woff'),
-       url('//at.alicdn.com/t/font_2402558_538plilwhpo.ttf?t=1620356015326') format('truetype');
+  src: url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.woff2?t=1626146634584') format('woff2'),
+       url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.woff?t=1626146634584') format('woff'),
+       url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.ttf?t=1626146634584') format('truetype');
 }

+ 87 - 0
src/components/Tag/TagSearch.vue

@@ -0,0 +1,87 @@
+<template>
+  <el-select
+    ref="select"
+    v-model="tagSelectValue"
+    style="width: 100%"
+    multiple
+    remote
+    size="small"
+    filterable
+    default-first-option
+    :placeholder="placeholder"
+    :remote-method="remoteMethod"
+    @change="change"
+  >
+    <el-option
+      v-for="(item, itemIndex) in options"
+      :key="itemIndex"
+      :label="item.tag"
+      :value="item.id"
+    >
+      {{ item.tag }}
+    </el-option>
+  </el-select>
+</template>
+<script>
+import { taskGetTag } from '@/api/common'
+import { desDecryptId } from '@/utils/crypto-js'
+
+export default {
+  name: 'TagSearch',
+  props: {
+    value: {
+      required: false,
+      type: Array,
+      default: () => []
+    },
+    type: {
+      required: false,
+      type: String,
+      default: () => ''
+    },
+    placeholder: {
+      required: false,
+      type: String,
+      default: () => '请选择标签'
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      isAddTag: true,
+      options: [],
+      tagSelectValue: [],
+      tagValue: []
+    }
+  },
+  mounted() {
+    setTimeout(() => {
+      this.remoteMethod()
+    }, 700)
+  },
+  methods: {
+    change() {
+      this.$emit('input', this.tagSelectValue)
+      this.$emit('change', this.tagSelectValue)
+    },
+    remoteMethod(searchTag = '') {
+      if (!this.$route.query.bizId) return
+      // const bizId_id = analysisBizId_id(this.$route.query.bizId)
+      // const bizId_id = window.localStorage.getItem('bizId')
+      const bizId_id = desDecryptId(this.$route.query.bizId).replace(/_.*/, '')
+      console.log(bizId_id)
+      taskGetTag({
+        type: this.type,
+        searchTag,
+        bizId: bizId_id
+      }).then(res => {
+        if (res.code === 200) {
+          this.options = res.data.map(elm => elm)
+        }
+      })
+    }
+  }
+}
+</script>
+<style scoped lang="less">
+</style>

+ 252 - 0
src/components/Tag/index.vue

@@ -0,0 +1,252 @@
+<template>
+  <div class="tips-wrapper">
+    <span>
+      <el-tag
+        v-for="item in value"
+        :key="item"
+        closable
+        size="small"
+        type="info"
+        @close="() => tabClose(item)">
+          {{ item }}
+        </el-tag>
+    </span>
+    <span class="add-tag" @click="addTag">打标</span>
+
+    <!-- 弹窗 -->
+    <div v-if="visible" class="tag_dialogbox">
+      <div class="box">
+        <div class="title">
+          <span class="line" />
+          <span class="name">设置标签</span>
+          <i class="el-icon-close icon" @click="visible = false" />
+        </div>
+        <div class="body">
+          <div class="tag-from_item">
+            <div class="tag-from__label">标签:</div>
+            <div class="tag-from__content">
+              <el-select
+              ref="select"
+              v-model="tagSelectValue"
+              style="width: 100%;"
+              multiple
+              remote
+              filterable
+              allow-create
+              default-first-option
+              :placeholder="placeholder"
+              :remote-method="remoteMethod"
+              @click.stop
+              @change="change"
+            >
+              <el-option
+                v-for="(item, itemIndex) in options"
+                :key="itemIndex"
+                :label="item"
+                :value="item"
+              >
+                {{ item }}
+              </el-option>
+            </el-select>
+            </div>
+          </div>
+        </div>
+        <div class="footer">
+          <el-button size="small" @click="close">取消</el-button>
+        <el-button size="small" type="primary" @click="ok">确定</el-button>
+        </div>
+      </div>
+      <div class="bg" />
+    </div>
+  </div>
+</template>
+<script>
+import Clickoutside from 'element-ui/src/utils/clickoutside'
+import { desDecryptId } from '@/utils/crypto-js.js'
+import { taskGetTag } from '@/api/common'
+// import Modal from '@/components/modal'
+
+export default {
+  name: 'Tag',
+  // components: { Modal },
+  directives: { Clickoutside },
+  props: {
+    value: {
+      required: false,
+      type: Array,
+      default: () => []
+    },
+    type: {
+      required: false,
+      type: String,
+      default: () => ''
+    },
+    placeholder: {
+      required: false,
+      type: String,
+      default: () => '请选择标签'
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      isAddTag: true,
+      options: [],
+      tagSelectValue: [],
+      tagValue: []
+    }
+  },
+  methods: {
+    change() {
+      this.tagSelectValue = Array.from(new Set(this.tagSelectValue))
+      this.options = Array.from(new Set([...this.options, ...this.tagSelectValue]))
+    },
+    close() {
+      this.visible = false
+    },
+    tabClose(item) {
+      const newTagValue = [...this.value].filter(elm => elm !== item)
+      this.$emit('input', newTagValue)
+      this.$emit('change', newTagValue)
+    },
+    ok() {
+      this.$emit('input', this.tagSelectValue)
+      this.$emit('change', this.tagSelectValue)
+
+      this.close()
+    },
+    addTag() {
+      this.remoteMethod()
+      this.tagSelectValue = Array.from(new Set(this.value))
+      this.options = Array.from(new Set([...this.options, ...this.value]))
+      this.visible = true
+    },
+    remoteMethod(searchTag = '') {
+      if (!this.$route.query.bizId) return
+      // const bizId_id = analysisBizId_id(this.$route.query.bizId)
+      // const bizId_id = window.localStorage.getItem('bizId')
+      const bizId_id = desDecryptId(this.$route.query.bizId).replace(/_.*/, '')
+      console.log(bizId_id)
+      taskGetTag({
+        type: this.type,
+        searchTag,
+        bizId: bizId_id
+      }).then(res => {
+        if (res.code === 200) {
+          this.options = res.data.map(elm => elm.tag)
+        }
+      })
+    }
+  }
+}
+</script>
+<style scoped lang="less">
+.tips-wrapper {
+  display: inline-block;
+}
+
+/deep/ .el-select__tags {
+  max-width: calc(100% - 100px) !important;
+}
+
+.el-tag {
+  margin-left: 5px;
+  user-select: none;
+}
+
+.add-tag {
+  color: #00a0ff;
+  margin-left: 10px;
+  cursor: pointer;
+}
+
+/deep/ .el-select {
+  /deep/ .el-input__inner {
+    border: 1px solid rgba(220, 223, 230) !important;
+  }
+}
+
+.tag_dialogbox {
+  .box {
+    position: fixed;
+    top:20vh;
+    left: calc(50% - 250px);
+    width: 500px;
+    min-height: 180px;
+    // overflow: auto;
+    background: #fff;
+    z-index: 1000;
+    border-radius: 4px;
+    display: flex;
+    flex-direction: column;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
+    .title {
+      padding: 5px 0px 5px 18px;
+      // border-bottom: 1px solid #eee;
+      position: relative;
+      .line {
+        display: inline-block;
+        width: 4px;
+        height: 18px;
+        background: #409eff;
+        border-radius: 1px;
+        vertical-align: text-top;
+      }
+      .name {
+        margin-left: 6px;
+        color: rgba(0,0,0,.85);
+        font-weight: 500;
+        font-size: 16px;
+        line-height: 22px;
+        word-wrap: break-word;
+      }
+      .icon {
+        position: absolute;
+        top: 20px;
+        right: 20px;
+        font-size: 16px;
+        color: #909399;
+        cursor: pointer;
+      }
+    }
+    .body {
+      padding: 15px 20px;
+      color: #606266;
+      font-size: 14px;
+      word-break: break-all;
+      // height: calc(80vh - 118px);
+      overflow: auto;
+      .tag-from .el-form-item__content {
+        width: calc(100% - 100px);
+      }
+      .tag-from_item{
+        display: flex;
+        .tag-from__label {
+          width: 50px;
+        }
+        .tag-from__content {
+          flex: 1;
+          /deep/.el-input__inner {
+            border: 1px solid #c0c4cc!important;
+          }
+        }
+      }
+    }
+    .footer {
+      padding: 0 20px 15px;
+      text-align: right;
+      // border-top: 1px solid #eee;
+      background: #fff;
+    }
+  }
+  .bg {
+    background: rgba(000, 000, 000, 0.5);
+    position: fixed;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    z-index: 100;
+  }
+}
+</style>

+ 1 - 1
src/components/formInput/index.vue

@@ -70,7 +70,7 @@
       v-else-if="type === 'date'"
       v-model="val"
       :size="size"
-      value-format="yyyy-MM-dd hh:mm:ss"
+      value-format="yyyy-MM-dd hh:mm:ss a"
       type="datetime"
       :placeholder="placeholder"
       :style="itemStyles"

+ 13 - 0
src/components/searchHeader/searchForm.vue

@@ -123,6 +123,15 @@
         :tree-data="l.option"
         @change="(e) => $emit('change', l.key, e || '')"
       />
+
+      <TagSearch
+        v-else-if="l.type === 'TagSearch'"
+        v-model="l.value"
+        :type="l.pageType"
+        style="width: 100%"
+         :placeholder="l.placeholder"
+        @change="(e) => $emit('change', l.key, e || '')"
+      />
       <el-input
         v-else
         v-model="l.value"
@@ -138,8 +147,12 @@
 </template>
 <script>
 import moment from 'moment'
+import TagSearch from '@/components/Tag/TagSearch'
 moment.locale('zh-cn')
 export default {
+  components: {
+    TagSearch
+  },
   props: {
     data: {
       type: Array,

+ 7 - 1
src/components/select/selectCascader.vue

@@ -10,6 +10,7 @@
     :style="itemStyles"
     :placeholder="placeholder"
     :remote-method="remoteMethod"
+    :popper-append-to-body="popperAppendToBody"
     @change="changeCascader"
   >
     <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
@@ -63,6 +64,11 @@ export default {
       type: String,
       required: false,
       default: '请选择'
+    },
+    popperAppendToBody: {
+      type: Boolean,
+      required: false,
+      default: true
     }
   },
   data() {
@@ -78,7 +84,7 @@ export default {
   },
   watch: {
     value() {
-      this.init()
+      // this.init()
     },
     selectEnum() {
       this.init()

+ 2 - 3
src/components/select/selectCascaderInfo.vue

@@ -7,11 +7,10 @@
           effect="dark"
           :content="item.deptPath"
           placement="top">
-          <span>{{ item.label }}</span>
+          <span>{{ item.label }}{{ index === itemName.length - 1 ? '' : ',' }}</span>
         </el-tooltip>
-        <span>{{ !(index >= itemName.length - 1) ? ',' : '' }}</span>
       </span>
-    </span>
+    </span><slot />
   </div>
 </template>
 

+ 3 - 3
src/styles/index.less

@@ -206,7 +206,7 @@ div:focus {
 }
 @font-face {
   font-family: 'iconfont';  /* Project id 2402558 */
-  src: url('//at.alicdn.com/t/font_2402558_538plilwhpo.woff2?t=1620356015326') format('woff2'),
-       url('//at.alicdn.com/t/font_2402558_538plilwhpo.woff?t=1620356015326') format('woff'),
-       url('//at.alicdn.com/t/font_2402558_538plilwhpo.ttf?t=1620356015326') format('truetype');
+  src: url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.woff2?t=1626146634584') format('woff2'),
+       url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.woff?t=1626146634584') format('woff'),
+       url('//at.alicdn.com/t/font_2402558_uuubcw5e4b.ttf?t=1626146634584') format('truetype');
 }

+ 37 - 14
src/views/dataBigManage/components/dataItem/index.vue

@@ -1,28 +1,38 @@
 <template>
-  <div v-if="item" class="dataItem flex-center-start" :style="{ background: item.bgColor, padding: item.padding, alignItems: item.type === 'circle' && 'baseline' }">
-    <div v-if="item.type === 'circle'" class="circle" :style="{ background: item.color }" />
-    <addIcon v-else :inner-color="item.innerColor" :out-color="item.outColor" />
-    <div class="textWord ellipsis">
-      <div>
-        <span :style="{color: item.labelColor ? item.labelColor : '#666'}">{{ item.label }}</span>
-        <span class="title isHove">{{ item.title }}</span>
-        <span>{{ item.titleUnit }}</span>
-        <span v-if="item.remark" class="remark">({{ item.remark }})</span>
-      </div>
-      <!-- <div class="ellipsis" v-html="subTitle" /> -->
-      <div class="subTitleName">
+  <div class="dataItem" :style="{ background: item.bgColor, padding: item.padding, alignItems: item.type === 'circle' && 'baseline' }">
+    <div v-if="item" class=" flex-center-start">
+      <div v-if="item.type === 'circle'" class="circle" :style="{ background: item.color }" />
+      <addIcon v-else :inner-color="item.innerColor" :out-color="item.outColor" />
+      <div class="textWord ellipsis">
+        <div>
+          <span :style="{color: item.labelColor ? item.labelColor : '#666'}">{{ item.label }}</span>
+          <span class="title isHove" @click.stop="$emit('subCountListClick', { index: 1, subIndex: 0})">{{ item.title }}</span>
+          <span>{{ item.titleUnit }}</span>
+          <span v-if="item.remark" class="remark">({{ item.remark }})</span>
+        </div>
+        <!-- <div class="ellipsis" v-html="subTitle" /> -->
+        <div class="subTitleName">
         <span v-if="item.subTitleUnit === 'rate'">
           <span>环比:</span>
-          <span :class="Number(item.subTitle) > 0 ? 'item-up isHove' : 'item-down isHove'">
+          <span :class="Number(item.subTitle) > 0 ? 'item-up isHove' : 'item-down isHove'" @click.stop="$emit('subCountListClick', { index: 0, subIndex: 0})">
             <i v-if="Number(item.subTitle) > 0" class="el-icon-caret-top" />
             <i v-else class="el-icon-caret-bottom" />
             {{ item.subTitle }}%
           </span>
         </span>
-        <span v-else style="font-weight: 600" v-html="item.subTitle" />
+          <span v-else style="font-weight: 600" v-html="item.subTitle" />
+        </div>
+      </div>
+    </div>
+    <div v-if="item.subListCountList" class="subCountList">
+      <div v-for="(subItem, subItemIndex) in item.subListCountList" :key="subItemIndex" class="subCountItem" @click.stop="$emit('subCountListClick', { index: subItemIndex + 1, subIndex: 0})">
+          <span v-for="(subItemItem, subItemIndexIndex) in subItem" :key="subItemIndex + subItemIndexIndex">
+            <span>{{ subItemItem.label }}</span><span>{{ subItemItem.beforeLabel }}</span>&nbsp;&nbsp;<span class="isHove title" @click.stop="$emit('subCountListClick', { index: subItemIndex + 1, subIndex: subItemIndexIndex})">{{ subItemItem.countStr }}{{ subItemItem.unit }}</span>&nbsp;&nbsp;<span>{{ subItemItem.afterLabel }}</span>
+          </span>
       </div>
     </div>
   </div>
+
 </template>
 <script>
 import addIcon from '@/components/addIcon'
@@ -79,5 +89,18 @@ export default {
       }
     }
   }
+  .subCountList {
+    font-size: 12px;
+    color: #666;
+    font-weight: 500;
+    margin-top: 10px;
+    .subCountItem {
+      margin-bottom: 1px;
+      cursor: pointer;
+      &:last-child{
+        margin-bottom: 0;
+      }
+    }
+  }
 }
 </style>

+ 42 - 8
src/views/dataBigManage/components/drawerModal/drawerModalData.js

@@ -24,6 +24,33 @@ export const columns = {
       align: 'center'
     }
   ],
+  // 改进项完成率
+  improvementsOverRateColumns: [
+    {
+      label: '改进项',
+      key: 'title',
+      minWidth: 200
+    },
+    {
+      label: '级别',
+      key: 'priority',
+      minWidth: 50
+    },
+    {
+      label: '关联线上问题',
+      key: 'caseName',
+      minWidth: 200,
+      type: 'slot',
+      slotName: 'improvementsOverRateColumnsPrioritySlot'
+    },
+    {
+      label: '状态',
+      key: 'status',
+      minWidth: 100,
+      type: 'slot',
+      slotName: 'improvementsOverRateColumnsStatusSlot'
+    }
+  ],
   // 新增问题
   newQuestionColumns: [
     {
@@ -254,7 +281,7 @@ export const columns = {
         label: '研发周期',
         key: 'title3',
         minWidth: 100,
-        tips: '计算范围内需求, 需求排期里开发、联调、提测、上线类型排期的总周期',
+        tips: '计算范围内需求, 方案设计、需求排期里开发、自测、联调、提测、上线类型排期的总周期',
         align: 'left'
       },
       {
@@ -290,7 +317,7 @@ export const columns = {
         label: '研发周期',
         key: 'title',
         minWidth: 100,
-        tips: '计算范围内需求, 需求排期里开发、联调、提测、上线类型排期的总周期',
+        tips: '计算范围内需求, 方案设计、需求排期里开发、自测、联调、提测、上线类型排期的总周期',
         align: 'left'
       },
       {
@@ -330,7 +357,7 @@ export const columns = {
         label: '研发周期',
         key: 'title2',
         minWidth: 100,
-        tips: '计算范围内任务,任务开发、联调、提测、上线排期的总周期',
+        tips: '计算范围内任务,方案设计、任务开发、自测、联调、提测、上线排期的总周期',
         align: 'center'
       },
       {
@@ -359,7 +386,7 @@ export const columns = {
         label: '研发周期',
         key: 'title',
         minWidth: 100,
-        tips: '计算范围内任务,任务开发、联调、提测、上线排期的总周期',
+        tips: '计算范围内任务,方案设计、任务开发、自测、联调、提测、上线排期的总周期',
         align: 'center'
       },
       {
@@ -399,7 +426,7 @@ export const columns = {
         label: '开发人力',
         key: 'title2',
         minWidth: 100,
-        tips: ' 计算范围内需求,需求开发、联调、提测、上线排期人日总量',
+        tips: ' 计算范围内需求,方案设计、需求开发、自测、联调、提测、上线排期人日总量',
         align: 'center'
       },
       {
@@ -435,7 +462,7 @@ export const columns = {
         label: '开发人力',
         key: 'title',
         minWidth: 100,
-        tips: ' 计算范围内需求,需求开发、联调、提测、上线排期人日总量',
+        tips: ' 计算范围内需求,方案设计、需求开发、自测、联调、提测、上线排期人日总量',
         align: 'center'
       },
       {
@@ -482,7 +509,7 @@ export const columns = {
         label: '开发人力',
         key: 'title2',
         minWidth: 100,
-        tips: '计算范围内任务,开发、联调、提测、上线排期人日总量 / 参与计算的任务总量',
+        tips: '计算范围内任务,方案设计、开发、自测、联调、提测、上线排期人日总量 / 参与计算的任务总量',
         align: 'center'
       },
       {
@@ -518,7 +545,7 @@ export const columns = {
         label: '开发人力',
         key: 'title',
         minWidth: 100,
-        tips: '计算范围内任务,开发、联调、提测、上线排期人日总量 / 参与计算的任务总量',
+        tips: '计算范围内任务,方案设计、开发、自测联调、提测、上线排期人日总量 / 参与计算的任务总量',
         align: 'center'
       },
       {
@@ -688,6 +715,13 @@ export const timelineList = {
   // 回滚次数
   returnCase: [
     '全部', '预发回滚', '全量回滚', '小流量回滚'
+  ],
+  // 改进项
+  ImprovementsOverRate: [
+    '全部', '已完成', '未完成'
+  ],
+  ImprovementsOverRateTwo: [
+    '全部', '逾期', '逾期-高优'
   ]
 }
 

+ 117 - 11
src/views/dataBigManage/components/drawerModal/index.vue

@@ -54,6 +54,18 @@
       <template #bugStatusNameSlot="{scope}">
         <div @click.stop="print(scope)">自定义</div>
       </template>
+      <!-- 改进项:级别 -->
+      <template #improvementsOverRateColumnsPrioritySlot="{scope}">
+        <div v-if="scope.row.caseName" @click.stop>
+          <a target="_blank" :href="scope.row.url">{{ scope.row.caseName }}</a>
+        </div>
+      </template>
+      <!-- 改进项:状态 -->
+      <template #improvementsOverRateColumnsStatusSlot="{scope}">
+        <div v-if="scope.row.status" @click.stop>
+          {{ scope.row.status }} <el-tag v-if="scope.row.tag" size="mini" type="danger" effect="dark">{{ scope.row.tag }}</el-tag>
+        </div>
+      </template>
       <!-- 报告名称 -->
       <template #returnReasonSlot="{scope}">
         <div v-if="scope.row.reportName" @click.stop>
@@ -134,7 +146,8 @@ import {
   bugRepair2Data, // 缺陷平均修复时长
   getStarFlowerList, // 线上问题:半浮层 列表
   getProjectList, // 需求任务项目列表: 半浮层
-  getOdinJobList // 获取上线过程列表
+  getOdinJobList, // 获取上线过程列表
+  dataMarketQualityGetImproveList // 线上问题-改进项半浮层
 } from '@/api/dataMarket'
 
 export default {
@@ -218,7 +231,7 @@ export default {
       console.log(slotProp)
     },
     init() {
-      console.log(this.drawerData, 210)
+      // console.log(this.drawerData, 210)
       this.headerTitle = this.drawerData.headerTitle
       this.title = this.headerTitle.search(/质量|效率/) > -1 ? this.drawerData.label || '' : this.drawerData.activeLabel
       console.log(this.headerTitle, this.title)
@@ -247,7 +260,7 @@ export default {
       const paging = _.cloneDeep(this.paging)
       paging.pageSize = Number.parseInt(pageSize)
       this.paging = _.cloneDeep(paging)
-      console.log(this.paging, 250)
+      // console.log(this.paging, 250)
     },
     // 设置标题
     setTitle() {
@@ -259,6 +272,9 @@ export default {
         if (this.drawerData.label === '准出不通过率') {
           this.title = '准出记录'
         }
+        if (this.drawerData.label === '改进项完成率') {
+          this.title = '改进项'
+        }
         if (this.drawerData.moduleName && this.drawerData.moduleName === '回滚次数') {
           this.title = !this.drawerData.index ? '回滚次数' : this.drawerData[this.drawerData.itemKey].label
         }
@@ -301,7 +317,17 @@ export default {
     // 设置ids
     setIds() {
       if (this.headerTitle === '质量' && !this.drawerData.moduleName) {
-        this.ids = this.drawerData.IdList.map(e => e)
+        if (this.drawerData.label === '改进项完成率') {
+          // if (this.drawerData.hasOwnProperty('subCountItem')) {
+          //   const { subItemList, subIndex } = this.drawerData.subCountItem
+          //   this.ids = subItemList[subIndex].idList.map(e => e)
+          // } else {
+          //   this.ids = this.drawerData.idList.map(e => e)
+          // }
+          this.ids = this.drawerData.IdList.map(e => e)
+        } else {
+          this.ids = this.drawerData.IdList.map(e => e)
+        }
       }
       if (this.drawerData.moduleName && this.drawerData.moduleName === '上线过程') {
         this.ids = this.drawerData.IdList.map(e => e)
@@ -339,6 +365,22 @@ export default {
             first = [...timelineList.returnCase]
             firstActive = this.drawerData.index
           }
+          if (this.title === '改进项') {
+            secondActive = 0
+            firstActive = 0
+            first = [...timelineList.ImprovementsOverRate]
+            second = [...timelineList.ImprovementsOverRateTwo]
+            firstActive = this.drawerData.index
+            if (this.drawerData.hasOwnProperty('subCountItem')) {
+              if (this.drawerData.subCountItem.index === 3) {
+                firstActive = 0
+                secondActive = this.drawerData.subCountItem.subIndex + 1
+              } else {
+                firstActive = this.drawerData.subCountItem.index
+                secondActive = this.drawerData.subCountItem.subIndex
+              }
+            }
+          }
         }
         if (this.headerTitle === '吞吐量') {
           secondActive = 0
@@ -453,6 +495,9 @@ export default {
         // this.ids = this.drawerData[this.drawerData.itemKey].idList.map(e => e)
         this.column = [...columns.test_freeOn_lineRateIdListColumns]
       }
+      if (this.title === '改进项') {
+        this.column = [...columns.improvementsOverRateColumns]
+      }
       if (this.title.search(/新增问题/) > -1) {
         this.column = [...columns.newQuestionColumns]
       }
@@ -520,6 +565,10 @@ export default {
             const items = ['countStr', 'preCountStr', 'fullCountStr', 'lowCountStr']
             this.ids = [...this.drawerData[items[value]].idList]
           }
+          if (this.title === '改进项') {
+            this.ids = this.drawerData.IdList.map(e => e)
+            this.priorityList = value ? [value - 1] : []
+          }
         }
         if (this.headerTitle === '吞吐量') {
           this.timelineData.firstActive = value || 0
@@ -541,8 +590,14 @@ export default {
         this.setTableList()
       }
       if (index === 2) {
-        this.timelineData.secondActive = value || 0
-        this.ids = this.sourceData[value || 0].IdList
+        if (this.title === '改进项') {
+          // console.log(593, value)
+          this.timelineData.secondActive = value
+        } else {
+          this.timelineData.secondActive = value || 0
+          this.ids = this.sourceData[value || 0].IdList
+          // this.setTableList()
+        }
         this.setTableList()
       }
     },
@@ -564,6 +619,11 @@ export default {
             this.setStarFlowerList()
           }
         }
+        if (this.title.search(/改进项/) > -1) {
+          this.loading = true
+          // console.log(624)
+          this.dataMarketQualityGetImproveListFn()
+        }
         if (this.title.search(/提测报告|准出记录/) > -1) {
           if (this.ids.length) {
             this.loading = true
@@ -649,6 +709,52 @@ export default {
         }
       }
     },
+    // 线上问题-改进项半浮层
+    async dataMarketQualityGetImproveListFn() {
+      const paging = { ...this.paging }
+      // const { index, subIndex } = this.drawerData.subCountItem
+      const { firstActive,
+        // first,
+        secondActive
+        // second
+      } = this.timelineData
+      const params = {
+        ...paging,
+        isOverDue: 1,
+        ids: this.drawerData.IdList
+      }
+      if (firstActive) {
+        params.status = timelineList.ImprovementsOverRate[firstActive]
+      }
+      // console.log(722, secondActive)
+      if (secondActive === 2) {
+        params.priority = '高优'
+      }
+
+      if (!firstActive) {
+        delete params.status
+      } else {
+        delete params.isOverDue
+      }
+
+      if (!secondActive) {
+        delete params.isOverDue
+      } else {
+        params.isOverDue = 1
+      }
+
+      // if (index < 2 && subIndex < 1) {
+      //   delete params.isOverDue
+      // }
+      delete params.pageTotal
+      const res = await dataMarketQualityGetImproveList(params)
+      if (res.code === 200) {
+        this.tableList = res.data.result
+        paging.pageTotal = res.data.total
+        this.paging = { ...paging }
+        this.loading = false
+      }
+    },
     // 获取需求
     async getRequirementList() {
       const paging = this.paging
@@ -841,7 +947,7 @@ export default {
         paging.pageTotal = this.tableListOld.length
         paging.pageTotal = this.sourceData[0].IdList.length
         this.paging = { ...paging }
-        console.log(847, this.paging)
+        // console.log(847, this.paging)
         this.loading = false
       }
     },
@@ -963,7 +1069,7 @@ export default {
         this.tableHeight = 'calc(100vh - 136px)'
       }
       if (this.headerTitle === '质量' && this.title.search(/新增缺陷|reopen/) < 0 && !this.drawerData.moduleName) {
-        this.tableHeight = 'calc(100vh - 136px)'
+        this.tableHeight = this.title === '改进项' ? 'calc(100vh - 300px)' : 'calc(100vh - 136px)'
       }
       if (this.headerTitle === '吞吐量') {
         this.tableHeight = 'calc(100vh - 288px)'
@@ -1038,7 +1144,7 @@ export default {
     isTimeLine(index = 1) {
       // 是第一条
       if (index === 1) {
-        if (this.headerTitle.search(/质量/) > -1 && this.title.search(/新增缺陷|reopen/) > -1) {
+        if (this.headerTitle.search(/质量/) > -1 && this.title.search(/新增缺陷|reopen|改进项/) > -1) {
           return true
         }
         if (this.drawerData.moduleName && this.drawerData.moduleName === '上线过程') {
@@ -1047,7 +1153,7 @@ export default {
         if (this.drawerData.moduleName && this.drawerData.moduleName === '回滚次数') {
           return true
         }
-        console.log(1049, this.title, this.headerTitle)
+        // console.log(1049, this.title, this.headerTitle)
         if (this.headerTitle.search(/效率/) > -1 && this.title.search(/需求周期|任务周期|需求人力|任务人力/) > -1) {
           return false
         }
@@ -1055,7 +1161,7 @@ export default {
       }
       // 是第二条
       if (index === 2) {
-        return this.title.search(/缺陷24小时修复率/) > -1
+        return this.title.search(/缺陷24小时修复率|改进项/) > -1
       }
     }
   }

+ 43 - 20
src/views/dataBigManage/components/qualityModule/index.vue

@@ -8,17 +8,35 @@
       <div class="itemBox onlineProblem" style="height: 470px">
         <div class="titleLevel3 mb10">线上问题</div>
         <el-row :gutter="10">
+          <el-col :span="11">
+            <el-row
+              v-for="(item, index) in [...mainData.onlineProblemList || []].filter((item, itemIndex)=> itemIndex !== 2)"
+              :key="index"
+              :span="12"
+              class="mb10"
+            >
+              <div
+                :style="{ cursor: item.label === '新增问题' && 'pointer'}"
+                @click.stop="onDetial('线上问题', item)"
+              >
+                <dataItem :item="item" />
+              </div>
+            </el-row>
+          </el-col>
+          <!-- 20210712日常: http://wiki.intra.xiaojukeji.com/pages/viewpage.action?pageId=629812523 -->
           <el-col
-            v-for="(item, index) in mainData.onlineProblemList"
-            :key="index"
-            :span="12"
+            v-if="mainData.onlineProblemList && mainData.onlineProblemList[2]"
+            :span="13"
             class="mb10"
           >
-            <div
-              :style="{ cursor: item.label == '新增问题' && 'pointer'}"
-              @click.stop="onDetial('线上问题', item)"
-            >
-              <dataItem :item="item" />
+            <div>
+              <dataItem
+                :item="mainData.onlineProblemList[2]"
+                @subCountListClick="(subCountItem) => onDetial('线上问题', {
+                  ...mainData.onlineProblemList[2],
+                  subCountItem
+                })"
+              />
             </div>
           </el-col>
         </el-row>
@@ -245,8 +263,10 @@ export default {
   },
   methods: {
     onDetial(name, item) {
+      console.log(266, name, item)
       if (
         (name === '线上问题' && item.label === '新增问题') ||
+        (name === '线上问题' && item.label === '改进项完成率') ||
         name === '线下质量' ||
         (name === '上线过程' && item.label !== '免测上线率')
       ) {
@@ -297,7 +317,8 @@ export default {
     },
     // 组织线上问题表数据
     getOnlineProblemList() {
-      const { ImprovementsOverRate, depleteUnavailableTime, newOnlineProblems, onlineBreakRate } = this.datas.onlineProblemList
+      const { ImprovementsOverRate, // depleteUnavailableTime,
+        newOnlineProblems, onlineBreakRate } = this.datas.onlineProblemList
       return [
         {
           ...this.redObj,
@@ -308,15 +329,15 @@ export default {
           'subTitle': newOnlineProblems.chainRatio,
           'IdList': newOnlineProblems.idList
         },
-        {
-          ...this.yellowObj,
-          'label': depleteUnavailableTime.label,
-          'title': depleteUnavailableTime.countStr,
-          'titleUnit': '分',
-          'subTitleUnit': 'rate',
-          'subTitle': depleteUnavailableTime.chainRatio,
-          'IdList': depleteUnavailableTime.IdList
-        },
+        // {
+        //   ...this.yellowObj,
+        //   'label': depleteUnavailableTime.label,
+        //   'title': depleteUnavailableTime.countStr,
+        //   'titleUnit': '分',
+        //   'subTitleUnit': 'rate',
+        //   'subTitle': depleteUnavailableTime.chainRatio,
+        //   'IdList': depleteUnavailableTime.IdList
+        // },
         {
           ...this.purpleObj,
           'label': onlineBreakRate.label,
@@ -327,13 +348,15 @@ export default {
           'IdList': onlineBreakRate.IdList
         },
         {
-          ...this.greenObj,
+          ...this.yellowObj,
           'label': ImprovementsOverRate.label,
           'title': ImprovementsOverRate.countStr,
           'titleUnit': '%',
           'subTitleUnit': 'rate',
           'subTitle': ImprovementsOverRate.chainRatio,
-          'IdList': ImprovementsOverRate.IdList
+          'IdList': ImprovementsOverRate.idList,
+          'subCountList': ImprovementsOverRate.subCountList,
+          'subListCountList': ImprovementsOverRate.subListCountList
         }
       ]
     },

+ 36 - 8
src/views/monthlyReport/childrenPage/editReport/components/VarText.vue

@@ -5,29 +5,44 @@
         {{ name }}:
       </div>
       <div class="value">
-        <el-input
-          v-if="pageDate.status < 20 && !isHistory"
-          v-model="textValue"
-          type="textarea"
-          :placeholder="headerTitleType === 'Head2' ? `请针对${headerTitle}进行分析` : '请输入'"
-          show-word-limit
-        />
+        <el-row v-if="pageDate.status < 20 && !isHistory" type="flex" justify="space-between">
+          <el-input
+            v-model="textValue"
+            type="textarea"
+            :placeholder="headerTitleType === 'Head2' ? `请针对${headerTitle}进行分析,建议格式:【1、事实描述:XXXXXX;2、根因分析:XXXXXX;3、建议:XXXXXX。】` : '请输入'"
+            show-word-limit
+          />
+          <el-button
+            style="margin-left: 20px;font-size: 14px;font-weight: 400;"
+            type="text"
+            @click="markingIssues"
+          >
+            标记
+          </el-button>
+        </el-row>
         <span
           v-if="pageDate.status > 10 && textValue || isHistory && textValue"
           style="display: inline-block;line-height: 1.65;background-color: #f7f7f7; padding: 10px; border-radius: 5px;"
           v-html="textValue.replace(/\n/g, '<br />')"
-          />
+        />
       </div>
     </div>
+    <span @click.stop>
+      <markingIssues ref="markingIssues" />
+    </span>
   </div>
 </template>
 
 <script>
 import { mapState } from 'vuex'
+import markingIssues from './markingIssues'
 import _ from 'lodash'
 
 export default {
   name: 'VarText',
+  components: {
+    markingIssues
+  },
   props: {
     title: {
       type: String,
@@ -84,6 +99,16 @@ export default {
   methods: {
     upData() {
       this.$emit('input', this.textValue)
+    },
+    markingIssues() {
+      this.$refs.markingIssues.modalShow = true
+      this.$nextTick(() => {
+        this.$refs.markingIssues.openModal({
+          title: '标记为重点问题',
+          normalAreaName: `${this.value}`,
+          headerTitle: this.headerTitle
+        })
+      })
     }
   }
 }
@@ -93,6 +118,7 @@ export default {
 .fixedText {
   //margin-top: 10px;
   padding-top: 10px;
+
   .top-title {
     width: 100%;
     display: flex;
@@ -126,6 +152,7 @@ export default {
     }
   }
 }
+
 .VarTextV2 {
   padding-top: 10px;
   margin-top: 5px;
@@ -136,6 +163,7 @@ export default {
   //  }
   //}
 }
+
 .fontWeight {
   font-weight: 600;
   padding-top: 10px;

+ 2 - 2
src/views/monthlyReport/childrenPage/editReport/components/core.vue

@@ -74,11 +74,11 @@
         :title="baseData.title"
         :dom-key="baseData.domKey"
         :style="{marginLeft: baseData.title.search(/线上问题/) > -1 ? '5px' : '5px'}" />
-      <span><el-button
+      <!--<span><el-button
         v-if="headerTitle.indexOf('线下缺陷') > -1 && pageDate.status < 20 && !isHistory"
         type="text"
         style="margin-left: 15px;font-weight: 400;"
-        @click.stop="marking">标记</el-button></span>
+        @click.stop="marking">标记</el-button></span>-->
     </div>
     <div
       v-if="baseData.content && baseData.content.length"

+ 14 - 1
src/views/monthlyReport/childrenPage/editReport/components/historyRecord.vue

@@ -21,6 +21,7 @@
                     <div v-if="item.operationTypeStr !== '创建'" class="isHove el-button--text" type="text" @click="showHistory(item)">内容查看</div>
                   </div>
                   <div class="operationTime">{{ item.operationTime }}</div>
+                  <span v-show="false" @click="rollback($route.query.subReportId, item.historyId)">回滚</span>
                 </div>
 
             </div>
@@ -33,7 +34,7 @@
 </template>
 
 <script>
-import { getHistory } from '@/api/qualityMonthlyReport/edit'
+import { getHistory, rollback } from '@/api/qualityMonthlyReport/edit'
 import commentsAndChanges from '@/components/commentsAndChanges/index.vue'
 
 export default {
@@ -97,6 +98,18 @@ export default {
     showHistory(item) {
       const url = `${location.origin}/#/monthlyReport/edit?pageType=read&reportId=334&subReportId=${this.$route.query.subReportId}&isHistory=new&historyId=${item.historyId}`
       window.open(url, '_blank')
+    },
+    rollback(subReportId, historyId) {
+      rollback({
+        subReportId,
+        historyId
+      }).then(res => {
+        if (res.code === 200) {
+          this.$message.success('回滚成功!')
+        } else {
+          this.$message.error(res.msg)
+        }
+      })
     }
   }
 }

+ 23 - 23
src/views/monthlyReport/childrenPage/editReport/components/markingIssues.vue

@@ -1,25 +1,25 @@
 <template>
   <div>
     <normal-dialog
-      v-if="modalShow"
-      ref="normalDialog"
-      v-loading="loading"
-      :show-dialog="false"
-      is-succes
-      :title="titleName"
-      width="45%"
-      @succes="updateModule"
+        v-if="modalShow"
+        ref="normalDialog"
+        v-loading="loading"
+        :show-dialog="false"
+        is-succes
+        :title="titleName"
+        width="45%"
+        @succes="updateModule"
     >
       <el-form :key="domKey" label-width="100px" style="padding-right: 20px;padding-left: 20px">
         <el-form-item label="问题">
           <el-input
-            v-model="normalAreaName"
-            autocomplete="off"
-            size="small"
-            type="textarea"
-            :autosize="{ minRows: 2, maxRows: 30 }"
-            show-word-limit
-            placeholder="请输入问题"
+              v-model="normalAreaName"
+              autocomplete="off"
+              size="small"
+              type="textarea"
+              :autosize="{ minRows: 2, maxRows: 30 }"
+              show-word-limit
+              placeholder="请输入问题"
           />
         </el-form-item>
         <el-form-item label="责任团队">
@@ -27,10 +27,10 @@
         </el-form-item>
         <el-form-item label="责任人">
           <searchPeople
-            ref="searchPeople"
-            style="width: 100%"
-            :value.sync="multiplePeople"
-            :multiple="true"
+              ref="searchPeople"
+              style="width: 100%"
+              :value.sync="multiplePeople"
+              :multiple="true"
           />
         </el-form-item>
       </el-form>
@@ -108,7 +108,7 @@ export default {
       this.subTitles = subTitles
       this.headerTitle = headerTitle
       if (normalAreaName) this.normalAreaName = normalAreaName
-      this.setDefaultValues()
+      // this.setDefaultValues()
       this.loading = false
     },
     // 设置默认值
@@ -166,8 +166,8 @@ export default {
           (elm) => elm.name.search(/模块/) > -1
         )[0]
         this.normalAreaName =
-          this.menuData[headerKeyMK.headerKey] &&
-          `${this.menuData[headerKeyMK.headerKey]}模块一次全量回滚`
+            this.menuData[headerKeyMK.headerKey] &&
+            `${this.menuData[headerKeyMK.headerKey]}模块一次全量回滚`
       }
       if (this.cascaderValue && this.cascaderValue.length) {
         this.setDeptCharge()
@@ -197,7 +197,7 @@ export default {
   }
 }
 </script>
-<style scoped lang='less'>
+<style scoped lang="less">
 /deep/ .el-form-item__label {
   text-align: left !important;
 }

+ 10 - 2
src/views/projectManage/bugList/bugindex.vue

@@ -210,15 +210,20 @@
                         start-placeholder="开始日期"
                         end-placeholder="结束日期"
                         size="small"
+                        style="min-width: 476px"
                         value-format="yyyy-MM-dd HH:mm:ss"
                         :default-time="['00:00:00','23:59:59']"
                         :picker-options="pickerOptions"
                       />
                     </div>
+                    <div class="Layout searchItem">
+                      <div class="queryName">标签</div>
+                      <TagSearch v-model="formInline.tagIdList" type="BUG" placeholder="请选择" @change="query_project(form_all)" />
+                    </div>
                   </div>
                   <div align="right">
                     <el-button type="text" @click="showSaveSearch = true">保存筛选项</el-button>
-                    <el-button type="primary" style="margin: 0 20px;" size="mini" @click="getBugList()">筛 选</el-button>
+                    <el-button type="primary" style="margin: 0 0 0 10px;" size="mini" @click="getBugList()">筛 选</el-button>
                     <el-button size="mini" @click="resetQuery()">重 置</el-button>
                   </div>
                 </el-col>
@@ -284,13 +289,16 @@ import {
   getFilterItem
 } from '@/api/defectManage'
 import '@/views/projectManage/bugList/css/index.css'
+import TagSearch from '@/components/Tag/TagSearch'
+
 export default {
   components: {
     createdBug,
     normalDialog,
     filterList,
     bugTableDialog,
-    searchPeople
+    searchPeople,
+    TagSearch
   },
   data() {
     return {

+ 7 - 1
src/views/projectManage/bugList/details/index.vue

@@ -160,6 +160,9 @@
                   <el-form-item label="Hold次数">
                     <div class="bug_manage_div">{{ bug.holdCount }}</div>
                   </el-form-item>
+                  <el-form-item label="标签">
+                    <Tag v-model="bug.tags" type="BUG" @change="bugUpdate(bug,'details')" />
+                  </el-form-item>
                 </el-form>
               </el-aside>
               <el-aside width="49%">
@@ -575,6 +578,7 @@ import normalArea from '@/components/input/normalArea' // 富文本
 import 'tinymce/plugins/table'// 插入表格插件
 // import 'tinymce/themes/silver/theme'
 // import 'tinymce/icons/default/icons'
+import Tag from '@/components/Tag'
 
 document.body.onpaste = function(event) {
   const data = (event.clipboardData || window.clipboardData)
@@ -598,7 +602,8 @@ export default {
     statusChange,
     searchPeople,
     ElImageViewer,
-    normalArea
+    normalArea,
+    Tag
   },
   props: {
     id: {
@@ -1075,6 +1080,7 @@ export default {
             document.getElementsByClassName('scop')[0].scrollTop = 0
           }
           this.bug = res.data
+          this.bug.tags = res.data.tags || []
           this.bizId = this.bug.bizId
           this.bug.currentHandler = res.data.currentHandler.split(',')
           this.bug.assigner = res.data.assigner.split(',')

+ 10 - 1
src/views/projectManage/onlineproblem/component/header/searchData.js

@@ -77,7 +77,16 @@ const data = {
       placeholder: '请选择创建人',
       value: '',
       option: []
-    }]
+    },
+    {
+      name: '标签',
+      key: 'tagIdList',
+      type: 'TagSearch',
+      pageType: 'ONLINE_PROBLEM',
+      placeholder: '请选择标签',
+      value: []
+    }
+    ]
   ]
 }
 export default data

+ 4 - 0
src/views/projectManage/onlineproblem/create/index.vue

@@ -87,6 +87,10 @@ export default {
       addLinkModelvisible: false
     }
   },
+  mounted() {
+    // 获取部门数据
+    this.$store.dispatch('monthlyReportEdit/setSelectEnum')
+  },
   methods: {
     back() {
       this.$router.push({ name: '线上问题 ', query: { id: 111 }})

+ 59 - 4
src/views/projectManage/onlineproblem/detial/component/base.vue

@@ -58,8 +58,33 @@
           </template>
         </over-click>
       </el-form-item>
-      <el-form-item label="责任团队:">
-        <over-click id="teamId-select" @overMouse="changeArea">
+      <el-form-item label="责任团队:" class="over-context-from">
+        <span v-if="isActive" v-clickoutside="() => {isActive = false; changeArea()}" class="active">
+          <selectTeam
+            v-if="data.teamId"
+            :value="data.teamId"
+            :name="data.teamName"
+            :biz-id-flag="false"
+            @click.stop
+            @onChange="(e) => onChange('teamId', e)"
+          />
+          <span v-else>
+            <selectCascader
+            v-model="data.deptIds"
+            :popper-append-to-body="false"
+            placeholder="请选择责任团队"
+            @click.stop
+            @change="(e) => {onChange('deptIds', e)}"
+          />
+          </span>
+        </span>
+        <span v-else class="overMouse over-context">
+          <selectCascaderInfo v-if="data.deptIds && data.deptIds.length" :key="data.deptIds.length" :team-data="data.deptIds">
+            <i class="el-icon-edit" @click="isActive = !isActive" />
+          </selectCascaderInfo>
+          <span v-else :class="!data.teamName && 'showPlacehodler'">{{ data.teamName ? data.teamName : '请选择' }}<i class="el-icon-edit" @click="isActive = !isActive" /></span>
+        </span>
+        <!-- <over-click id="teamId-select" inline @overMouse="changeArea">
           <template slot="active">
             <selectTeam
               v-if="data.teamId"
@@ -80,7 +105,7 @@
             <selectCascaderInfo v-if="data.deptIds" :key="data.deptIds.length" :team-data="data.deptIds" />
             <span v-else :class="!data.teamName && 'showPlacehodler'">{{ data.teamName ? data.teamName : '请选择' }}</span>
           </template>
-        </over-click>
+        </over-click> -->
       </el-form-item>
       <el-form-item label="影响面:">
         <over-click id="influenceSurface-select" @overMouse="changeArea">
@@ -170,6 +195,9 @@
           </template>
         </over-click>
       </el-form-item>
+      <el-form-item label="标签:" class="tag-from">
+        <Tag v-model="data.tags" type="ONLINE_PROBLEM" @change="(e) => {onChange('tags', e); changeArea()}" />
+      </el-form-item>
     </el-form>
   </div>
 </template>
@@ -178,13 +206,18 @@ import selectTeam from '@/components/select/selectTeam'
 import selectCascader from '@/components/select/selectCascader'
 import selectCascaderInfo from '@/components/select/selectCascaderInfo'
 import overClick from '@/components/click/overClick'
+import Tag from '@/components/Tag'
+import Clickoutside from 'element-ui/src/utils/clickoutside'
+
 export default {
   components: {
     overClick,
     selectTeam,
     selectCascader,
-    selectCascaderInfo
+    selectCascaderInfo,
+    Tag
   },
+  directives: { Clickoutside },
   props: {
     data: {
       type: Object,
@@ -196,6 +229,7 @@ export default {
     return {
       size: 'small',
       form_data: this.data,
+      isActive: false,
       // data: this.data,
       priorityOption: [
         { value: '0', label: 'P0' },
@@ -258,11 +292,32 @@ export default {
         line-height: 18px;
       }
     }
+    .over-context {
+      .el-icon-edit{
+        display: none;
+      }
+    }
+    .over-context:hover{
+      color: #409EFF;
+      .el-icon-edit {
+        display: inline;
+      }
+    }
+    .el-icon-edit {
+      cursor: pointer;
+      padding-right: 5px;
+      color: #409EFF;
+    }
     /deep/.el-form-item__content {
       width: calc(100% - 340px);
       color: #333333;
       font-size: 14px;
     }
+    .over-context-from {
+      /deep/.el-form-item__content {
+        width: calc(100% - 200px);
+      }
+    }
   }
 }
 </style>

+ 10 - 0
src/views/projectManage/projectList/projectIndex.vue

@@ -45,6 +45,7 @@
                 <el-option v-for="item in arr_priority" :key="item.value" :label="item.name" :value="item.value" />
               </el-select>
             </div>
+
           </el-form>
         </div>
         <div class="screen" @click="showSelect">{{ goodName }}</div>
@@ -85,6 +86,10 @@
                 </el-option>
               </el-select>
             </div>
+            <div class="Layout">
+              <div class="queryName marginLeft">标签</div>
+              <TagSearch v-model="form_all.tagIdList" style="width: inherit;" type="PROEJCT" placeholder="请选择" />
+            </div>
           </el-form>
         </div>
         <div align="right" style="padding-top: 1%;">
@@ -329,8 +334,12 @@ import {
   projectCreate
 } from '@/api/projectIndex'
 import '@/views/projectManage/publicCss/index.css'
+import TagSearch from '@/components/Tag/TagSearch'
 
 export default {
+  components: {
+    TagSearch
+  },
   data() {
     return {
       curIndex: 1,
@@ -390,6 +399,7 @@ export default {
       immediate: true
     }
   },
+
   created() {
     this.$store.state.data.status = true
   },

+ 10 - 1
src/views/projectManage/projectList/projectViewDetails.vue

@@ -90,6 +90,9 @@
                   <el-option v-for="item in arr_priority" :key="item.value" :label="item.name" :value="item.value" />
                 </el-select>
               </el-form-item>
+              <el-form-item label="标签:" class="tag-from" style="width: 67%">
+                <Tag v-model="form_query.tags" type="PROEJCT" @change="changeArea" />
+              </el-form-item>
             </el-form>
           </div>
         </section>
@@ -232,6 +235,8 @@ import '@/views/projectManage/projectList/css/index.css'
 import drawer from '@/views/projectManage/Drawer'
 import image_url from '@/assets/home_images/home_u.png'
 import bugTableDialog from '@/views/projectManage/bugList/details/bugTableDialog' // 缺陷表格
+import Tag from '@/components/Tag'
+
 export default {
   components: {
     record,
@@ -247,7 +252,8 @@ export default {
     needsList,
     modifyProject,
     bugTableDialog,
-    commentsAndChanges
+    commentsAndChanges,
+    Tag
   },
   filters: {
     ellipsis(value, num) {
@@ -497,4 +503,7 @@ export default {
 /deep/.el-input--small {
   font-size: 14px;
 }
+.el-form-item__content{
+  width: calc(100% - 100px);
+}
 </style>

+ 6 - 1
src/views/projectManage/requirement/components/BasicsCode.vue

@@ -139,6 +139,9 @@
           </template>
         </over-click>
       </el-form-item>
+      <el-form-item label="标签:" class="tag-from" style="width: 67%">
+        <Tag v-model="form_query.tags" type="REQUIRE" @change="changeArea" />
+      </el-form-item>
     </el-form>
 
     <el-form :model="form_query" class="demo-form-inline" label-position="left" label-width="100px">
@@ -182,11 +185,13 @@ import { mapGetters } from 'vuex'
 import searchPeople from '@/components/select/searchPeople'
 import overClick from '@/components/click/overClick'
 import { updateRequirement, iterationList, showRequirementEnum, settingQueryBizRqmtOrntList, projectListProject } from '@/api/requirement.js'
+import Tag from '@/components/Tag'
 
 export default {
   components: {
     searchPeople,
-    overClick
+    overClick,
+    Tag
   },
   props: {
     value: { type: Object, required: true }

+ 9 - 1
src/views/projectManage/requirement/list/index.vue

@@ -148,6 +148,12 @@
                 />
               </div>
             </el-form>
+            <el-form :model="searchForm" class="flex_start el-from-spacing">
+              <div class="Layout item">
+                <div class="queryName">标签</div>
+                <TagSearch v-model="searchForm.tagIdList" type="REQUIRE" placeholder="请选择" />
+              </div>
+            </el-form>
             <div v-if="DemandStatus === true" style="color:#409EFF; cursor: pointer; margin-top: 15px;" @click="DemandSta"><i class="el-icon-circle-plus-outline" /> 添加需求状态停留时长条件</div>
             <el-divider v-if="DemandStatus === false"> 且 </el-divider>
             <div v-if="DemandStatus === false" style=" margin-top: 15px;">
@@ -338,6 +344,7 @@ import extraUrgent from '@/assets/extraUrgent.png'
 import normalDialog from '@/components/dialog/normalDialog'
 import rqmtStatus from '@/views/projectManage/components/requirementStatus.vue'
 import '@/views/projectManage/publicCss/index.css'
+import TagSearch from '@/components/Tag/TagSearch'
 
 export default {
   components: {
@@ -348,7 +355,8 @@ export default {
     chartView,
     filterModal,
     chartSearchForm,
-    rqmtStatus
+    rqmtStatus,
+    TagSearch
   },
   data() {
     return {

+ 10 - 3
src/views/projectManage/taskList/taskIndex.vue

@@ -272,11 +272,15 @@
                     />
                   </el-select>
                 </div>
+                <div class="Layout item">
+                  <div class="queryName">标签</div>
+                  <TagSearch v-model="form_task.tagIdList" type="TASK" placeholder="请选择" />
+                </div>
               </el-form>
             </div>
             <div class="Layout" style="margin: 15px 0;">
               <el-form :model="form_task" class="flex_start">
-                <div class="Layout item">
+                <div class="Layout" style="width: 100%">
                   <div class="queryName">创建日期</div>
                   <el-date-picker
                     v-model="form_task.date"
@@ -288,7 +292,7 @@
                     end-placeholder="结束日期"
                     size="small"
                     class="input"
-                    style="min-width: 458px"
+                    style="min-width: 476px"
                     value-format="yyyy-MM-dd HH:mm:ss"
                     :default-time="['00:00:00','23:59:59']"
                     :picker-options="pickerOptions"
@@ -509,6 +513,8 @@ import chartSearchFormData from './renderData/chartSearchForm'
 import chartSearchForm from '@/components/searchHeader/searchForm'
 import taskDialog from '@/views/projectManage/taskList/dialog/taskDialog' // 任务状态修改(已上线/已提测/已准出)
 import '@/views/projectManage/publicCss/index.css'
+import TagSearch from '@/components/Tag/TagSearch'
+
 export default {
   components: {
     openDialog,
@@ -518,7 +524,8 @@ export default {
     chartView,
     filterModal,
     chartSearchForm,
-    taskDialog
+    taskDialog,
+    TagSearch
   },
   data() {
     return {

+ 15 - 4
src/views/projectManage/taskList/taskViewDetail.vue

@@ -177,6 +177,9 @@
                   <el-progress :percentage="Number(form_query.rate && form_query.rate.substring(0,4)) || 0" color="#409eff" />
                 </div>
               </el-form-item>
+              <el-form-item label="标签:" class="tag-from">
+                <Tag v-model="form_query.tags" type="TASK" @change="changeArea" />
+              </el-form-item>
             </el-form>
             <el-form :inline="true" :model="form_query" class="demo-form-inline" style="white-space: nowrap;" label-position="left" label-width="100px">
               <el-form-item label="技术文档:" class="module">
@@ -434,7 +437,7 @@ const _ = require('lodash')
 import Vue from 'vue'
 import VueClipboard from 'vue-clipboard2'
 Vue.use(VueClipboard)
-import { mapGetters } from 'vuex'
+// import { mapGetters } from 'vuex'
 // import store from '@/store'
 import { EncryptId, analysisBizId_id } from '@/utils/crypto-js.js'
 import {
@@ -482,6 +485,8 @@ import synchronizeDialog from './dialog/synchronizeDialog' // 同步弹框
 import workflowAndStatus from '@/views/projectManage/components/workflowAndStatus.vue'
 import { formatHMS } from '@/utils/global'
 import testPlan from '@/views/projectManage/components/testPlan.vue'
+import Tag from '@/components/Tag'
+
 export default {
   components: {
     searchPeople,
@@ -507,6 +512,7 @@ export default {
     synchronizeDialog,
     publishTask,
     testPlan,
+    Tag,
     checkListStopConfirm // checklist拦截弹窗
   },
   filters: {
@@ -586,8 +592,8 @@ export default {
   computed: {
     getStatus() {
       return this.allStatus.find(item => item.code === this.form_query.status) || { name: null }
-    },
-    ...mapGetters(['bizId'])
+    }
+    // ...mapGetters(['bizId'])
   },
   watch: {
     activeName: {
@@ -790,6 +796,7 @@ export default {
       const res = await taskGet(this.taskId)
       if (res.code === 200) {
         this.form_query = res.data
+        this.form_query.tags = res.data.tags || []
         if (!this.loaded) {
           this.loaded = true
         }
@@ -1281,5 +1288,9 @@ export default {
   margin: 12px 10px 0 0;
   width: 300px;
 }
-
+.tag-from {
+  .el-form-item__content {
+    width: calc(100% - 100px);
+  }
+}
 </style>

+ 19 - 1
src/views/reportManagement/ReleaseReport/releaePreview.vue

@@ -5,10 +5,16 @@
     <div class="Layout_space_between bottom_mar">
       <div class="div1">收件人</div>
       <searchTeam :value.sync="form.name" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+        <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('name')">&#xe61d;</i>
+      </el-tooltip>
     </div>
     <div class="Layout_space_between report-margin Above">
       <div class="div1">抄送</div>
       <searchTeam :value.sync="form.names" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+       <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('names')">&#xe61d;</i>
+      </el-tooltip>
     </div>
     <span class="backStyle">关联任务</span>
     <div v-for="(item, index) in fromCreateData.taskDetailList" :key="index" class="Layout_flex_start report-taskList">
@@ -96,7 +102,7 @@ import html2canvas from 'html2canvas'
 import searchTeam from '@/components/select/searchTeam'
 import iconDisplay from '@/views/reportManagement/ReleaseReport/components/iconDisplay.vue'
 import moduleCode from '@/views/reportManagement/components/template/nodule.vue'
-import { reportreleaseGetReportById, reportreleaseSendmail } from '@/api/reportTemplate' // 模版添删改查
+import { reportreleaseGetReportById, reportreleaseSendmail, dailyReportGetReportReceiver } from '@/api/reportTemplate' // 模版添删改查
 
 export default {
   name: 'DailyNewsAdded',
@@ -142,6 +148,18 @@ export default {
       }
     },
 
+    async autoGetMember(e) {
+      const res = await dailyReportGetReportReceiver({ reportId: this.fromCreateData.id, type: 4 })
+      if (res.code === 200) {
+        if (this.form[e] !== undefined) {
+          const list = [...this.form[e], ...res.data]
+          this.form[e] = [...new Set(list)]
+        } else {
+          this.$set(this.form, e, [...res.data])
+        }
+      }
+    },
+
     sendReport() {
       if (this.form.name !== undefined) {
         setTimeout(() => {

+ 19 - 1
src/views/reportManagement/Testing/TestingPreview.vue

@@ -5,10 +5,16 @@
     <div class="Layout_space_between" style="margin-bottom: 10px;">
       <div class="div1">收件人</div>
       <searchTeam :value.sync="form.name" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+        <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('name')">&#xe61d;</i>
+      </el-tooltip>
     </div>
     <div class="Layout_space_between">
       <div class="div1">抄送</div>
       <searchTeam :value.sync="form.names" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+       <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('names')">&#xe61d;</i>
+      </el-tooltip>
     </div>
     <span class="backStyle"> 关联任务</span>
     <div v-for="(item, index) in fromCreateData.taskDetailList" :key="index" class="Layout_flex_start report-taskList">
@@ -106,7 +112,7 @@ import { mapGetters } from 'vuex'
 import html2canvas from 'html2canvas'
 import searchTeam from '@/components/select/searchTeam'
 import moduleCode from '@/views/reportManagement/components/template/nodule.vue'
-import { reportdelivertestGetReportById, reportdelivertestSendmail, reportdelivertestGetRequiresByTaskIds } from '@/api/reportTemplate' // 模版添删改查
+import { reportdelivertestGetReportById, reportdelivertestSendmail, reportdelivertestGetRequiresByTaskIds, dailyReportGetReportReceiver } from '@/api/reportTemplate' // 模版添删改查
 
 export default {
   name: 'DailyNewsAdded',
@@ -157,6 +163,18 @@ export default {
       }
     },
 
+    async autoGetMember(e) {
+      const res = await dailyReportGetReportReceiver({ reportId: this.fromCreateData.id, type: 1 })
+      if (res.code === 200) {
+        if (this.form[e] !== undefined) {
+          const list = [...this.form[e], ...res.data]
+          this.form[e] = [...new Set(list)]
+        } else {
+          this.$set(this.form, e, [...res.data])
+        }
+      }
+    },
+
     async reportdelivertestGetRequiresByTaskIds(val) {
       const res2 = await reportdelivertestGetRequiresByTaskIds({ taskIds: val })
       if (res2.code === 200) {

+ 19 - 1
src/views/reportManagement/daily/dailyPreview.vue

@@ -5,10 +5,16 @@
     <div class="Layout_space_between report-div">
       <div class="div1">收件人</div>
       <searchTeam :value.sync="form.name" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+        <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('name')">&#xe61d;</i>
+      </el-tooltip>
     </div>
     <div class="Layout_space_between report-div" style="margin-bottom: 30px;">
       <div class="div1">抄送</div>
       <searchTeam :value.sync="form.names" :clearable="true" :multiple="true" style="width:100%" />
+      <el-tooltip class="item" effect="dark" content="自动填充任务成员" placement="top">
+        <i class="iconfont" style="margin-left: 10px; cursor: pointer;color: #666;" @click="autoGetMember('names')">&#xe61d;</i>
+      </el-tooltip>
     </div>
 
     <span class="backStyle">关联任务</span>
@@ -44,7 +50,7 @@ import { dailyReportSendmail } from '@/api/ResultPage'
 import searchTeam from '@/components/select/searchTeam'
 import iconDisplay from '@/views/reportManagement/daily/components/iconDisplay.vue'
 import moduleCode from '@/views/reportManagement/components/template/nodule.vue'
-import { dailyReportGetV2 } from '@/api/reportTemplate' // 模版添删改查
+import { dailyReportGetV2, dailyReportGetReportReceiver } from '@/api/reportTemplate' // 模版添删改查
 
 export default {
   name: 'DailyNewsAdded',
@@ -95,6 +101,18 @@ export default {
       }
     },
 
+    async autoGetMember(e) {
+      const res = await dailyReportGetReportReceiver({ reportId: this.fromCreateData.id, type: 3 })
+      if (res.code === 200) {
+        if (this.form[e] !== undefined) {
+          const list = [...this.form[e], ...res.data]
+          this.form[e] = [...new Set(list)]
+        } else {
+          this.$set(this.form, e, [...res.data])
+        }
+      }
+    },
+
     sendReport() {
       if (this.form.name !== undefined) {
         setTimeout(() => {