Browse Source

Merge branch 'http_test' into qinzhipeng

qinzhipeng_v@didiglobal.com 4 years ago
parent
commit
7bc9f813b1
43 changed files with 2165 additions and 953 deletions
  1. BIN
      public/favicon-tips.ico
  2. 94 0
      src/api/workSchedule.js
  3. 8 0
      src/apiConfig/requestIP.js
  4. 0 179
      src/excel/Blob.js.js
  5. 0 141
      src/excel/Export2Excel.js
  6. 26 2
      src/layout/components/Sidebar/SidebarItem.vue
  7. 20 1
      src/layout/components/Sidebar/index.vue
  8. 1 1
      src/layout/index.vue
  9. 41 0
      src/mixins/websocket.js
  10. 0 1
      src/router/index.js
  11. 1 1
      src/views/projectManage/projectList/projectIndex.vue
  12. 153 0
      src/views/projectManage/requirement/components/ganntViews.vue
  13. 175 0
      src/views/projectManage/requirement/gannttOptions/requireGannt.js
  14. 1 1
      src/views/projectManage/requirement/list/index.vue
  15. 149 11
      src/views/projectManage/requirement/requirementDetail.vue
  16. 0 1
      src/views/projectManage/taskList/components/reportList.vue
  17. 1 1
      src/views/projectManage/taskList/taskIndex.vue
  18. 7 2
      src/views/quality/components/changeRequireChart.vue
  19. 3 11
      src/views/quality/components/statusChart.vue
  20. 6 3
      src/views/quality/requireStatistics.vue
  21. 6 3
      src/views/quality/taskStatistics.vue
  22. 21 22
      src/views/reportManagement/ReleaseReport/components/iconDisplay.vue
  23. 188 131
      src/views/reportManagement/ReleaseReport/components/releaseDetails.vue
  24. 136 30
      src/views/reportManagement/ReleaseReport/newReleaeTemplate.vue
  25. 72 60
      src/views/reportManagement/ReleaseReport/releaePreview.vue
  26. 11 1
      src/views/reportManagement/Testing/TestingPreview.vue
  27. 11 2
      src/views/reportManagement/Testing/components/deliverDetails.vue
  28. 71 4
      src/views/reportManagement/Testing/newTestingTemplate.vue
  29. 0 1
      src/views/reportManagement/components/DailyReport.vue
  30. 13 13
      src/views/reportManagement/daily/components/iconDisplay.vue
  31. 47 43
      src/views/reportManagement/daily/dailyPreview.vue
  32. 24 30
      src/views/reportManagement/daily/dailyTemplate.vue
  33. 4 3
      src/views/reportManagement/testPresentation.vue
  34. 57 72
      src/views/workbench/bugTableList.vue
  35. 322 0
      src/views/workbench/components/statisticsSection.vue
  36. 42 0
      src/views/workbench/mixins/websocket.js
  37. 9 2
      src/views/workbench/person/components/myFullCalendar.vue
  38. 172 17
      src/views/workbench/person/index.vue
  39. 37 67
      src/views/workbench/team/components/needsList.vue
  40. 8 2
      src/views/workbench/team/components/projectList.vue
  41. 38 69
      src/views/workbench/team/components/taskList.vue
  42. 189 25
      src/views/workbench/team/index.vue
  43. 1 0
      vue.config.js

BIN
public/favicon-tips.ico


+ 94 - 0
src/api/workSchedule.js

@@ -158,3 +158,97 @@ export function taskTeamList(data) {
     data
   })
 }
+// 添加统计后接口----------------------T------------------------------------------
+
+// 需求头部统计数据,数据里返idList(团队)
+export function getTeamRequireSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getRequireSummary`,
+    method: 'post',
+    data
+  })
+}
+// 需求按状态统计数据,数据里返idList(团队)
+export function getTeamRequireDisData(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getRequireDisDataByStatus`,
+    method: 'post',
+    data
+  })
+}
+// 任务头部统计数据,数据里返idList(团队)
+export function getTeamTaskSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getTaskSummary`,
+    method: 'post',
+    data
+  })
+}
+// 任务按状态统计数据,数据里返idList(团队)
+export function getTeamTaskDisData(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getTaskDisDataByStatus`,
+    method: 'post',
+    data
+  })
+}
+// 缺陷头部统计数据,数据里返(团队)
+export function getTeamBugSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getBugSummary`,
+    method: 'post',
+    data
+  })
+}
+// 缺陷按状态统计数据,数据里返idList(团队)
+export function getTeamBugDisDataBy(data) {
+  return request({
+    url: TeamManagement + `/workbench/team/getBugDisDataByStatus`,
+    method: 'post',
+    data
+  })
+}
+
+// 需求头部统计数据,数据里返idList(个人)
+export function getPersonalRequireSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getRequireSummary`,
+    method: 'get'
+  })
+}
+// 需求按状态统计数据,数据里返idList(个人)
+export function getPersonalRequireDisData(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getRequireDisDataByStatus`,
+    method: 'get'
+  })
+}
+// 任务头部统计数据,数据里返idList(个人)
+export function getPersonalTaskSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getTaskSummary`,
+    method: 'get'
+  })
+}
+// 任务按状态统计数据,数据里返idList(个人)
+export function getPersonalTaskDisData(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getTaskDisDataByStatus`,
+    method: 'get'
+  })
+}
+// 缺陷头部统计数据,数据里返(个人)
+export function getPersonalBugSummary(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getBugSummary`,
+    method: 'get'
+  })
+}
+// 缺陷按状态统计数据,数据里返idList(个人)
+export function getPersonalBugDisDataBy(data) {
+  return request({
+    url: TeamManagement + `/workbench/personal/getBugDisDataByStatus`,
+    method: 'post',
+    data
+  })
+}

+ 8 - 0
src/apiConfig/requestIP.js

@@ -15,3 +15,11 @@ if (location.host.indexOf('localhost') < 0) {
     envTag = 'online'
   }
 }
+
+export let ws = '10.78.128.20:10234'
+if (location.host.match(/localhost/) || location.host.match(/zhihui-test/)) {
+  ws = '10.78.128.20:10232'
+} else if (location.host.match(/zhihui-pre/)) {
+  ws = '10.78.128.20:10233'
+}
+

+ 0 - 179
src/excel/Blob.js.js

@@ -1,179 +0,0 @@
-/* eslint-disable */
-/* Blob.js
- * A Blob implementation.
- * 2014-05-27
- *
- * By Eli Grey, http://eligrey.com
- * By Devin Samarin, https://github.com/eboyjr
- * License: X11/MIT
- *   See LICENSE.md
- */
-
-/*global self, unescape */
-/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
- plusplus: true */
-
-/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
-
-(function (view) {
-  "use strict";
-
-  view.URL = view.URL || view.webkitURL;
-
-  if (view.Blob && view.URL) {
-      try {
-          new Blob;
-          return;
-      } catch (e) {}
-  }
-
-  // Internally we use a BlobBuilder implementation to base Blob off of
-  // in order to support older browsers that only have BlobBuilder
-  var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
-          var
-              get_class = function(object) {
-                  return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
-              }
-              , FakeBlobBuilder = function BlobBuilder() {
-                  this.data = [];
-              }
-              , FakeBlob = function Blob(data, type, encoding) {
-                  this.data = data;
-                  this.size = data.length;
-                  this.type = type;
-                  this.encoding = encoding;
-              }
-              , FBB_proto = FakeBlobBuilder.prototype
-              , FB_proto = FakeBlob.prototype
-              , FileReaderSync = view.FileReaderSync
-              , FileException = function(type) {
-                  this.code = this[this.name = type];
-              }
-              , file_ex_codes = (
-                  "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
-                  + "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
-              ).split(" ")
-              , file_ex_code = file_ex_codes.length
-              , real_URL = view.URL || view.webkitURL || view
-              , real_create_object_URL = real_URL.createObjectURL
-              , real_revoke_object_URL = real_URL.revokeObjectURL
-              , URL = real_URL
-              , btoa = view.btoa
-              , atob = view.atob
-
-              , ArrayBuffer = view.ArrayBuffer
-              , Uint8Array = view.Uint8Array
-              ;
-          FakeBlob.fake = FB_proto.fake = true;
-          while (file_ex_code--) {
-              FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
-          }
-          if (!real_URL.createObjectURL) {
-              URL = view.URL = {};
-          }
-          URL.createObjectURL = function(blob) {
-              var
-                  type = blob.type
-                  , data_URI_header
-                  ;
-              if (type === null) {
-                  type = "application/octet-stream";
-              }
-              if (blob instanceof FakeBlob) {
-                  data_URI_header = "data:" + type;
-                  if (blob.encoding === "base64") {
-                      return data_URI_header + ";base64," + blob.data;
-                  } else if (blob.encoding === "URI") {
-                      return data_URI_header + "," + decodeURIComponent(blob.data);
-                  } if (btoa) {
-                      return data_URI_header + ";base64," + btoa(blob.data);
-                  } else {
-                      return data_URI_header + "," + encodeURIComponent(blob.data);
-                  }
-              } else if (real_create_object_URL) {
-                  return real_create_object_URL.call(real_URL, blob);
-              }
-          };
-          URL.revokeObjectURL = function(object_URL) {
-              if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
-                  real_revoke_object_URL.call(real_URL, object_URL);
-              }
-          };
-          FBB_proto.append = function(data/*, endings*/) {
-              var bb = this.data;
-              // decode data to a binary string
-              if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
-                  var
-                      str = ""
-                      , buf = new Uint8Array(data)
-                      , i = 0
-                      , buf_len = buf.length
-                      ;
-                  for (; i < buf_len; i++) {
-                      str += String.fromCharCode(buf[i]);
-                  }
-                  bb.push(str);
-              } else if (get_class(data) === "Blob" || get_class(data) === "File") {
-                  if (FileReaderSync) {
-                      var fr = new FileReaderSync;
-                      bb.push(fr.readAsBinaryString(data));
-                  } else {
-                      // async FileReader won't work as BlobBuilder is sync
-                      throw new FileException("NOT_READABLE_ERR");
-                  }
-              } else if (data instanceof FakeBlob) {
-                  if (data.encoding === "base64" && atob) {
-                      bb.push(atob(data.data));
-                  } else if (data.encoding === "URI") {
-                      bb.push(decodeURIComponent(data.data));
-                  } else if (data.encoding === "raw") {
-                      bb.push(data.data);
-                  }
-              } else {
-                  if (typeof data !== "string") {
-                      data += ""; // convert unsupported types to strings
-                  }
-                  // decode UTF-16 to binary string
-                  bb.push(unescape(encodeURIComponent(data)));
-              }
-          };
-          FBB_proto.getBlob = function(type) {
-              if (!arguments.length) {
-                  type = null;
-              }
-              return new FakeBlob(this.data.join(""), type, "raw");
-          };
-          FBB_proto.toString = function() {
-              return "[object BlobBuilder]";
-          };
-          FB_proto.slice = function(start, end, type) {
-              var args = arguments.length;
-              if (args < 3) {
-                  type = null;
-              }
-              return new FakeBlob(
-                  this.data.slice(start, args > 1 ? end : this.data.length)
-                  , type
-                  , this.encoding
-              );
-          };
-          FB_proto.toString = function() {
-              return "[object Blob]";
-          };
-          FB_proto.close = function() {
-              this.size = this.data.length = 0;
-          };
-          return FakeBlobBuilder;
-      }(view));
-
-  view.Blob = function Blob(blobParts, options) {
-      var type = options ? (options.type || "") : "";
-      var builder = new BlobBuilder();
-      if (blobParts) {
-          for (var i = 0, len = blobParts.length; i < len; i++) {
-              builder.append(blobParts[i]);
-          }
-      }
-      return builder.getBlob(type);
-  };
-}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

+ 0 - 141
src/excel/Export2Excel.js

@@ -1,141 +0,0 @@
-/* eslint-disable */
-require('script-loader!file-saver');
-require('./Blob.js');
-require('script-loader!xlsx/dist/xlsx.core.min');
-function generateArray(table) {
-    var out = [];
-    var rows = table.querySelectorAll('tr');
-    var ranges = [];
-    for (var R = 0; R < rows.length; ++R) {
-        var outRow = [];
-        var row = rows[R];
-        var columns = row.querySelectorAll('td');
-        for (var C = 0; C < columns.length; ++C) {
-            var cell = columns[C];
-            var colspan = cell.getAttribute('colspan');
-            var rowspan = cell.getAttribute('rowspan');
-            var cellValue = cell.innerText;
-            if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
-
-            //Skip ranges
-            ranges.forEach(function (range) {
-                if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
-                    for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
-                }
-            });
-
-            //Handle Row Span
-            if (rowspan || colspan) {
-                rowspan = rowspan || 1;
-                colspan = colspan || 1;
-                ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
-            }
-            ;
-
-            //Handle Value
-            outRow.push(cellValue !== "" ? cellValue : null);
-
-            //Handle Colspan
-            if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
-        }
-        out.push(outRow);
-    }
-    return [out, ranges];
-};
-
-function datenum(v, date1904) {
-    if (date1904) v += 1462;
-    var epoch = Date.parse(v);
-    return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
-}
-
-function sheet_from_array_of_arrays(data, opts) {
-    var ws = {};
-    var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
-    for (var R = 0; R != data.length; ++R) {
-        for (var C = 0; C != data[R].length; ++C) {
-            if (range.s.r > R) range.s.r = R;
-            if (range.s.c > C) range.s.c = C;
-            if (range.e.r < R) range.e.r = R;
-            if (range.e.c < C) range.e.c = C;
-            var cell = {v: data[R][C]};
-            if (cell.v == null) continue;
-            var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
-
-            if (typeof cell.v === 'number') cell.t = 'n';
-            else if (typeof cell.v === 'boolean') cell.t = 'b';
-            else if (cell.v instanceof Date) {
-                cell.t = 'n';
-                cell.z = XLSX.SSF._table[14];
-                cell.v = datenum(cell.v);
-            }
-            else cell.t = 's';
-
-            ws[cell_ref] = cell;
-        }
-    }
-    if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
-    return ws;
-}
-
-function Workbook() {
-    if (!(this instanceof Workbook)) return new Workbook();
-    this.SheetNames = [];
-    this.Sheets = {};
-}
-
-function s2ab(s) {
-    var buf = new ArrayBuffer(s.length);
-    var view = new Uint8Array(buf);
-    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
-    return buf;
-}
-
-export function export_table_to_excel(id) {
-    var theTable = document.getElementById(id);
-    console.log('a')
-    var oo = generateArray(theTable);
-    var ranges = oo[1];
-
-    /* original data */
-    var data = oo[0];
-    var ws_name = "SheetJS";
-    console.log(data);
-
-    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
-
-    /* add ranges to worksheet */
-    // ws['!cols'] = ['apple', 'banan'];
-    ws['!merges'] = ranges;
-
-    /* add worksheet to workbook */
-    wb.SheetNames.push(ws_name);
-    wb.Sheets[ws_name] = ws;
-
-    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
-
-    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
-}
-
-function formatJson(jsonData) {
-    console.log(jsonData)
-}
-export function export_json_to_excel(th, jsonData, defaultTitle) {
-
-    /* original data */
-
-    var data = jsonData;
-    data.unshift(th);
-    var ws_name = "SheetJS";
-
-    var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
-
-
-    /* add worksheet to workbook */
-    wb.SheetNames.push(ws_name);
-    wb.Sheets[ws_name] = ws;
-
-    var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
-    var title = defaultTitle || '列表'
-    saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
-}

+ 26 - 2
src/layout/components/Sidebar/SidebarItem.vue

@@ -4,10 +4,10 @@
       <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
         <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
           <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
+          <div v-show="onlyOneChild.meta.title === '个人工作台' && showTips" class="if-notice" />
         </el-menu-item>
       </app-link>
     </template>
-
     <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
       <template slot="title">
         <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
@@ -48,13 +48,25 @@ export default {
     basePath: {
       type: String,
       default: ''
+    },
+    showTips: {
+      type: Boolean,
+      default: false,
+      required: false
     }
   },
   data() {
     // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
     // TODO: refactor with render function
     this.onlyOneChild = null
-    return {}
+    return {
+    }
+  },
+  watch: {
+    showTips: {
+      handler(newV) {},
+      immediate: true
+    }
   },
   methods: {
     hasOneShowingChild(children = [], parent) {
@@ -93,3 +105,15 @@ export default {
   }
 }
 </script>
+<style lang="scss" scoped>
+.if-notice {
+  position: absolute;
+  height: 8px;
+  width: 8px;
+  background-color: #E02020;
+  border-radius: 50%;
+  top: 43%;
+  right: 37%;
+  transform: translateY(-50%);
+}
+</style>

+ 20 - 1
src/layout/components/Sidebar/index.vue

@@ -13,7 +13,7 @@
           :collapse-transition="false"
           mode="vertical"
         >
-          <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
+          <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" :show-tips="showTips" />
         </el-menu>
       </el-scrollbar>
     </el-main>
@@ -31,9 +31,16 @@ import SidebarItem from './SidebarItem'
 import variables from '@/styles/variables.scss'
 import { logoutUrl } from '@/apiConfig/requestIP.js'
 import Hamburger from '@/components/Hamburger'
+import websocket from '@/views/workbench/mixins/websocket'
 
 export default {
   components: { SidebarItem, Logo, Hamburger },
+  mixins: [websocket],
+  data() {
+    return {
+      showTips: false
+    }
+  },
   computed: {
     ...mapGetters([
       'sidebar',
@@ -67,6 +74,18 @@ export default {
     },
     Logout() {
       location.href = logoutUrl
+    },
+    websocketonmessage(e) { // websocket数据接收
+      const { hasReminding } = JSON.parse(e.data)
+      if (hasReminding) {
+        this.showTips = true
+        const link = document.querySelector('link')
+        link.href = link.href.replace(/favicon.ico/, 'favicon-tips.ico')
+      } else {
+        this.showTips = false
+        const link = document.querySelector('link')
+        link.href = link.href.replace(/favicon-tips.ico/, 'favicon.ico')
+      }
     }
   }
 }

+ 1 - 1
src/layout/index.vue

@@ -83,7 +83,7 @@ export default {
     position: fixed;
     top: 0;
     right: 0;
-    z-index: 9;
+    z-index: 999;
     width: calc(100% - #{$sideBarWidth});
     transition: width 0.28s;
   }

+ 41 - 0
src/mixins/websocket.js

@@ -0,0 +1,41 @@
+export default {
+  name: 'test',
+  data() {
+    return {
+      websocketUrl: '',
+      websocket: null,
+      requestData: null,
+      responseData: null
+    }
+  },
+  created() {
+    this.initWebSocket()
+  },
+  destroyed() {
+    this.websocketClose() // 离开路由之后断开websocket连接
+  },
+  methods: {
+    initWebSocket() { // websocket
+      this.websocket = new WebSocket(this.websocketUrl)
+      this.websocket.onmessage = this.websocketOnmessage
+      this.websocket.onopen = this.websocketOnopen
+      this.websocket.onerror = this.websocketOnerror
+      this.websocket.onclose = this.websocketClose
+    },
+    websocketOnopen() { // 连接建立之后执行send方法发送数据
+      this.websocketSend(this.requestData)
+    },
+    websocketOnerror() { // 连接失败重连
+      this.initWebSocket()
+    },
+    websocketOnmessage(e) { // 数据接收
+      this.responseData = e.data
+    },
+    websocketSend(Data) { // 数据发送
+      this.websocket.send(this.requestData)
+    },
+    websocketClose(e) { // 关闭
+      this.websocket.close()
+    }
+  }
+}

+ 0 - 1
src/router/index.js

@@ -635,7 +635,6 @@ const createRouter = () => new Router({
 
 const router = createRouter()
 router.beforeEach((to, from, next) => {
-  console.log(this)
   if (to.name !== '接口管理') {
     next()
   } else {

+ 1 - 1
src/views/projectManage/projectList/projectIndex.vue

@@ -337,7 +337,7 @@ import '@/views/projectManage/publicCss/index.css'
 export default {
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       curIndex: 1,
       pageSize: 15,
       activeColor: 'red',

+ 153 - 0
src/views/projectManage/requirement/components/ganntViews.vue

@@ -0,0 +1,153 @@
+<template>
+  <div class="ganntt-parent">
+    <gantt-elastic
+      v-if="ganttElastic"
+      :options="options"
+      :tasks="tasks"
+    >
+      <gantt-header slot="header" />
+    </gantt-elastic>
+  </div>
+</template>
+
+<script>
+import GanttElastic from 'gantt-elastic'
+import GanttHeader from 'gantt-elastic-header'
+import teamGanttOptions from './../gannttOptions/requireGannt'
+import { listByRequire } from '@/api/requirement.js'
+import moment from 'moment'
+
+export default {
+  components: {
+    GanttElastic,
+    GanttHeader
+  },
+  data() {
+    return {
+      ganttElastic: false,
+      tasks: [],
+      scheduleDetail: [],
+      options: teamGanttOptions, // 甘特图配置
+      dynamicStyle: {},
+      mun: {}
+    }
+  },
+  mounted() {
+    this.listByRequire()
+  },
+  methods: {
+    async listByRequire() { // 获取排期列表
+      const res = await listByRequire(Number(this.$route.query.id))
+      if (res.code === 200) {
+        this.mun = res.data.scheduleDetailRespons
+        this.handleData(res.data.taskDetailList)
+        this.scheduleDetailRespons(res.data.scheduleDetailRespons)
+      }
+    },
+    handleData(data) {
+      this.ganttElastic = false
+      this.tasks = []
+      this.tasks = data.map((value, key) => {
+        return this.handleItem(value, key)
+      })
+      this.ganttElastic = true
+      this.$nextTick(() => {
+        this.setOption()
+      })
+    },
+
+    handleItem(item, key) {
+      let data = ''
+      for (const key in this.mun) {
+        if (Number(key) === item.id) {
+          data = this.mun[key].length
+        }
+      }
+      const colorlist = ['#A1DEFF', '#FAB5B5', '#BCED86', '#FFA87F', '#8E44AD', '#1EBC61', '#0287D0']
+      return {
+        id: item.id,
+        taskName: item.name,
+        label: item.name,
+        belongModules: item.moduleInfoName || '',
+        devPerson: item.rdObject.name || '',
+        testPerson: item.qaObject.name || '',
+        description: data + '个排期',
+        startDate: item.optionsObject.startTime ? moment(item.optionsObject.startTime).format('YYYY-MM-DD') : '',
+        endDate: item.optionsObject.endTime ? moment(item.optionsObject.endTime).format('YYYY-MM-DD') : '',
+        needLegalAllDays: item.optionsObject.workDays + '/' + item.optionsObject.days,
+        type: 'task',
+        percent: 0,
+        html: true,
+        start: moment(item.optionsObject.startTime).toDate().getTime(),
+        duration: moment(item.optionsObject.endTime).toDate().getTime() - moment(item.optionsObject.startTime).toDate().getTime(),
+        style: {
+          base: {
+            fill: colorlist[key % colorlist.length]
+          }
+        }
+      }
+    },
+
+    scheduleDetailRespons(data) { // 处理排期
+      const colorlist = ['#A1DEFF', '#FAB5B5', '#BCED86', '#FFA87F', '#8E44AD', '#1EBC61', '#0287D0']
+      for (const key in data) {
+        const arr = data[key]
+        for (const vel in arr) {
+          const ds = {
+            id: arr[vel].id,
+            taskName: '',
+            label: arr[vel].desc ? arr[vel].name + '-' + arr[vel].desc : arr[vel].name,
+            description: arr[vel].desc ? arr[vel].name + '-' + arr[vel].desc : arr[vel].name,
+            startDate: arr[vel].dayList[0],
+            endDate: arr[vel].dayList[arr[vel].dayList.length - 1],
+            needLegalAllDays: arr[vel].dayLength + '/' + arr[vel].days,
+            type: 'task',
+            percent: 0,
+            html: true,
+            parentId: Number(key),
+            start: moment(arr[vel].dayList[0]).toDate().getTime(),
+            duration: moment(arr[vel].dayList[arr[vel].dayList.length - 1]).toDate().getTime() - moment(arr[vel].dayList[0]).toDate().getTime(),
+            style: {
+              base: {
+                fill: colorlist[key % colorlist.length]
+              }
+            }
+          }
+          this.scheduleDetail.push(ds)
+        }
+      }
+      this.tasks = this.tasks.concat(this.scheduleDetail)
+    },
+
+    setOption() {
+      const node = document.querySelectorAll('.gantt-elastic__header-label')
+      node[0].style = 'display: none'
+      node[1].style = 'display: none'
+      node[2].style = 'display: none'
+      node[3].removeChild(node[3].childNodes[0])
+      const span = document.createElement('span')
+      span.innerText = '列表区域 :'
+      node[3].insertBefore(span, node[3].childNodes[0])
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.ganntt-parent {
+  margin: 0 20px 20px;
+  >>>.gantt-elastic__header {
+    display: block !important;
+    background: #FFF !important;
+  }
+  >>>.gantt-elastic__header-title {
+    display: none;
+  }
+ >>>.gantt-elastic__header-btn-recenter {
+    display: none;
+ }
+ >>>.gantt-elastic__header-task-list-switch--wrapper {
+    display: none;
+ }
+}
+</style>

+ 175 - 0
src/views/projectManage/requirement/gannttOptions/requireGannt.js

@@ -0,0 +1,175 @@
+const options = {
+  taskMapping: {
+    progress: 'percent'
+  },
+  maxRows: 100,
+  maxHeight: 460,
+  title: {
+    label: 'Your project title as html (link or whatever...)',
+    html: false
+  },
+  row: {
+    height: 24
+  },
+  calendar: {
+    hour: {
+      display: true
+    }
+  },
+  chart: {
+    progress: {
+      bar: false
+    },
+    expander: {
+      display: true
+    }
+  },
+  taskList: {
+    expander: {
+      straight: false
+    },
+    columns: [
+      {
+        id: 1,
+        label: '任务名称',
+        value: 'taskName',
+        width: 150,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 2,
+        label: '所属模块',
+        value: 'belongModules',
+        width: 100,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 3,
+        label: '开发负责人',
+        value: 'devPerson',
+        width: 80,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 4,
+        label: '测试负责人',
+        value: 'testPerson',
+        width: 80,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 5,
+        label: '排期类型及描述',
+        value: 'description',
+        width: 180,
+        expander: true,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 6,
+        label: '开始时间',
+        value: 'startDate',
+        width: 90,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 7,
+        label: '结束时间',
+        value: 'endDate',
+        width: 90,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      },
+      {
+        id: 8,
+        label: '时长/周期',
+        value: 'needLegalAllDays',
+        width: 130,
+        style: {
+          'task-list-header-label': {
+            'text-align': 'center',
+            width: '100%'
+          },
+          'task-list-item-value-container': {
+            'text-align': 'center',
+            width: '100%'
+          }
+        }
+      }
+    ]
+  },
+  locale: {
+    Now: 'Now',
+    'X-Scale': 'Zoom-X',
+    'Y-Scale': 'Zoom-Y',
+    'Task list width': 'Task1 list',
+    'Before/After': 'Expand',
+    'Display task list': 'Task list',
+    name: 'zh_cn',
+    weekdays: ['周天', '周一', '周二', '周三', '周四', '周五', '周六'],
+    months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月']
+  }
+} // 甘特图配置
+export default options

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

@@ -227,7 +227,7 @@ export default {
   },
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       DemandStatus: true, // 筛选需求时长
       extraUrgent: extraUrgent,
       priorityColors: ['#F56C6C', '#FF8952', '#F5E300', '#7ED321', '#61D3B8', '#69B3FF', '#BDBDBD'],

+ 149 - 11
src/views/projectManage/requirement/requirementDetail.vue

@@ -176,8 +176,11 @@
           </div>
           <section class="main-section">
             <div class="allTips">
-              <div class="tips"><i class="el-icon-warning-outline" /> 每个任务仅支持一次提测和一次准出,请合理拆解后任务再排期</div><br>
-              <div class="allTips">
+              <el-radio-group v-model="listOrGannt" size="small" style="margin-left: 10px">
+                <el-radio-button label="列表" />
+                <el-radio-button label="甘特图" />
+              </el-radio-group>
+              <div v-show="listOrGannt === '列表'" class="allTips">
                 <div v-if="BackToTheLatest" class="Scheduling" @click="GetRequireScheduleHistory"><i class="el-icon-refresh" /> 回到最新</div>
                 <div v-if="Latest" align="left" class="Scheduling" @click="scheduleHiHide"><div class="el-icon-document" /> 排期变更记录</div>
                 <download :id="requirementId" :name="'需求'" />
@@ -185,7 +188,7 @@
             </div>
           </section>
 
-          <el-container>
+          <el-container v-show="listOrGannt === '列表'" class="allTips">
             <el-main style="padding: 0;">
               <!-- <schedule-list :id="requirementId" ref="ScheduleEvent" :showunlock="showunlock" :type-list="taskScheduleEvent" :required-list="taskScheduleList" class-name="white" :all="true" :no-move="false" /> -->
               <demand :id="requirementId" ref="ScheduleEvent" :showunlock="showunlock" :type-list="taskScheduleEvent" :required-list="taskScheduleList" />
@@ -207,6 +210,36 @@
               <div v-if="SchedulingContent.length === 0" style="width: 270px; margin: 50% 20px; text-align: center;"> 暂无排期变更记录!</div>
             </el-aside>
           </el-container>
+          <gannt-views v-if="listOrGannt === '甘特图'" />
+          <div class="detail-info border-top">
+            <el-divider />
+            <el-form ref="form_query" :inline="true" :model="form_query" class="Layout_space_start" label-position="left" label-width="140px">
+              <el-form-item
+                v-if="brdPassRealTime"
+                label="BRD评审通过时间:"
+              >
+                <el-date-picker v-model="form_query.brdPassRealTime" type="date" :clearable="false" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" size="small" @change="setChangeArea" />
+              </el-form-item>
+              <el-form-item
+                v-if="prdPassRealTime"
+                label="PRD评审通过时间:"
+              >
+                <el-date-picker v-model="form_query.prdPassRealTime" type="date" :clearable="false" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" size="small" @change="setChangeArea" />
+              </el-form-item>
+              <el-form-item
+                v-if="techInRealTime"
+                label="技术准入时间:"
+              >
+                <el-date-picker v-model="form_query.techInRealTime" type="date" :clearable="false" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" size="small" @change="setChangeArea" />
+              </el-form-item>
+              <el-form-item
+                v-if="onlineRealTime"
+                label="实际上线时间:"
+              >
+                <el-date-picker v-model="form_query.onlineRealTime" type="date" :clearable="false" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" size="small" @change="setChangeArea" />
+              </el-form-item>
+            </el-form>
+          </div>
         </section>
         <section class="main-section">
           <div class="el-main-title">
@@ -303,6 +336,34 @@
         @childValInput="childVal"
         @click.stop
       />
+      <el-dialog
+        title="状态变更"
+        :visible.sync="dialogStatusVisible"
+        width="30%"
+        class="public_task"
+      >
+        <div class="blueStripe" />
+        <div align="center">
+          <el-form ref="form_query" :inline="true" :model="form_query" :rules="rules" label-position="left" label-width="158px">
+            <el-form-item v-if="statusName === 'BRD评审通过'" :label="statusName + '时间:'" prop="brdPassRealTime">
+              <el-date-picker v-model="form_query.brdPassRealTime" type="date" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" />
+            </el-form-item>
+            <el-form-item v-if="statusName === 'PRD评审通过'" :label="statusName + '时间:'" prop="prdPassRealTime">
+              <el-date-picker v-model="form_query.prdPassRealTime" type="date" :clearable="false" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" />
+            </el-form-item>
+            <el-form-item v-if="statusName === '技术准入'" :label="statusName + '时间:'" prop="techInRealTime">
+              <el-date-picker v-model="form_query.techInRealTime" type="date" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" />
+            </el-form-item>
+            <el-form-item v-if="statusName === '已上线'" :label="statusName + '时间:'" prop="onlineRealTime">
+              <el-date-picker v-model="form_query.onlineRealTime" type="date" placeholder="请选择" format="yyyy.MM.dd" value-format="yyyy.MM.dd" style="width: 100%;" />
+            </el-form-item>
+          </el-form>
+        </div>
+        <span slot="footer" class="dialog-footer">
+          <el-button @click="dialogStatusVisible = false">取 消</el-button>
+          <el-button type="primary" @click="setChangeArea">确 定</el-button>
+        </span>
+      </el-dialog>
     </el-container>
   </div>
 </template>
@@ -330,6 +391,7 @@ import {
   // scheduleGetHistoryScheduleById,
   settingQueryBizRqmtOrntList
 } from '@/api/requirement.js'
+import { projectGetMemberList } from '@/api/drewer'
 import { configShowTaskEnum } from '@/api/taskIndex'
 import searchPeople from '@/components/select/searchPeople'
 import textArea from '@/components/input/textArea'
@@ -340,6 +402,7 @@ import image_url from '@/assets/home_images/home_u.png'
 import createdBug from '@/views/projectManage/bugList/file/createdBug'
 import tasksList from './components/taskList'
 import dataStatistics from './components/dataStatistics'
+import moment from 'moment'
 // import scheduleList from './components/scheduleList'
 import bugTableDialog from '@/views/projectManage/bugList/details/bugTableDialog' // 缺陷表格
 import schedule from '@/views/projectManage/schedule' // 排期锁定弹窗
@@ -349,6 +412,7 @@ import demand from '@/views/projectManage/components/demand.vue'
 import '@/styles/PublicStyle/index.scss'
 import record from '@/views/projectManage/components/record.vue'
 import timeLine from '@/views/projectManage/components/timeLine.vue'
+import ganntViews from './components/ganntViews'
 export default {
   components: {
     searchPeople,
@@ -365,7 +429,8 @@ export default {
     download,
     record,
     timeLine,
-    demand
+    demand,
+    ganntViews
   },
   filters: {
     ellipsis(value, num) {
@@ -392,8 +457,21 @@ export default {
         children: 'childRqmtOrnts',
         multiple: true
       },
+      rules: {
+        brdPassRealTime: [{ required: true, message: '请输入BRD评审通过时间', trigger: 'change' }],
+        prdPassRealTime: [{ required: true, message: '请输入PRD评审通过时间', trigger: 'change' }],
+        techInRealTime: [{ required: true, message: '请输入技术准入时间', trigger: 'change' }],
+        onlineRealTime: [{ required: true, message: '请输入实际上线时间', trigger: 'change' }]
+      },
       Latest: true,
+      statusName: '',
+      statusValue: '',
+      dialogStatusVisible: false,
       demandDirection: [], // 需求方向option
+      brdPassRealTime: false, // BRD评审通过时间
+      prdPassRealTime: false, // PRD评审通过时间
+      techInRealTime: false, // 技术准入
+      onlineRealTime: false, // 实际上线
       optionName: 'first',
       visible: false, // Hold任务
       ScheduId: '', // 排期ID
@@ -427,7 +505,8 @@ export default {
       taskScheduleList: [], // 排期数据
       lockHide: false, // 隐藏排期变更记录
       isScheduleLocked: '', // 锁定状态1锁定0未锁定
-      SchedulingContent: [] // 排期历史变更记录
+      SchedulingContent: [], // 排期历史变更记录
+      listOrGannt: '列表'
     }
   },
   computed: {
@@ -474,12 +553,19 @@ export default {
       this.taskScheduleList = res.data
       this.BackToTheLatest = true
     },
-    changeSchedule() { // 修改锁定状态
+    async changeSchedule() { // 修改锁定状态
       if (this.isScheduleLocked === 1) {
-        if (this.form_query.pmMemberInfoResponse.idap === localStorage.getItem('username')) {
-          this.scheduleVisble = true
-        } else {
-          this.$message({ message: '没有权限,请联系PM执行解锁!', type: 'error', duration: 1000, offset: 150 })
+        const res = await projectGetMemberList({ projectId: this.form_query.belongingProject, requireId: this.requirementId })
+        if (res.code === 200) {
+          const data = res.data.PM
+          data.map(item => {
+            if (item.memberInfoResponse.idap === localStorage.getItem('username')) {
+              this.scheduleVisble = true
+            }
+          })
+          if (!this.scheduleVisble) {
+            this.$message({ message: '没有权限,请联系PM执行解锁!', type: 'error', duration: 2000, offset: 150 })
+          }
         }
       } else {
         this.scheduleVisble = true
@@ -488,6 +574,15 @@ export default {
     // clickBackToTheLatest() {
     //   this.$refs.ScheduleEvent.rowDrop()
     // },
+    setChangeArea() {
+      this.$refs.form_query.validate((valid) => {
+        if (valid) {
+          this.changeArea()
+        } else {
+          this.$message({ message: '还有必填项未填写', type: 'error', duration: 1000, offset: 150 })
+        }
+      })
+    },
     async changeArea(e) { // area修改
       const requirementInfo = _.cloneDeep(this.form_query)
       requirementInfo.rqmtProposer = requirementInfo.rqmtProposer ? requirementInfo.rqmtProposer.join() : null
@@ -500,8 +595,10 @@ export default {
       if (requirementInfo.referredClientType !== null) {
         requirementInfo.referredClientType = requirementInfo.referredClientType.join()
       }
+      requirementInfo.status = this.statusValue
       const res = await updateRequirement(requirementInfo)
       if (res.code === 200) {
+        this.dialogStatusVisible = false
         this.$message({ message: '修改成功', type: 'success', duration: 1000, offset: 150 })
       }
       this.getRequirementById()
@@ -602,6 +699,28 @@ export default {
           this.form_query.rqmtProposer = this.form_query.rqmtProposer.split(',')
         }
       }
+      this.availableStatusList.map(item => {
+        if (item.name === 'BRD评审通过') {
+          if (this.form_query.status >= item.code) {
+            this.brdPassRealTime = true
+          }
+        }
+        if (item.name === 'PRD评审通过') {
+          if (this.form_query.status >= item.code) {
+            this.prdPassRealTime = true
+          }
+        }
+        if (item.name === '技术准入') {
+          if (this.form_query.status >= item.code) {
+            this.techInRealTime = true
+          }
+        }
+        if (item.name === '已上线') {
+          if (this.form_query.status >= item.code) {
+            this.onlineRealTime = true
+          }
+        }
+      })
     },
     async getCommentList() { // 获取需求评论
       const res = await getCommentList({ type: 4, joinId: this.$route.query.id })
@@ -627,6 +746,16 @@ export default {
       }
     },
     async updateStatus(status) { // 修改状态
+      if (status.label === 'PRD评审通过' || status.label === 'BRD评审通过' || status.label === '技术准入' || status.label === '已上线') {
+        this.statusName = status.label
+        this.statusValue = status.value
+        this.dialogStatusVisible = true
+        status.label === 'BRD评审通过' ? this.form_query.brdPassRealTime = moment().locale('zh-cn').format('YYYY.MM.DD') : '' // BRD评审通过时间
+        status.label === 'PRD评审通过' ? this.form_query.prdPassRealTime = moment().locale('zh-cn').format('YYYY.MM.DD') : '' // PRD评审通过时间
+        status.label === '技术准入' ? this.form_query.techInRealTime = moment().locale('zh-cn').format('YYYY.MM.DD') : '' // 技术准入
+        status.label === '已上线' ? this.form_query.onlineRealTime = moment().locale('zh-cn').format('YYYY.MM.DD') : '' // 实际上线
+        return false
+      }
       const res = await updateRequirementStatus({
         id: this.$route.query.id,
         status: status.value,
@@ -749,7 +878,7 @@ export default {
     }
     .demo-form-inline {
       .el-form-item {
-        width: 33%;
+        width: 20%;
         margin-right: 0;
       }
     }
@@ -821,5 +950,14 @@ export default {
 .el-btn-size {
    margin: 10px 30px;
 }
+.border-top {
+ padding: 0 20px 10px !important;
+ >>>.el-divider--horizontal {
+    display: block;
+    height: 1px;
+    width: 100%;
+    margin: 10px 0;
+}
+}
 </style>
 

+ 0 - 1
src/views/projectManage/taskList/components/reportList.vue

@@ -336,7 +336,6 @@ export default {
     },
 
     async dailyReportGetByTaskIdV2(e) {
-      alert()
       e ? this.dailyDataStatus = false : ''
       const indexPage = { taskId: this.taskId, bizId: localStorage.getItem('bizId'), curIndex: this.dailyPages.curIndex, pageSize: this.dailyPages.pageSize }
       const res = await dailyReportGetByTaskIdV2(indexPage)

+ 1 - 1
src/views/projectManage/taskList/taskIndex.vue

@@ -232,7 +232,7 @@ export default {
   },
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       header_show: true,
       props: { multiple: true },
       priorityColors: ['#F56C6C', '#FF8952', '#F5E300', '#7ED321', '#61D3B8', '#69B3FF', '#BDBDBD'],

+ 7 - 2
src/views/quality/components/changeRequireChart.vue

@@ -10,13 +10,13 @@
       >
         <el-table-column
           prop="id"
-          label="任务ID"
+          :label="typeName+'id'"
           width="180"
           align="center"
         />
         <el-table-column
           prop="name"
-          label="任务名称"
+          :label="typeName + '名称'"
           align="center"
           min-width="200"
         />
@@ -46,6 +46,11 @@ export default {
       type: Array,
       default: () => [],
       required: false
+    },
+    typeName: {
+      type: String,
+      default: '需求',
+      required: false
     }
   },
   watch: {

+ 3 - 11
src/views/quality/components/statusChart.vue

@@ -86,16 +86,7 @@ export default {
     },
     handlerXdata(arr) {
       if (!this.seriesData[0]) return []
-      let newArr = []
-      if (this.seriesData[0].dayTime.length <= 12) {
-        newArr = this.seriesData[0].dayTime
-      } else {
-        const gap = Math.floor(this.seriesData[0].dayTime.length / 12)
-        newArr = this.seriesData[0].dayTime.filter((item, index) => {
-          return index % gap === 0
-        })
-      }
-      return newArr
+      return this.seriesData[0].dayTime
     },
     setPile() {
       this.echartsOption = {
@@ -116,7 +107,8 @@ export default {
           {
             type: 'category',
             boundaryGap: false,
-            data: this.handlerXdata(this.seriesData)
+            data: this.handlerXdata(this.seriesData),
+            axisLabel: { rotate: 15, align: 'center', margin: 20 }
           }
         ],
         yAxis: [{ type: 'value' }],

+ 6 - 3
src/views/quality/requireStatistics.vue

@@ -157,8 +157,8 @@
         <belong-chart :chart-data="orntDistributeData" />
       </div>
       <div class="chart-item">
-        <h3>排期发生变更的任务(<span class="strong-font">{{ changeTotal }}</span>个)</h3>
-        <change-require-chart :chart-data="changeRequireData" />
+        <h3>排期发生变更的需求(<span class="strong-font">{{ changeTotal }}</span>个)</h3>
+        <change-require-chart :chart-data="changeRequireData" type-name="需求" />
       </div>
       <div class="chart-item">
         <h3>缺陷统计</h3>
@@ -592,9 +592,12 @@ export default {
     padding: 6px 10px;
     margin-top: 5px;
     font-size: 12px;
-    color: #E6A23C;
+    color: #444444;
     background: rgba(230, 162, 61, 0.1);
     border: 2px;
+    i {
+      margin-right: 10px;
+    }
   }
 }
 .strong-font {

+ 6 - 3
src/views/quality/taskStatistics.vue

@@ -149,8 +149,8 @@
         <belong-chart :chart-data="moduleDistributeData" />
       </div>
       <div class="chart-item">
-        <h3>排期发生变更的需求(<span class="strong-font">{{ changeTotal }}</span>个)</h3>
-        <change-require-chart :chart-data="changeTaskData" />
+        <h3>排期发生变更的任务(<span class="strong-font">{{ changeTotal }}</span>个)</h3>
+        <change-require-chart :chart-data="changeTaskData" type-name="任务" />
       </div>
       <div class="chart-item">
         <h3>报告统计</h3>
@@ -616,9 +616,12 @@ export default {
     padding: 6px 10px;
     margin-top: 5px;
     font-size: 12px;
-    color: #E6A23C;
+    color: #444444;
     background: rgba(230, 162, 61, 0.1);
     border: 2px;
+    i {
+      margin-right: 10px;
+    }
   }
 }
 .strong-font {

+ 21 - 22
src/views/reportManagement/ReleaseReport/components/iconDisplay.vue

@@ -24,18 +24,17 @@
       size="mini"
       :header-cell-style="{ backgroundColor: 'rgba(241,241,241,1)', color: 'rgba(51,59,74,1)', fontSize: '14px', fontWeight: '400'}"
       style="width: 100%"
-      show-overflow-tooltip="true"
     >
-      <el-table-column prop="bugName" label="缺陷标题" align="center" min-width="100" show-overflow-tooltip>
+      <el-table-column prop="bugName" label="缺陷标题" align="center" min-width="100">
         <template slot-scope="scope">
           <span class="didi-hover" @click.stop="click_bugName(scope.row.id)">{{ scope.row.bugName }}</span>
         </template>
       </el-table-column>
-      <el-table-column prop="bugStatusName" label="缺陷状态" align="center" min-width="100" show-overflow-tooltip />
-      <el-table-column prop="priorityLevel" label="缺陷等级" align="center" min-width="100" show-overflow-tooltip />
-      <el-table-column prop="creatorList" label="提报人" align="center" min-width="90" show-overflow-tooltip />
-      <el-table-column prop="assignerList" label="责任人" align="center" min-width="90" show-overflow-tooltip />
-      <el-table-column prop="currentHandlerList" label="修复人" align="center" min-width="90" show-overflow-tooltip />
+      <el-table-column prop="bugStatusName" label="缺陷状态" align="center" min-width="100" />
+      <el-table-column prop="priorityLevel" label="缺陷等级" align="center" min-width="100" />
+      <el-table-column prop="creatorList" label="提报人" align="center" min-width="90" />
+      <el-table-column prop="assignerList" label="责任人" align="center" min-width="90" />
+      <el-table-column prop="currentHandlerList" label="修复人" align="center" min-width="90" />
       <el-table-column prop="reasonOrDesc" label="备注(hold原因等)" align="center" min-width="150" />
     </el-table>
   </div>
@@ -69,11 +68,11 @@ export default {
           }
         }
       },
+      deep: true,
       immediate: true
     },
     releaseType: {
       handler(newV) {
-        console.log(newV)
         this.types = newV
       },
       immediate: true
@@ -94,9 +93,9 @@ export default {
           title: { text: '新增缺陷趋势图', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
           tooltip: { axisPointer: { type: 'shadow' }},
           grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-          xAxis: [{ type: 'category', data: res.bugDisByDate.xAxis ? res.bugDisByDate.xAxis : ['未知', '未知', '未知', '未知', '未知', '未知', '未知'], axisTick: { alignWithLabel: true }}],
+          xAxis: [{ type: 'category', data: res.bugDisByDate.xAxis ? res.bugDisByDate.xAxis : ['未知'], axisTick: { alignWithLabel: true }}],
           yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-          series: res.bugDisByDate.yAxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0] }] : data
+          series: res.bugDisByDate.yAxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0] }] : data
         })
       }, 200)
 
@@ -111,9 +110,9 @@ export default {
           title: { text: '缺陷责任人分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
           tooltip: { axisPointer: { type: 'shadow' }},
           grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-          xAxis: [{ type: 'category', data: res.bugDisByMember.xAxis[0] ? res.bugDisByMember.xAxis : ['未知', '未知', '未知', '未知'], axisTick: { alignWithLabel: true }}],
+          xAxis: [{ type: 'category', data: res.bugDisByMember.xAxis[0] ? res.bugDisByMember.xAxis : ['未知'], axisTick: { alignWithLabel: true }}],
           yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-          series: res.bugDisByMember.yAxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }] : lineShow
+          series: res.bugDisByMember.yAxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }] : lineShow
         })
       }, 200)
       const barShow = res.bugDisByPri.yAxis.map(item => ({
@@ -158,9 +157,9 @@ export default {
               title: { text: '新增缺陷趋势图', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
               tooltip: { axisPointer: { type: 'shadow' }},
               grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-              xAxis: [{ type: 'category', data: res6.data.xaxis === null ? ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期七'] : res6.data.xaxis, axisTick: { alignWithLabel: true }}],
+              xAxis: [{ type: 'category', data: res6.data.xaxis === null ? ['未知'] : res6.data.xaxis, axisTick: { alignWithLabel: true }}],
               yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-              series: res6.data.yaxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0] }] : data
+              series: res6.data.yaxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0] }] : data
 
             })
           }, 200)
@@ -178,9 +177,9 @@ export default {
               title: { text: '缺陷责任人分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
               tooltip: { axisPointer: { type: 'shadow' }},
               grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-              xAxis: [{ type: 'category', data: res2.data.xaxis[0] ? res2.data.xaxis : ['P0', 'P1', 'P2', 'P3'], axisTick: { alignWithLabel: true }}],
+              xAxis: [{ type: 'category', data: res2.data.xaxis[0] ? res2.data.xaxis : ['未知'], axisTick: { alignWithLabel: true }}],
               yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-              series: res2.data.yaxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }] : data
+              series: res2.data.yaxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }] : data
             })
           }, 200)
         }
@@ -211,18 +210,18 @@ export default {
             title: { text: '新增缺陷趋势图', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
             tooltip: { axisPointer: { type: 'shadow' }},
             grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-            xAxis: [{ type: 'category', data: ['未知', '未知', '未知', '未知', '未知', '未知', '未知'], axisTick: { alignWithLabel: true }}],
+            xAxis: [{ type: 'category', data: ['未知'], axisTick: { alignWithLabel: true }}],
             yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-            series: [{ name: '直接访问', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0] }]
+            series: [{ name: '直接访问', type: 'line', smooth: true, data: [0] }]
           })
           echarts.init(document.getElementById('lineShow')).setOption({
             color: ['#409EFF'],
             title: { text: '缺陷责任人分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
             tooltip: { axisPointer: { type: 'shadow' }},
             grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-            xAxis: [{ type: 'category', data: ['未知', '未知', '未知', '未知'], axisTick: { alignWithLabel: true }}],
+            xAxis: [{ type: 'category', data: ['未知'], axisTick: { alignWithLabel: true }}],
             yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-            series: [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }]
+            series: [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }]
           })
           echarts.init(document.getElementById('barShow')).setOption({
             backgroundColor: '#FFF',
@@ -230,9 +229,9 @@ export default {
             title: { text: '缺陷等级分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
             tooltip: { axisPointer: { type: 'shadow' }},
             grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-            xAxis: [{ type: 'category', data: ['未知', '未知', '未知', '未知'], axisTick: { alignWithLabel: true }}],
+            xAxis: [{ type: 'category', data: ['未知'], axisTick: { alignWithLabel: true }}],
             yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-            series: [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }]
+            series: [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }]
           })
         }, 200)
       }

+ 188 - 131
src/views/reportManagement/ReleaseReport/components/releaseDetails.vue

@@ -15,90 +15,137 @@
     </el-header>
     <el-container>
       <el-main class="report-main">
-        <div class="title"><div class="blur-column" /> 报告内容</div>
+        <div class="title">
+          <div class="blur-column" />报告内容
+        </div>
         <el-row>
           <el-col :span="24" class="task-bot">
-            <span class="from-names">测试结果: </span>
+            <span class="from-names">测试结果:</span>
             <span class="from-value" style="color:rgba(126,211,33,1);">通过</span>
           </el-col>
         </el-row>
 
         <el-row>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-name"><span class="test-details">计划准出时间:</span>{{ details.releasePlanTime }}</span>
+            <span class="from-name">
+              <span class="test-details">计划准出时间:</span>
+              {{ details.releasePlanTime }}
+            </span>
           </el-col>
           <el-col :span="12">
-            <span class="from-namev"><span class="test-details">实际准出时间:</span>{{ details.releaseActualTime }}</span>
+            <span class="from-namev">
+              <span class="test-details">实际准出时间:</span>
+              {{ details.releaseActualTime }}
+            </span>
           </el-col>
         </el-row>
 
-        <el-row v-if="details.isDelay === 1">
+        <el-row v-if="details.releasePlanTime < details.releaseActualTime">
           <el-col :span="24" class="Layout_space_between task-bot">
-            <span class="from-namea"><span class="test-details">准出延期原因 : </span>{{ details.delayReason }}</span>
+            <span class="from-namea">
+              <span class="test-details">准出延期原因 :</span>
+              {{ details.delayReason }}
+            </span>
           </el-col>
         </el-row>
 
         <el-row>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-name"><span class="test-details">计划开发周期:</span>{{ details.testPlanTimeStart }}</span>
+            <span class="from-name">
+              <span class="test-details">计划开发周期:</span>
+              {{ details.devPlanTimeStart }} 至 {{ details.devPlanTimeEnd }}
+            </span>
           </el-col>
           <el-col :span="12" class="Layout_space_between">
-            <span class="from-namer"><span class="test-details">实际开发周期:</span>{{ details.testPlanTimeEnd }}</span>
+            <span class="from-namer">
+              <span class="test-details">实际开发周期:</span>
+              {{ details.devActualTimeStart }} 至 {{ details.devActualTimeEnd }}
+            </span>
           </el-col>
         </el-row>
 
         <el-row>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-name"><span class="test-details">计划测试周期:</span>{{ details.testActualTimeStart }} </span>
+            <span class="from-name">
+              <span class="test-details">计划测试周期:</span>
+              {{ details.testPlanTimeStart }} 至 {{ details.testPlanTimeEnd }}
+            </span>
           </el-col>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-namer"><span class="test-details">实际测试周期:</span>{{ details.testActualTimeEnd }}</span>
+            <span class="from-namer">
+              <span class="test-details">实际测试周期:</span>
+              {{ details.testActualTimeStart }} 至 {{ details.testActualTimeEnd }}
+            </span>
           </el-col>
         </el-row>
 
         <el-row class="task-bot">
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-name"><span class="test-details">开发人员:</span>{{ details.developer }}</span>
+            <span class="from-name">
+              <span class="test-details">开发人员:</span>
+              <span v-for="(item, index) in details.devObject" :key="index">
+                {{ item.name }}
+                <span v-if="index < details.devObject.length - 1">,</span>
+              </span>
+            </span>
           </el-col>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-namer"><span class="test-details">测试人员:</span>{{ details.tester }}</span>
+            <span class="from-namer">
+              <span class="test-details">测试人员:</span>
+              <span v-for="(item, index) in details.testerObject" :key="index">
+                {{ item.name }}
+                <span v-if="index < details.testerObject.length - 1">,</span>
+              </span>
+            </span>
           </el-col>
         </el-row>
         <div v-html="details.content" />
 
-        <div class="title"> 缺陷统计</div>
-        <icon-display :task-ids="details.statusString === '已发送' ? details : details.taskIds" />
+        <div class="title">缺陷统计</div>
+        <icon-display :task-ids="details.statusString === ' 已发送' ? details : details.taskIds" />
       </el-main>
       <el-aside width="400px">
         <el-container>
           <el-header class="report-mains">
-            <div class="title"><div class="blur-column" /> 用户信息</div>
+            <div class="title">
+              <div class="blur-column" />用户信息
+            </div>
             <div class="Layout_flex_start task-bot">
-              <div class="title-name">报告人 : </div>
+              <div class="title-name">报告人 :</div>
               <div class="task-name">{{ details.reportorObject ? details.reportorObject.name : '' }}</div>
             </div>
             <div class="Layout_flex_start task-bot">
-              <div class="title-name">收件人 : </div>
-              <div v-for="(item, index) in details.sendToObject" :key="index" class="task-name"> {{ item.name }} <span v-if="index < details.sendToObject.length - 1"> , </span></div>
+              <div class="title-name">收件人 :</div>
+              <div v-for="(item, index) in details.sendToObject" :key="index" class="task-name">
+                {{ item.name }}
+                <span v-if="index < details.sendToObject.length - 1">,</span>
+              </div>
             </div>
             <div class="Layout_flex_start task-bot">
-              <div class="title-name">抄送人 : </div>
-              <div v-for="(item, index) in details.sendCcObject" :key="index" class="task-name">{{ item.name }} <span v-if="index < details.sendCcObject.length - 1"> , </span></div>
+              <div class="title-name">抄送人 :</div>
+              <div v-for="(item, index) in details.sendCcObject" :key="index" class="task-name">
+                {{ item.name }}
+                <span v-if="index < details.sendCcObject.length - 1">,</span>
+              </div>
             </div>
           </el-header>
           <el-main class="report-mains">
-            <div class="title"><div class="blur-column" /> 时间</div>
+            <div class="title">
+              <div class="blur-column" />时间
+            </div>
             <div class="Layout_flex_start task-bot">
-              <div class="title-name">创建时间 : </div>
+              <div class="title-name">创建时间 :</div>
               <div class="task-name">{{ details.gmtCreate }}</div>
             </div>
             <div class="Layout_flex_start task-bot">
-              <div class="title-name">发送时间 : </div>
+              <div class="title-name">发送时间 :</div>
               <div class="task-name">{{ details.reportTime }}</div>
             </div>
           </el-main>
           <el-footer class="report-mains">
-            <div class="title"><div class="blur-column" /> 关联任务</div>
+            <div class="title">
+              <div class="blur-column" />关联任务
+            </div>
             <div v-for="(item, index) in details.taskDetailList" :key="index" class="Layout_flex_start task-bot">
               <div class="task-id">{{ item.taskId }}</div>
               <el-tooltip class="item" effect="dark" :content="item.name" placement="top-start">
@@ -126,7 +173,10 @@
 
 <script>
 import '@/styles/PublicStyle/index.scss'
-import { reportreleaseGetReportById, reportreleaseDelete } from '@/api/reportTemplate'
+import {
+  reportreleaseGetReportById,
+  reportreleaseDelete
+} from '@/api/reportTemplate'
 import iconDisplay from '@/views/reportManagement/ReleaseReport/components/iconDisplay.vue'
 import ReleaseReport from '@/views/reportManagement/components/ReleaseReport' // 准出
 export default {
@@ -146,13 +196,15 @@ export default {
     this.reportreleaseGetReportById(this.reportId)
   },
   methods: {
-    async reportreleaseGetReportById(e) { // 获取准出报告data
+    async reportreleaseGetReportById(e) {
+      // 获取准出报告data
       const res = await reportreleaseGetReportById(e)
       if (res.code === 200) {
         this.details = res.data
       }
     },
-    clientButtom(e, data) { // 准出报告
+    clientButtom(e, data) {
+      // 准出报告
       this.report_data = data
       this.dialogDaily = true
       this.$nextTick(() => {
@@ -161,10 +213,14 @@ export default {
     },
 
     goTaskDetails(id) {
-      this.$router.push({ name: '任务详情', query: { id: id }})
+      this.$router.push({
+        name: '任务详情',
+        query: { id: id }
+      })
     },
 
-    async deleteDaily() { // 删除准出报告
+    async deleteDaily() {
+      // 删除准出报告
       const res = await reportreleaseDelete({}, this.details.id)
       if (res.code === 200) {
         this.dialog_testData = false
@@ -176,22 +232,22 @@ export default {
 }
 </script>
 
-<style lang="scss" scoped>
+<style lang='scss' scoped>
 .BackgroundCloth {
-.report-Header {
-  margin: 10px 10px 0;
-  background: #FFF;
-  border-radius:4px;
-}
-.from-margin {
+  .report-Header {
+    margin: 10px 10px 0;
+    background: #fff;
+    border-radius: 4px;
+  }
+  .from-margin {
     width: 100%;
     display: inline-block;
-    font-size:14px;
-    font-family:Microsoft Sans Serif;
-    font-weight:400;
-    line-height:22px;
-    color:#333;
-    opacity:1;
+    font-size: 14px;
+    font-family: Microsoft Sans Serif;
+    font-weight: 400;
+    line-height: 22px;
+    color: #333;
+    opacity: 1;
     .from-name {
       width: 140px !important;
     }
@@ -203,112 +259,113 @@ export default {
     }
     .from-namer {
       width: 140px !important;
-      margin-left:10px;
+      margin-left: 10px;
     }
     .from-namev {
-      margin-left:10px;
+      margin-left: 10px;
       vertical-align: sub;
     }
     .from-value {
       margin-left: 30px;
     }
-  .report-taskList{
-    font-size:14px;
-    font-family:MicrosoftYaHei;
-    line-height:17px;
-    color:rgba(102,102,102,1);
-    margin-top: 10px;
-    opacity:1;
+    .report-taskList {
+      font-size: 14px;
+      font-family: MicrosoftYaHei;
+      line-height: 17px;
+      color: rgba(102, 102, 102, 1);
+      margin-top: 10px;
+      opacity: 1;
+    }
   }
-}
 
-.details-id {
-  font-size:12px;
-  font-family:PingFang SC;
-  font-weight:400;
-  line-height:20px;
-  color:rgba(51,59,74,1);
-  opacity:0.5;
-}
+  .details-id {
+    font-size: 12px;
+    font-family: PingFang SC;
+    font-weight: 400;
+    line-height: 20px;
+    color: rgba(51, 59, 74, 1);
+    opacity: 0.5;
+  }
 
-.report-title {
-  font-size:20px;
-  font-family:PingFangSC-Medium;
-  line-height:28px;
-  color:rgba(51,59,74,1);
-  opacity:1;
-}
+  .report-title {
+    font-size: 20px;
+    font-family: PingFangSC-Medium;
+    line-height: 28px;
+    color: rgba(51, 59, 74, 1);
+    opacity: 1;
+  }
 
-.details-statusString {
-  font-size:14px;
-  font-family:MicrosoftYaHei;
-  line-height:17px;
-  padding: 5px 8px;
-  margin-left: 15px;
-  color:rgba(111,124,147,1);
-  border:1px solid rgba(191,198,220,1);
-  opacity:1;
-  border-radius:4px;
-}
+  .details-statusString {
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    padding: 5px 8px;
+    margin-left: 15px;
+    color: rgba(111, 124, 147, 1);
+    border: 1px solid rgba(191, 198, 220, 1);
+    opacity: 1;
+    border-radius: 4px;
+  }
 
-.report-main {
-  margin: 10px;
-  background: #FFF;
-  border-radius:4px;
-}
-.report-mains {
-  min-height: 200px;
-  padding: 20px 30PX;
-  margin: 10px 10px 0 0;
-  background: #FFF;
-  border-radius:4px;
-}
-.title {
-  font-size:16px;
-  font-family:PingFangSC-Medium;
-  margin-bottom: 10px;
-  color:rgba(51,59,74,1);
-  opacity:1;
-}
+  .report-main {
+    margin: 10px;
+    background: #fff;
+    border-radius: 4px;
+  }
+  .report-mains {
+    min-height: 200px;
+    padding: 20px 30px;
+    margin: 10px 10px 0 0;
+    background: #fff;
+    border-radius: 4px;
+  }
+  .title {
+    font-size: 16px;
+    font-family: PingFangSC-Medium;
+    margin-bottom: 10px;
+    color: rgba(51, 59, 74, 1);
+    opacity: 1;
+  }
 
-.title-name {
-  width:100px;
-  font-size:14px;
-  font-family:PingFangSC-Regular;
-  line-height:20px;
-  color:rgba(102,102,102,1);
-  opacity:1;
-}
+  .title-name {
+    width: 100px;
+    font-size: 14px;
+    font-family: PingFangSC-Regular;
+    line-height: 20px;
+    color: rgba(102, 102, 102, 1);
+    opacity: 1;
+  }
 
-.task-id {
-  width:100px;
-  font-size:14px;
-  font-family:MicrosoftYaHei;
-  line-height:17px;
-  color:rgba(102,102,102,1);
-  opacity:1;
-}
+  .task-id {
+    width: 100px;
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    color: rgba(102, 102, 102, 1);
+    opacity: 1;
+  }
 
-.task-name {
-  font-size:14px;
-  font-family:MicrosoftYaHei;
-  line-height:17px;
-  color:rgba(51,51,51,1);
-  opacity:1;
-}
+  .task-name {
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    color: rgba(51, 51, 51, 1);
+    opacity: 1;
+  }
 
-.blur-column {
-  width:4px;
-  height:15px;
-  display:inline-block;
-  vertical-align: middle;
-  background:#409EFF;
-  border-radius:1px;
-}
+  .blur-column {
+    width: 4px;
+    height: 15px;
+    display: inline-block;
+    vertical-align: middle;
+    background: #409eff;
+    border-radius: 1px;
+    margin-right: 5px;
+  }
 
-.task-bot {
-   margin-bottom: 10px;
-}
+  .task-bot {
+    margin-bottom: 10px;
+  }
 }
 .setLine {
   padding: 10px 20px;

+ 136 - 30
src/views/reportManagement/ReleaseReport/newReleaeTemplate.vue

@@ -6,7 +6,28 @@
     element-loading-text="数据上传中,请稍后"
     element-loading-spinner="el-icon-loading"
   >
+
     <el-form ref="fromCreateData" :model="from">
+
+      <div v-if="!releaseType" style=" margin-bottom: 10px;">
+        <span style="color: #f56b6c">*</span>
+        <span class="backStyle"> 关联任务</span><br>
+
+        <el-select v-model="from.taskIds" filterable remote reserve-keyword placeholder="🔍 请输入任务名称或ID" :remote-method="remoteMethod" style="width: 30%;margin-top: 10px;" size="small" @change="colseSelect">
+          <el-option v-for="item in tasksOptions" :key="item.id" :label="item.name" :value="item.id" @click.native="tasksChange(item)">
+            <div class="Layout_space_between"><span>{{ item.name }}</span></div>
+          </el-option>
+        </el-select>
+
+        <div v-if="selectTask" class="taskError">关联任务不能为空</div>
+
+        <div v-for="(item, index) in tasksDetailList" :key="index" class="Layout_space_between report-taskList">
+          <span>{{ item.taskIdSting }}</span>
+          <span>{{ item.name }}</span>
+          <i class="el-icon-circle-close didi-hover" @click="delete_task(item)" />
+        </div>
+      </div>
+
       <el-form-item v-if="releaseType" label="模版名称" prop="moduleName" :rules="[{ required: true, message: '模版名称不能为空', trigger: 'change'}]"><br>
         <el-input v-model="from.moduleName" size="small" style="width:100%;" placeholder="请输入模版名称(不可与现有模版重名)" />
       </el-form-item>
@@ -33,7 +54,7 @@
         </el-col>
       </el-row>
 
-      <el-row v-if="from.isDelay === 1 || from.releasePlanTime < from.releaseActualTime" style="margin: 20px 0">
+      <el-row v-if="from.releasePlanTime < from.releaseActualTime" style="margin: 20px 0">
         <!-- 准出是否延期 1 延期,0未延期 -->
         <el-col :span="24" class="Layout_space_between">
           <span class="from-namea"><span v-if="!releaseType" style="color:red;">*</span>准出延期原因 : </span>
@@ -66,11 +87,13 @@
       <el-row class="from-margin">
         <el-col :span="12" class="Layout_space_between">
           <span class="from-name">开发人员: </span>
-          <searchTeam :value.sync="from.developer" :clearable="true" :multiple="true" :disabled="releaseType" :placeholder="'请输入姓名或邮箱前缀'" :size="'small'" style="width: 100%;" />
+          <search-people :value.sync="from.developer" :clearable="true" :multiple="true" :size="'small'" :disabled="releaseType" style="width: 100%;" />
+          <!-- <searchTeam :value.sync="from.developer" :clearable="true" :multiple="true" :disabled="releaseType" :size="'small'" style="width: 100%;" /> -->
         </el-col>
         <el-col :span="12" class="Layout_space_between">
           <span class="from-namer"> 测试人员:</span>
-          <searchTeam :value.sync="from.tester" :clearable="true" :multiple="true" :disabled="releaseType" :placeholder="'请输入姓名或邮箱前缀'" :size="'small'" style="width: 100%;" />
+          <search-people :value.sync="from.tester" :clearable="true" :multiple="true" :size="'small'" :disabled="releaseType" style="width: 100%;" />
+          <!-- <searchTeam :value.sync="from.tester" :clearable="true" :multiple="true" :disabled="releaseType" :placeholder="'请输入姓名或邮箱前缀'" :size="'small'" style="width: 100%;" /> -->
         </el-col>
       </el-row>
       <normal-area id="report-template" :value.sync="fromData.content" :height="500" />
@@ -82,9 +105,10 @@
 
 <script>
 import '@/styles/PublicStyle/index.scss' // 公共css
-import searchTeam from '@/components/select/searchTeam' // 人员搜索
+import searchPeople from '@/components/select/searchPeople' // 人员select
 import normalArea from '@/components/input/normalArea' // 富文本
 import 'tinymce/plugins/table'// 插入表格插件
+import { taskList } from '@/api/taskIndex'
 import iconDisplay from '@/views/reportManagement/ReleaseReport/components/iconDisplay.vue'
 import { settingAddReportModule, settingUpdateReportModule, settingGetReportModuleById, reportreleaseInitReportRelease, reportreleaseCreate, reportreleaseUpdate, reportreleaseGetReportById } from '@/api/reportTemplate' // 模版添删改查
 import { getContainImgHTMLNode } from '@/utils/handleTinymce' // 富文本本图片转换
@@ -92,7 +116,7 @@ import { getContainImgHTMLNode } from '@/utils/handleTinymce' // 富文本本图
 export default {
   components: {
     normalArea,
-    searchTeam,
+    searchPeople,
     iconDisplay
   },
   props: {
@@ -106,6 +130,10 @@ export default {
       taskId: [], // 创建准出的任务
       content: {}, // from
       from: {}, // from-date
+      tasksOptions: [], // 关联任务
+      tasksDetailList: [], // 关联任务
+      selectTask: false,
+      judge: false,
       fromData: {},
       modelID: '',
       releaseType: false, // 新建模版还是新建准出报告
@@ -138,8 +166,13 @@ export default {
     taskIds: {
       handler(newV) {
         if (newV) {
-          this.taskId = newV
-          this.reportreleaseInitReportRelease()
+          this.$nextTick(() => {
+            this.taskId = newV
+            newV.map(item => {
+              this.remoteMethod_data(item)
+            })
+          })
+          this.reportreleaseInitReportRelease(newV)
         }
       },
       immediate: true
@@ -151,14 +184,17 @@ export default {
       if (res.code === 200) {
         this.$nextTick(() => {
           this.taskId = res.data.taskIds
+          this.taskId.map(item => {
+            this.remoteMethod_data(item)
+          })
         })
         this.setDefaultData(res.data)
       }
     },
 
-    async reportreleaseInitReportRelease() { // 获取表单数据
+    async reportreleaseInitReportRelease(newV) { // 获取表单数据
       if (!this.releaseType) {
-        const res = await reportreleaseInitReportRelease({ taskIds: this.taskId })
+        const res = await reportreleaseInitReportRelease({ taskIds: newV })
         if (res.code === 200) {
           this.setDefaultData(res.data)
         }
@@ -205,6 +241,45 @@ export default {
       }
     },
 
+    async remoteMethod_data(query) { // 远程搜索任务
+      const res = await taskList({ name: query })
+      if (res.code === 200) {
+        this.tasksDetailList.push(res.data[0])
+      }
+    },
+
+    async remoteMethod(query) { // 远程搜索任务
+      const res = await taskList({ name: query })
+      if (res.code === 200) {
+        this.tasksOptions = res.data
+      }
+    },
+
+    tasksChange(data) { // 任务列表变动
+      const isEx = this.tasksDetailList.find(item => item.id === data.id)
+      if (isEx) {
+        this.$message({ message: '任务已存在', type: 'warning', duration: 1000, offset: 150 })
+        return false
+      }
+      this.tasksDetailList.push(data)
+      this.taskId.push(data.id)
+      this.tasksDetailList.length <= 0 ? this.selectTask = true : this.selectTask = false
+    },
+
+    delete_task(val) { // 删除关联的任务
+      this.tasksDetailList = this.tasksDetailList.filter(item => {
+        return item.id !== val.id
+      })
+      this.taskId = this.taskId.filter(item => {
+        return item !== val.id
+      })
+      this.tasksDetailList.length <= 0 ? this.selectTask = true : this.selectTask = false
+    },
+
+    colseSelect() { // 清空任务名称
+      this.$set(this.from, 'taskIds', null)
+    },
+
     // 创建准出报告
     reportreleaseCreate(val) {
       this.$refs.fromCreateData.validate(async(valid) => {
@@ -223,9 +298,14 @@ export default {
             data.reportName = this.from.reportName // 报告名称
             data.releasePlanTime = this.from.releasePlanTime // 计划准出时间
             data.releaseActualTime = this.from.releaseActualTime // 实际准出时间
-            if (this.from.releasePlanTime > this.from.releaseActualTime) {
+            if (this.from.releasePlanTime < this.from.releaseActualTime) {
               data.isDelay = 1// 准出是否延期 1 延期,0未延期
-              data.delayReason = this.from.delayReason // 是否延期
+              if (this.from.delayReason) {
+                data.delayReason = this.from.delayReason // 是否延期
+              } else {
+                this.$message({ type: 'warning', message: '准出原因不能为空' })
+                return false
+              }
             } else {
               data.isDelay = 0
             }
@@ -246,7 +326,11 @@ export default {
               data.testActualTimeEnd = this.from.testActualTimeStart[1] // 实际测试结束时间
             }
             data.testResult = 1 // 测试结果 状态 1通过 2未通过
-            data.taskIds = this.taskId // 关联任务
+            if (this.taskId[0]) {
+              data.taskIds = this.taskId // 关联任务
+            } else {
+              return false
+            }
             data.bizId = this.from.bizId // 业务线
             data.moduleId = this.from.moduleId // 模块id
             data.content = this.fromData.content // 富文本
@@ -264,9 +348,14 @@ export default {
             data.reportName = this.from.reportName // 报告名称
             data.releasePlanTime = this.from.releasePlanTime // 计划准出时间
             data.releaseActualTime = this.from.releaseActualTime // 实际准出时间
-            if (this.from.releasePlanTime > this.from.releaseActualTime) {
+            if (this.from.releasePlanTime < this.from.releaseActualTime) {
               data.isDelay = 1// 准出是否延期 1 延期,0未延期
-              data.delayReason = this.from.delayReason // 是否延期
+              if (this.from.delayReason) {
+                data.delayReason = this.from.delayReason // 是否延期
+              } else {
+                this.$message({ type: 'warning', message: '准出原因不能为空' })
+                return false
+              }
             } else {
               data.isDelay = 0
             }
@@ -286,7 +375,11 @@ export default {
               data.testActualTimeStart = this.from.testActualTimeStart[0] // 实际测试开始时间
               data.testActualTimeEnd = this.from.testActualTimeStart[1] // 实际测试结束时间
             }
-            data.taskIds = this.taskId // 关联任务
+            if (this.taskId[0]) {
+              data.taskIds = this.taskId // 关联任务
+            } else {
+              return false
+            }
             data.bizId = localStorage.getItem('bizId') // 业务线
             data.moduleId = this.fromData.id // 模块id
             data.content = this.fromData.content // 富文本
@@ -354,7 +447,7 @@ export default {
     margin: 20px 0;
   }
 
-.report-container {
+  .report-container {
     display: inline-block;
     font-size:14px;
     width: 100%;
@@ -364,14 +457,14 @@ export default {
     color:#666666;
     opacity:1;
     >>> .el-form-item__label {
-    color: #333;
-}
->>> .el-date-editor .el-range-separator {
-    padding: 0 5px;
-    line-height: 26px;
-    width: auto;
-    color: #303133;
-}
+      color: #333;
+    }
+    >>> .el-date-editor .el-range-separator {
+      padding: 0 5px;
+      line-height: 26px;
+      width: auto;
+      color: #303133;
+    }
     .from-name {
       width: 140px !important;
     }
@@ -386,12 +479,25 @@ export default {
       margin-left: 30px;
       color:rgba(126,211,33,1);
     }
-}
-.sodu {
-  color:#C0C4CC;
-  margin-left: 15px;
-}
-
+  }
+  .report-taskList{
+    font-size:14px;
+    font-family:MicrosoftYaHei;
+    line-height:17px;
+    color:rgba(102,102,102,1);
+    margin-top: 10px;
+    opacity:1;
+  }
+  .sodu {
+    color:#C0C4CC;
+    margin-left: 15px;
+  }
+  .taskError {
+    color: #F56C6C;
+    font-size: 12px;
+    line-height: 1;
+    padding-top: 4px;
+  }
   .from-margin {
     margin-bottom:20px;
   }

+ 72 - 60
src/views/reportManagement/ReleaseReport/releaePreview.vue

@@ -1,16 +1,21 @@
 <template>
   <!-- 预览准出报告 -->
   <div class="parent-style">
-    <div class="backStyle"> 邮件列表</div>
-    <div class="Layout_space_between report-margin bottom_mar">
+    <div class="backStyle">邮件列表</div>
+    <div class="Layout_space_between bottom_mar">
       <div class="div1">收件人</div>
       <searchTeam :value.sync="form.name" :clearable="true" :multiple="true" style="width:100%" />
     </div>
-    <div class="Layout_space_between report-margin">
+    <div class="Layout_space_between report-margin Above">
       <div class="div1">抄送</div>
       <searchTeam :value.sync="form.names" :clearable="true" :multiple="true" style="width:100%" />
     </div>
-    <div class="backStyle"> 报告预览</div>
+    <span class="backStyle">关联任务</span>
+    <div v-for="(item, index) in fromCreateData.taskDetailList" :key="index" class="Layout_flex_start report-taskList">
+      <span>{{ item.taskId }}</span>
+      <span>{{ item.name }}</span>
+    </div>
+    <div class="backStyle">报告预览</div>
     <div id="repot-list">
       <div class="Layout_space_between" style="border-bottom: 0.5px solid #eee;margin-bottom: 15px;">
         <div class="reportName">{{ fromCreateData.reportName }}</div>
@@ -19,7 +24,7 @@
 
       <el-row>
         <el-col :span="24">
-          <span class="from-names">测试结果: </span>
+          <span class="from-names">测试结果:</span>
           <span class="from-value">通过</span>
         </el-col>
       </el-row>
@@ -29,7 +34,7 @@
           <span class="parent-style">计划准出时间:{{ fromCreateData.releasePlanTime }}</span>
         </el-col>
         <el-col :span="12">
-          <span class="parent-style"> 实际准出时间:{{ fromCreateData.releaseActualTime }}</span>
+          <span class="parent-style">实际准出时间:{{ fromCreateData.releaseActualTime }}</span>
         </el-col>
       </el-row>
 
@@ -41,28 +46,38 @@
 
       <el-row>
         <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style">计划开发周期:{{ fromCreateData.testPlanTimeStart }}</span>
+          <span class="parent-style">计划开发周期:{{ fromCreateData.devPlanTimeStart }} 至 {{ fromCreateData.devPlanTimeEnd }}</span>
         </el-col>
         <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style"> 实际开发周期:{{ fromCreateData.testPlanTimeEnd }}</span>
+          <span class="parent-style">实际开发周期:{{ fromCreateData.devActualTimeStart }} 至 {{ fromCreateData.devActualTimeEnd }}</span>
         </el-col>
       </el-row>
 
       <el-row>
         <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style">计划测试周期:{{ fromCreateData.testActualTimeStart }} </span>
+          <span class="parent-style">计划测试周期:{{ fromCreateData.testPlanTimeStart }} 至 {{ fromCreateData.testPlanTimeEnd }}</span>
         </el-col>
         <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style">实际测试周期:{{ fromCreateData.testActualTimeEnd }}</span>
+          <span class="parent-style">实际测试周期:{{ fromCreateData.testActualTimeStart }} 至 {{ fromCreateData.testActualTimeEnd }}</span>
         </el-col>
       </el-row>
 
       <el-row class="Above">
         <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style">开发人员:{{ fromCreateData.developer }}</span>
+          <div class="parent-style">开发人员:
+            <span v-for="(item, index) in fromCreateData.devObject" :key="index">
+              {{ item.name }}
+              <span v-if="index < fromCreateData.devObject.length - 1">,</span>
+            </span>
+          </div>
         </el-col>
-        <el-col :span="12" class="Layout_space_between">
-          <span class="parent-style"> 测试人员:{{ fromCreateData.tester }}</span>
+        <el-col :span="12" class="Layout_space_start">
+          <div class="parent-style">测试人员:
+            <span v-for="(item, index) in fromCreateData.testerObject" :key="index">
+              {{ item.name }}
+              <span v-if="index < fromCreateData.testerObject.length - 1">,</span>
+            </span>
+          </div>
         </el-col>
       </el-row>
       <div v-html="fromCreateData.content" />
@@ -146,56 +161,53 @@ export default {
 
 <style lang="scss" scoped>
 .parent-style {
-    width: 100%;
-    display: inline-block;
-    font-size:14px;
-    font-family:Microsoft Sans Serif;
-    font-weight:400;
-    line-height:22px;
-    margin: 10px 0;
-    color:#666666;
-    opacity:1;
-    .from-value {
-      margin-left: 30px;
-      color:rgba(126,211,33,1);
-    }
-  .report-taskList{
-    font-size:14px;
-    font-family:MicrosoftYaHei;
-    line-height:17px;
-    color:rgba(102,102,102,1);
+  width: 100%;
+  display: inline-block;
+  font-size: 14px;
+  font-family: Microsoft Sans Serif;
+  font-weight: 400;
+  line-height: 22px;
+  margin: 10px 0;
+  color: #666666;
+  opacity: 1;
+  .from-value {
+    margin-left: 30px;
+    color: rgba(126, 211, 33, 1);
+  }
+  .report-taskList {
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    color: rgba(102, 102, 102, 1);
     margin-top: 10px;
-    opacity:1;
+    opacity: 1;
   }
 }
 .div1 {
-    width: 60px;
-    font-size: 14px;
-    font-family: MicrosoftYaHei;
-    color: rgba(51, 51, 51, 1);
-    line-height: 19px;
+  width: 60px;
+  font-size: 14px;
+  font-family: MicrosoftYaHei;
+  color: rgba(51, 51, 51, 1);
+  line-height: 19px;
+}
+.backStyle {
+  color: #333;
+  font-size: 14px;
+  font-weight: bold;
+  border-radius: 4px;
+  margin: 10px 0;
+}
+.reportName {
+  font-size: 20px;
+  font-weight: bold;
+  border-radius: 4px;
+  margin: 20px 0;
+  color: #409eff;
+}
+.bottom_mar {
+  margin-bottom: 10px;
+}
+.Above {
+  margin-bottom: 20px;
 }
-  .backStyle {
-    color: #333;
-    font-size: 14px;
-    font-weight: bold;
-    border-radius: 4px;
-    margin: 10px 0;
-  }
-  .reportName {
-    font-size: 20px;
-    font-weight: bold;
-    border-radius: 4px;
-    margin: 20px 0;
-     color:#409EFF;
-  }
-  .report-margin {
-    margin-left: 20px;
-  }
-  .bottom_mar {
-    margin-bottom: 10px;
-  }
-  .Above {
-    margin-bottom: 20px;
-  }
 </style>

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

@@ -10,6 +10,11 @@
       <div class="div1">抄送</div>
       <searchTeam :value.sync="form.names" :clearable="true" :multiple="true" style="width:100%" />
     </div>
+    <span class="backStyle"> 关联任务</span>
+    <div v-for="(item, index) in fromCreateData.taskDetailList" :key="index" class="Layout_flex_start report-taskList">
+      <span>{{ item.taskId }}</span>
+      <span>{{ item.name }}</span>
+    </div>
     <div class="backStyle"> 报告预览</div>
     <div id="repot-list">
       <div class="Layout_space_between" style="border-bottom: 0.5px solid #eee;margin-bottom: 15px;">
@@ -55,7 +60,12 @@
           <span class="parent-style">CodeReview:{{ fromCreateData.isCodeReview === 0 ? '否' : '是' }}</span>
         </el-col>
         <el-col v-if="fromCreateData.isCodeReview === 1" :span="12" class="Layout_space_between">
-          <span v-for="(item, index) in fromCreateData.codeReviewExecutorObject" :key="index" class="parent-style">执行人:{{ item.name }}</span>
+          <div class="parent-style">执行人:
+            <span v-for="(item, index) in fromCreateData.codeReviewExecutorObject" :key="index">
+              {{ item.name }}
+              <span v-if="index < fromCreateData.codeReviewExecutorObject.length - 1">,</span>
+            </span>
+          </div>
         </el-col>
       </el-row>
       <div class="backStyle">需求列表</div>

+ 11 - 2
src/views/reportManagement/Testing/components/deliverDetails.vue

@@ -55,10 +55,19 @@
 
         <el-row>
           <el-col :span="12" class="Layout_space_between task-bot">
-            <span class="from-name">CodeReview:{{ details.isCodeReview === 0 ? '否' : '是' }}</span>
+            <span class="from-name">
+              <span class="test-details">CodeReview:</span>
+              {{ details.isCodeReview === 0 ? '否' : '是' }}
+            </span>
           </el-col>
           <el-col v-show="details.isCodeReview === 1" :span="12" class="Layout_space_between task-bot">
-            <span class="from-namer"><span class="test-details">执行人:</span>{{ details.codeReviewExecutor }}</span>
+            <div class="from-namer">
+              <span class="test-details">执行人:</span>
+              <span v-for="(item, index) in details.codeReviewExecutorObject" :key="index">
+                {{ item.name }}
+                <span v-if="index < details.codeReviewExecutorObject.length - 1">,</span>
+              </span>
+            </div>
           </el-col>
         </el-row>
         <div class="title">需求列表</div>

+ 71 - 4
src/views/reportManagement/Testing/newTestingTemplate.vue

@@ -2,6 +2,26 @@
   <!-- 新建报告模版 -->
   <div class="report-container">
     <el-form ref="fromCreateData" :model="from">
+
+      <div v-if="!releaseType" style=" margin-bottom: 10px;">
+        <span style="color: #f56b6c">*</span>
+        <span class="backStyle"> 关联任务</span><br>
+
+        <el-select v-model="from.taskIds" filterable remote reserve-keyword placeholder="🔍 请输入任务名称或ID" :remote-method="remoteMethod" style="width: 30%; margin-top: 15px;" size="small" @change="colseSelect">
+          <el-option v-for="item in tasksOptions" :key="item.id" :label="item.name" :value="item.id" @click.native="tasksChange(item)">
+            <div class="Layout_space_between"><span>{{ item.name }}</span></div>
+          </el-option>
+        </el-select>
+
+        <div v-if="selectTask" class="taskError">关联任务不能为空</div>
+
+        <div v-for="(item, index) in tasksDetailList" :key="index" class="Layout_space_between report-taskList">
+          <span>{{ item.taskIdSting }}</span>
+          <span>{{ item.name }}</span>
+          <i class="el-icon-circle-close didi-hover" @click="delete_task(item)" />
+        </div>
+      </div>
+
       <el-form-item v-if="releaseType" label="模版名称" prop="moduleName" :rules="[{ required: true, message: '模版名称不能为空', trigger: 'change'}]"><br>
         <el-input v-model="from.moduleName" size="small" style="width:100%;" placeholder="请输入模版名称(不可与现有模版重名)" />
       </el-form-item>
@@ -77,7 +97,7 @@
         </el-col>
         <el-col v-if="from.isCodeReview === 1" :span="12" class="Layout_space_between">
           <span class="from-namer"><span style="color: red;">*</span>执行人:</span>
-          <searchTeam :value.sync="from.codeReviewExecutor" :clearable="true" :multiple="true" :size="'small'" :placeholder="'请输入姓名或邮箱前缀'" :disabled="releaseType" style="width: 100%;" />
+          <search-people :value.sync="from.codeReviewExecutor" :clearable="true" :multiple="true" :size="'small'" :disabled="releaseType" style="width: 100%;" />
         </el-col>
       </el-row>
     </el-form>
@@ -109,9 +129,10 @@
 
 <script>
 import '@/styles/PublicStyle/index.scss' // 公共css
-import searchTeam from '@/components/select/searchTeam' // 人员搜索
+import searchPeople from '@/components/select/searchPeople' // 人员select
 import normalArea from '@/components/input/normalArea' // 富文本
 import 'tinymce/plugins/table'// 插入表格插件
+import { taskList } from '@/api/taskIndex'
 import { configShowTaskEnum } from '@/api/taskIndex'
 import { settingAddReportModule, settingUpdateReportModule, settingGetReportModuleById, reportdelivertestInitReportRelease, reportdelivertestCreate, reportdelivertestGetRequiresByTaskIds, reportdelivertestUpdate, reportdelivertestGetReportById } from '@/api/reportTemplate' // 模版添删改查
 import { getContainImgHTMLNode } from '@/utils/handleTinymce' // 富文本本图片转换
@@ -119,7 +140,7 @@ import { getContainImgHTMLNode } from '@/utils/handleTinymce' // 富文本本图
 export default {
   components: {
     normalArea,
-    searchTeam
+    searchPeople
   },
   props: {
     templateId: { type: [String, Number, Object], default: null }, // 模版id
@@ -131,6 +152,9 @@ export default {
       tpltId: '', // 模版id
       taskId: [], // 创建准出的任务
       fromCreateData: {}, // from
+      tasksOptions: [], // 关联任务
+      tasksDetailList: [], // 关联任务
+      selectTask: false,
       from: {
         reportName: ''
       },
@@ -165,8 +189,11 @@ export default {
     },
     taskIds: {
       handler(newV) {
+        console.log(newV, '111111')
         if (newV[0]) {
-          this.taskId = newV
+          newV.map(item => {
+            this.remoteMethod_data(item)
+          })
           this.reportdelivertestInitReportRelease(newV)
         }
       },
@@ -184,6 +211,9 @@ export default {
         this.from.id = from.id
         this.$set(this.from, 'reportName', from.reportName) // 报告名称
         this.setFromData(from)
+        res.data.taskIds.map(item => {
+          this.remoteMethod_data(item)
+        })
       }
     },
 
@@ -231,6 +261,43 @@ export default {
         this.$set(this.from, 'moduleName', res.data.moduleName)
       }
     },
+    async remoteMethod_data(query) { // 远程搜索任务
+      const res = await taskList({ name: query })
+      this.tasksDetailList.push(res.data[0])
+      this.taskId.push(res.data[0].id)
+    },
+
+    async remoteMethod(query) { // 远程搜索任务
+      const res = await taskList({ name: query })
+      if (res.code === 200) {
+        this.tasksOptions = res.data
+      }
+    },
+
+    tasksChange(data) { // 任务列表变动
+      const isEx = this.tasksDetailList.find(item => item.id === data.id)
+      if (isEx) {
+        this.$message({ message: '任务已存在', type: 'warning', duration: 1000, offset: 150 })
+        return false
+      }
+      this.tasksDetailList.push(data)
+      this.taskId.push(data.id)
+      this.tasksDetailList.length <= 0 ? this.selectTask = true : this.selectTask = false
+    },
+
+    delete_task(val) { // 删除关联的任务
+      this.tasksDetailList = this.tasksDetailList.filter(item => {
+        return item.id !== val.id
+      })
+      this.taskId = this.taskId.filter(item => {
+        return item !== val.id
+      })
+      this.tasksDetailList.length <= 0 ? this.selectTask = true : this.selectTask = false
+    },
+
+    colseSelect() { // 清空任务名称
+      this.$set(this.from, 'taskIds', null)
+    },
 
     goRequired(id) {
       this.$router.push({ name: '需求详情', query: { id: id }})

+ 0 - 1
src/views/reportManagement/components/DailyReport.vue

@@ -269,7 +269,6 @@ export default {
 
     sendReport() { // 截图
       this.$refs.dailyPreview.sendReport()
-      this.handleClose()
     },
 
     handleClose() {

+ 13 - 13
src/views/reportManagement/daily/components/iconDisplay.vue

@@ -9,8 +9,8 @@
       <el-table-column prop="Reopen" label="Reopen" align="center" min-width="100" />
       <el-table-column prop="Hold" label="Hold" align="center" min-width="100" />
     </el-table>
+    <div :id="'line-echarts' + randomId" style="margin-top:10px;" class="line-echarts" />
     <div class="backStyle">今日新增缺陷( {{ DataByPri }} 个)</div>
-    <div :id="'line-echarts' + randomId" class="line-echarts" />
     <div class="Layout_space_between">
       <div :id="'lineShow' + randomId" class="backTop" />
       <div :id="'barShow' + randomId" class="backTop" />
@@ -20,7 +20,7 @@
       <el-table-column prop="bugId" label="缺陷ID" align="center" min-width="80" />
       <el-table-column prop="bugName" label="缺陷标题" align="center" min-width="100">
         <template slot-scope="scope">
-          <span class="didi-hover" @click.stop="click_bugName(scope.row.id)">{{ scope.row.bugName }}</span>
+          <span class="didi-hover" @click.stop="click_bugName(scope.row.bugId)">{{ scope.row.bugName }}</span>
         </template>
       </el-table-column>
       <el-table-column prop="bugStatusName" label="状态" align="center" min-width="100" />
@@ -84,7 +84,7 @@ export default {
   // },
   methods: {
     click_bugName(id) { // 缺陷跳转
-      this.$router.push({ name: '缺陷详情', params: { id: id }})
+      this.$router.push({ name: '缺陷详情', params: { id: Number(id.replace('BUG-', '')) }})
     },
 
     setReportData(val) {
@@ -104,9 +104,9 @@ export default {
           title: { text: '新增缺陷趋势图', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
           tooltip: { axisPointer: { type: 'shadow' }},
           grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-          xAxis: [{ type: 'category', data: val.bugDisByDate.xAxis ? val.bugDisByDate.xAxis : ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期七'], axisTick: { alignWithLabel: true }}],
+          xAxis: [{ type: 'category', data: val.bugDisByDate.xAxis ? val.bugDisByDate.xAxis : ['未知'], axisTick: { alignWithLabel: true }}],
           yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-          series: val.bugDisByDate.yAxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0] }] : data3
+          series: val.bugDisByDate.yAxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0] }] : data3
 
         })
       }, 200)
@@ -123,9 +123,9 @@ export default {
           title: { text: '缺陷责任人分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
           tooltip: { axisPointer: { type: 'shadow' }},
           grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-          xAxis: [{ type: 'category', data: val.bugDisByMember.xAxis.length <= 0 ? ['无', '无', '无', '无'] : val.bugDisByMember.xAxis, axisTick: { alignWithLabel: true }}],
+          xAxis: [{ type: 'category', data: val.bugDisByMember.xAxis.length <= 0 ? ['未知'] : val.bugDisByMember.xAxis, axisTick: { alignWithLabel: true }}],
           yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-          series: val.bugDisByMember.yAxis[0].data.length <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }] : data
+          series: val.bugDisByMember.yAxis[0].data.length <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }] : data
         })
       }, 200)
 
@@ -143,7 +143,7 @@ export default {
           grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
           xAxis: [{ type: 'category', data: val.bugDisByPri.xAxis, axisTick: { alignWithLabel: true }}],
           yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-          series: val.bugDisByPri.yAxis[0].data.length <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }] : data1
+          series: val.bugDisByPri.yAxis[0].data.length <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }] : data1
         })
       }, 200)
     },
@@ -155,7 +155,7 @@ export default {
         createEndTime: `${date} 23:59:59`, // 当天的结束时间
         taskIds: this.taskid_arr // 任务id list
       }
-      const res = await bugGetReportSumData(data) // 获取缺陷统计
+      const res = await bugGetReportSumData({ taskIds: this.taskid_arr }) // 获取缺陷统计
       if (res.code === 200) {
         this.tableList = []
         this.tableList.push(res.data)
@@ -179,9 +179,9 @@ export default {
             title: { text: '新增缺陷趋势图', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
             tooltip: { axisPointer: { type: 'shadow' }},
             grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-            xAxis: [{ type: 'category', data: res6.data.xaxis === null ? ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六', '星期七'] : res6.data.xaxis, axisTick: { alignWithLabel: true }}],
+            xAxis: [{ type: 'category', data: res6.data.xaxis === null ? ['未知'] : res6.data.xaxis, axisTick: { alignWithLabel: true }}],
             yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-            series: res6.data.yaxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0, 0, 0, 0, 0, 0, 0] }] : data
+            series: res6.data.yaxis[0].data <= 0 ? [{ name: '直接访问', type: 'line', smooth: true, data: [0] }] : data
           })
         }, 200)
       }
@@ -200,9 +200,9 @@ export default {
             title: { text: '缺陷责任人分布', x: 'center', textStyle: { fontSize: 14, fontStyle: 'normal', fontWeight: 'normal' }},
             tooltip: { axisPointer: { type: 'shadow' }},
             grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
-            xAxis: [{ type: 'category', data: res2.data.xaxis.length <= 0 ? ['P0', 'P1', 'P2', 'P3'] : res2.data.xaxis, axisTick: { alignWithLabel: true }}],
+            xAxis: [{ type: 'category', data: res2.data.xaxis.length <= 0 ? ['未知'] : res2.data.xaxis, axisTick: { alignWithLabel: true }}],
             yAxis: [{ type: 'value', axisLine: { show: false }, axisTick: { show: false }, splitLine: { show: true, lineStyle: { type: 'dashed' }}}],
-            series: res2.data.yaxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0, 0, 0, 0] }] : data
+            series: res2.data.yaxis[0].data <= 0 ? [{ name: '无数据', type: 'bar', barWidth: '20px', data: [0] }] : data
           })
         }, 200)
       }

+ 47 - 43
src/views/reportManagement/daily/dailyPreview.vue

@@ -1,26 +1,34 @@
 <template>
   <!-- 预览测试日报 -->
   <div class="parent-style">
-    <div class="backStyle"> 邮件列表</div>
+    <div class="backStyle">邮件列表</div>
     <div class="Layout_space_between report-div">
       <div class="div1">收件人</div>
       <searchTeam :value.sync="form.name" :clearable="true" :multiple="true" style="width:100%" />
     </div>
-    <div class="Layout_space_between report-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%" />
     </div>
 
-    <div id="repot-list" class="marginTop">
-      <span class="backStyle"> 关联任务</span>
-      <div v-for="(item, index) in fromCreateData.taskDetailList" :key="index" class="Layout_flex_start report-taskList">
-        <span>{{ item.taskId }}</span>
-        <span>{{ item.name }}</span>
-      </div>
+    <span class="backStyle">关联任务</span>
+    <div
+      v-for="(item, index) in fromCreateData.taskDetailList"
+      :key="index"
+      class="Layout_flex_start report-taskList"
+    >
+      <span>{{ item.taskId }}</span>
+      <span>{{ item.name }}</span>
+    </div>
+
+    <div class="backStyle marginTop">报告预览</div>
 
-      <div class="backStyle marginTop"> 报告预览</div>
-      <div class="Layout_space_between reportName_style" style="border-bottom: 0.5px solid #eee;margin-bottom: 15px; padding-bottom: 10px;">
-        <span class="backStyle report-name"> {{ fromCreateData.reportName }}</span>
+    <div id="repot-list" class="marginTop">
+      <div
+        class="Layout_space_between reportName_style"
+        style="border-bottom: 0.5px solid #eee;margin-bottom: 15px; padding-bottom: 10px;"
+      >
+        <span class="backStyle report-name">{{ fromCreateData.reportName }}</span>
         <span>报告人:{{ name }}</span>
       </div>
       <div v-html="fromCreateData.content" />
@@ -88,9 +96,6 @@ export default {
 
     sendReport() {
       if (this.form.name !== undefined) {
-        // const targetDom = document.getElementById('repot-list')
-        // const copyDom = targetDom.cloneNode(true)
-        // document.querySelector('body').appendChild(copyDom)
         setTimeout(() => {
           html2canvas(document.getElementById('repot-list'), { useCORS: true }).then(canvas => {
             const imgData = canvas.toDataURL('image/png', 1)
@@ -98,7 +103,6 @@ export default {
             const postData = { 'reportId': this.daily_Id, 'imgStr': sendImgData, 'url': window.location.href, 'emailUser': this.form.name ? this.form.name.join(',') : null, 'copyTo': this.form.names ? this.form.names.join(',') : null }
             dailyReportSendmail(postData).then(res => {
               res.code === 200 ? this.$message.success('报告发送中,请稍后进行邮件查收!') : this.$message.error('发送测试日报失败,请联系管理员!')
-              // document.querySelector('body').removeChild(copyDom)
               this.$emit('handleClose')
             })
           })
@@ -113,41 +117,41 @@ export default {
 
 <style lang="scss" scoped>
 .parent-style {
-  .report-taskList{
-    font-size:14px;
-    font-family:MicrosoftYaHei;
-    line-height:17px;
-    color:#333;
+  .report-taskList {
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    color: #333;
     margin: 10px 0 0 0;
-    opacity:1;
+    opacity: 1;
   }
   .report-name {
     font-size: 20px;
     margin-top: 15px;
-    color:#409EFF;
+    color: #409eff;
   }
 }
 .div1 {
-    width: 60px;
-    font-size: 14px;
-    font-family: MicrosoftYaHei;
-    color: rgba(51, 51, 51, 1);
-    line-height: 19px;
+  width: 60px;
+  font-size: 14px;
+  font-family: MicrosoftYaHei;
+  color: rgba(51, 51, 51, 1);
+  line-height: 19px;
+}
+.backStyle {
+  color: #333;
+  font-size: 14px;
+  font-weight: bold;
+}
+.report-div {
+  margin-top: 10px;
+}
+.marginTop {
+  margin-top: 30px;
+}
+.reportName_style {
+  border-bottom: 0.5px solid #eee;
+  margin-bottom: 15px;
+  padding-bottom: 10px;
 }
-  .backStyle {
-    color:#333;
-    font-size: 14px;
-    font-weight: bold;
-  }
-  .report-div {
-    margin-top: 10px;
-  }
-  .marginTop {
-    margin-top: 30px;
-  }
-  .reportName_style {
-    border-bottom: 0.5px solid #eee;
-    margin-bottom: 15px;
-    padding-bottom: 10px;
-  }
 </style>

+ 24 - 30
src/views/reportManagement/daily/dailyTemplate.vue

@@ -1,11 +1,6 @@
 <template>
   <!-- 新增测试日报 -->
-  <div
-    v-loading="loading"
-    class="parent-style"
-    element-loading-text="数据上传中,请稍后"
-    element-loading-spinner="el-icon-loading"
-  >
+  <div v-loading="loading" class="parent-style" element-loading-text="数据上传中,请稍后" element-loading-spinner="el-icon-loading">
     <el-form ref="fromCreateData" class="dailyFrom" :model="fromCreateData" :rules="serviceDataRules">
 
       <div style=" margin-bottom: 10px;">
@@ -19,7 +14,7 @@
         </el-option>
       </el-select>
 
-      <div v-if="selectTask" class="taskError">报告关联任务不能为空</div>
+      <div v-if="selectTask" class="taskError">关联任务不能为空</div>
 
       <div v-for="(item, index) in tasksDetailList" :key="index" class="Layout_space_between report-taskList">
         <span>{{ item.taskIdSting }}</span>
@@ -35,7 +30,6 @@
         <normal-area id="report-template" :value.sync="fromCreateData.content" :height="500" />
       </el-form-item>
     </el-form>
-
     <icon-display :details="taskid_arr" />
   </div>
 </template>
@@ -197,33 +191,33 @@ export default {
 .dailyFrom {
   >>> .el-form-item {
     margin-bottom: 0px;
-}
->>> .el-form-item__label {
+  }
+  >>> .el-form-item__label {
     color: #333;
-}
+  }
 }
 .parent-style {
-  .report-taskList{
-    font-size:14px;
-    font-family:MicrosoftYaHei;
-    line-height:17px;
-    color:rgba(102,102,102,1);
+  .report-taskList {
+    font-size: 14px;
+    font-family: MicrosoftYaHei;
+    line-height: 17px;
+    color: rgba(102, 102, 102, 1);
     margin-top: 10px;
-    opacity:1;
+    opacity: 1;
   }
 }
-  .backStyle {
-     color: #333;
-    font-size: 14px;
-    font-weight: bold;
-    border-radius: 4px;
-    margin: 10px 0;
-  }
+.backStyle {
+  color: #333;
+  font-size: 14px;
+  font-weight: bold;
+  border-radius: 4px;
+  margin: 10px 0;
+}
 
-  .taskError {
-    color: #F56C6C;
-    font-size: 12px;
-    line-height: 1;
-    padding-top: 4px;
-  }
+.taskError {
+  color: #f56c6c;
+  font-size: 12px;
+  line-height: 1;
+  padding-top: 4px;
+}
 </style>

+ 4 - 3
src/views/reportManagement/testPresentation.vue

@@ -18,7 +18,7 @@
           </div>
           <div class="Layout_space_between">
             <el-button v-if="!history" type="primary" size="medium" @click="returnNewest">返回最新</el-button>
-            <el-button v-if="history" type="primary" size="medium" @click="gethistoryData">查看老数据</el-button>
+            <el-button v-if="history" type="text" @click="gethistoryData">查看老数据</el-button>
             <el-button v-if="history" type="primary" size="medium" @click="getQueryData">新建{{ title }}</el-button>
           </div>
         </div>
@@ -224,6 +224,7 @@ export default {
     },
 
     async getList(e) { // 报告list
+      console.log(e, 'sousuo')
       this.loading = true
       this.history = true
       const indexPage = { bizId: localStorage.getItem('bizId'), pageSize: this.pageSize, curIndex: this.curIndex }
@@ -244,7 +245,7 @@ export default {
       }
       if (this.title === '提测报告') {
         const data = { bizId: localStorage.getItem('bizId'), pageSize: this.pageSize, curIndex: this.curIndex }
-        e ? indexPage.name = this.state : ''
+        e ? data.reportName = this.state : ''
         const res = await reportdelivertestList(data)
         if (res.code === 200) {
           this.tableData = res.data.list || []
@@ -472,7 +473,7 @@ export default {
       }
       if (this.title === '提测报告') {
         const data = { bizId: localStorage.getItem('bizId'), pageSize: this.pageSize, curIndex: this.curIndex }
-        e ? indexPage.name = this.state : ''
+        e ? data.name = this.state : ''
         const res = await launchTestList(data)
         if (res.code === 200) {
           this.tableData = res.data

+ 57 - 72
src/views/workbench/bugTableList.vue

@@ -1,13 +1,8 @@
 <template>
   <div>
-    <el-dropdown class="dropdown" trigger="click">
-      <span class="el-dropdown-link">
-        {{ title }}<i class="el-icon-arrow-down el-icon--right" />
-      </span>
-      <el-dropdown-menu slot="dropdown">
-        <el-dropdown-item v-for="(item, index) in bugProcessStatusList" :key="index" @click.native="handleClick(item)">{{ item.name }}</el-dropdown-item>
-      </el-dropdown-menu>
-    </el-dropdown>
+    <div class="table-top">
+      <label>{{ name }}的缺陷</label>
+    </div>
     <el-table
       ref="bug_tableHeader"
       size="small"
@@ -34,7 +29,7 @@
       <el-table-column prop="priorityName" label="缺陷等级" align="center" />
       <el-table-column prop="bugStatusName" label="状态" min-width="110" align="center">
         <template slot-scope="scope">
-          <statusChange :bug-id="scope.row.id" :status-code="Number(scope.row.status)" :bug-data="scope.row" :status-obj="statusObj" @bugGet="bugGetTableList" />
+          <statusChange :bug-id="scope.row.id" :status-code="Number(scope.row.status)" :bug-data="scope.row" :status-obj="statusObj" @bugGet="getNew" />
         </template>
       </el-table-column>
       <el-table-column prop="taskName" label="所属任务" align="center" min-width="250" show-overflow-tooltip />
@@ -47,9 +42,9 @@
     </el-table>
     <div align="right">
       <el-pagination
-        :page-sizes="[15, 30, 45, total]"
-        :current-page.sync="curIndex"
-        :page-size="pageSize"
+        :page-sizes="[10, 20, 30, total]"
+        :current-page.sync="pages.curIndex"
+        :page-size="pages.pageSize"
         background
         layout="total, sizes, prev, pager, next, jumper"
         :total="total"
@@ -67,7 +62,7 @@
           :drawer-show="drawerShow"
           @close="drawerShow = false"
           @delete="getBugSelfList"
-          @update="getBugList"
+          @update="bugGetTableList"
         />
       </div>
     </el-drawer>
@@ -76,7 +71,7 @@
 
 <script>
 import statusChange from '@/views/projectManage/bugList/details/statusChange'
-import { bugGetEnum, bugTeamList, bugSelfList } from '@/api/defectManage'
+import { bugGetEnum, bugList } from '@/api/defectManage'
 import BugDetails from '@/views/projectManage/bugList/details/index'
 import '@/styles/PublicStyle/index.scss'
 
@@ -93,61 +88,57 @@ export default {
     }
   },
   props: {
-    name: { type: String, default: null },
-    bizId: { type: Number, default: null },
-    teamId: { type: Number, default: null }
+    idList: {
+      type: Array,
+      default: () => [],
+      required: true
+    },
+    name: {
+      type: String,
+      default: '',
+      required: false
+    }
   },
   data() {
     return {
-      title: '待团队成员处理',
       tableData: [], // tableData
       statusCode: 1, // 当前筛选的状态
-      pageSize: 15, // 分页
-      curIndex: 1, // 分页
+      pages: {
+        pageSize: 10, // 分页
+        curIndex: 1 // 分页
+      },
       total: 0, // 总数
       bugQuery: '', // bug详情
       drawerShow: false, // drawer展示
-      bugProcessStatusList: [], // 筛选
       statusObj: null // 状态对象
     }
   },
-  computed: {
-    clickCount() {
-      return this.$store.state.app.clickCount
-    }
-  },
   watch: {
-    clickCount(newVal, oldVal) {
-      this.drawerShow = false
+    idList: {
+      handler(newV) {
+        this.bugGetTableList()
+      },
+      deep: true
+    },
+    name: {
+      handler(newV) {},
+      immediate: true
     }
   },
   created() {
     this.getBugSelect()
   },
   methods: {
-    handleClick(vel) {
-      this.title = vel.name
-      this.statusCode = vel.code
-      this.curIndex = 1
-      this.bugGetTableList()
-    },
-    getBugSelect() { //  获取下拉菜单option
-      bugGetEnum().then(res => {
-        if (this.name === '个人') {
-          this.title = '待我处理'
-          this.bugProcessStatusList = res.data.bugProcessStatusList // 个人
-        }
-        if (this.name === '团队') {
-          this.title = '待团队成员处理'
-          this.bugProcessStatusList = res.data.bugProcessTeamStatusList // 待团队成员处理
-        }
+    async getBugSelect() { //  获取下拉菜单option
+      const res = await bugGetEnum()
+      if (res.code === 200) {
         this.statusObj = {
           bugEnumList: res.data.bugEnumList, // status
           repairResultEnumList: res.data.repairResultEnumList, // 修复结果
           bugReasonEnumList: res.data.bugReasonEnumList, // 缺陷原因
           theBugTypeEnumList: this.deleteChild(res.data.theBugTypeEnumList) // 缺陷类型
         }
-      })
+      }
     },
     deleteChild(arr) { // 删除无用子属性
       const bfs = arr => {
@@ -162,34 +153,17 @@ export default {
       bfs(arr)
       return arr
     },
-    async bugGetTableList(name, value) {
-      const teamSearchInfo = value || { teamId: this.teamId, bizId: this.bizId }
-      if (value) {
-        this.curIndex = 1
+    async bugGetTableList() { // 获取缺陷列表
+      if (this.idList.length === 0) {
+        this.tableData = []
+        return
       }
-      const pageInfoDO = {}
-      pageInfoDO.pageSize = this.pageSize
-      pageInfoDO.curIndex = this.curIndex
-      if (this.name === '团队') {
-        const res = await bugTeamList({ pageInfoDO, status: this.statusCode, teamSearchInfo })
-        if (res.code === 200) {
-          this.$forceUpdate()
-          this.tableData = res.data.map(item => { return { ...item, isSelected: false } })
-          this.total = res.total
-        }
-      }
-      if (this.name === '个人') {
-        const res = await bugSelfList({ pageInfoDO, status: this.statusCode })
-        if (res.code === 200) {
-          this.$forceUpdate()
-          this.tableData = res.data.map(item => { return { ...item, isSelected: false } })
-          this.total = res.total
-        }
+      const res = await bugList({ ids: this.idList, ...this.pages })
+      if (res && res.code === 200) {
+        this.tableData = res.data
+        this.total = res.total
       }
     },
-    getBugList() {
-      this.bugGetTableList()
-    },
     getBugSelfList() {
       this.bugGetTableList()
       this.$nextTick(() => {
@@ -197,10 +171,10 @@ export default {
       })
     },
     handleSizeChange(val) {
-      this.pageSize = val
+      this.pages.pageSize = val
     },
     handleCurrentChange(val) {
-      this.curIndex = val
+      this.pages.curIndex = val
       this.bugGetTableList()
     },
     click_bugName(e) {
@@ -208,12 +182,23 @@ export default {
         JSON.stringify(this.tableData.filter(value => value.id === e)[0])
       )
       this.drawerShow = true
+    },
+    getNew() { // 通知父组件,让父组件去执行子组件的数据更新
+      this.$emit('change', '缺陷')
     }
   }
 }
 </script>
 
 <style scoped lang="scss">
+.table-top {
+  color: #333333;
+  font-size: 16px;
+  width: 100%;
+  padding: 10px 15px 0 15px;
+  display: flex;
+  justify-content: space-between;
+}
 .dropdown {
   margin: 20px;
 }

+ 322 - 0
src/views/workbench/components/statisticsSection.vue

@@ -0,0 +1,322 @@
+<template>
+  <div class="drawer-statistics" :class="{'drawer-padding': !show}">
+    <transition name="fade-drawer">
+      <div v-show="show" class="statistics">
+        <div class="statistics-data">
+          <h2>{{ title }}统计</h2>
+          <div class="data-main">
+            <div v-for="(item,index) in statisticsList" :key="title+'-statistics-'+index" class="data-item" :class="{'active': clickItem === index }" @click="changeData(item.idList,item.label);clickItem=index">
+              <label>
+                {{ item.label }}
+                <template v-if="(title === '需求' || title ==='任务')&& index<5">
+                  <el-tooltip class="item" effect="dark" :content="tips[title][index]" placement="top">
+                    <i class="el-icon-warning" />
+                  </el-tooltip>
+                </template>
+              </label>
+              <div
+                class="number"
+                :class="{'color-red': (index === 0 || index === 1) && item.total>0}"
+              >
+                {{ item.total }}
+              </div>
+            </div>
+          </div>
+        </div>
+        <div v-if="title !=='缺陷'" class="statistics-chart">
+          <h2>未上线{{ title }}状态分布</h2>
+          <div class="chart-contain">
+            <normal-echart v-if="echartsOption" :chart-id="type+title" :option="echartsOption" @onClick="chartChange" />
+          </div>
+        </div>
+        <div v-if="title ==='缺陷'" class="statistics-chart">
+          <div class="two-title">
+            <h2>我提报的-未完成缺陷状态分布</h2>
+            <h2>提给我的-未完成缺陷状态分布</h2>
+          </div>
+          <div class="two-chart-contain">
+            <div class="chart-contain">
+              <normal-echart v-if="echartsOption" :chart-id="type+title+'1'" :option="echartsOption" @onClick="chartChange" />
+            </div>
+            <div class="chart-contain">
+              <normal-echart v-if="echartsOption2" :chart-id="type+title+'2'" :option="echartsOption2" @onClick="chartChange" />
+            </div>
+          </div>
+        </div>
+      </div>
+    </transition>
+    <div class="drawer" @click="show = !show">
+      <i v-show="show" class="el-icon-arrow-up" />
+      <i v-show="!show" class="el-icon-arrow-down" />
+    </div>
+  </div>
+</template>
+
+<script>
+import normalEchart from '@/components/chart/normalEchart'
+export default {
+  name: 'StatisticsSectionVue',
+  components: { normalEchart },
+  props: {
+    searchForm: { // 搜索项的信息
+      type: Object,
+      default: () => {
+        return {
+          teamId: null,
+          bizId: null
+        }
+      },
+      required: true
+    },
+    type: { // 个人还是团队
+      type: String,
+      default: 'team',
+      required: false
+    },
+    title: {
+      type: String,
+      default: '需求',
+      required: false
+    },
+    requestObj: { // 请求的接口
+      type: Object,
+      default: () => null,
+      required: false
+    }
+  },
+  data() {
+    return {
+      show: true,
+      statisticsList: [],
+      echartsOption: null,
+      echartsOption2: null,
+      tips: {
+        '需求': [
+          '交付日期为今天,且状态仍未变更为“已上线”的需求数量。',
+          '已过交付日期,状态仍未变更为“已上线”的需求数量。',
+          '交付日期在本周内的需求数量。',
+          '交付日期在本周,且状态仍未变更为“已上线”的需求数量。',
+          '交付日期在下周的需求数量。'
+        ],
+        '任务': [
+          '交付日期为今天,且状态仍未变更为“已上线”的任务数量。',
+          '已过交付日期,状态仍未变更为“已上线”的任务数量。',
+          '交付日期在本周内的任务数量。',
+          '交付日期在本周,且状态仍未变更为“已上线”的任务数量。',
+          '交付日期在下周任务数量。'
+        ]
+      },
+      clickItem: 0
+    }
+  },
+  watch: {
+    searchForm: {
+      handler(newV) {
+        this.initData()
+      },
+      deep: true,
+      immediate: true
+    },
+    title: {
+      handler(newV) {
+        this.clickItem = 0
+        this.initData()
+      },
+      immediate: true
+    }
+  },
+  methods: {
+    initData() { // 初始化
+      if (this.requestObj) {
+        const { requestData, requestChart } = this.requestObj
+        this.getData(requestData)
+        if (this.title === '缺陷') {
+          this.getChart(requestChart, '3')
+          this.getChart(requestChart, '5')
+        } else {
+          this.getChart(requestChart)
+        }
+      }
+    },
+    async getData(requestUrl) { // 获取顶部数据
+      const res = await requestUrl({ teamSearchInfo: this.searchForm })
+      if (res.code === 200) {
+        this.statisticsList = res.data || []
+        if (this.statisticsList.length > 0) {
+          const show = this.statisticsList[0].total + this.statisticsList[1].total > 0
+          this.$emit('showTips', this.title, show)
+        }
+        // 如果是点击左边6个区域的,保留idList,如果点击是图表,默认回到左边第一个区域中的idList
+        if (this.clickItem < 0) {
+          this.clickItem = 0
+          this.changeData(this.statisticsList[0].idList, this.statisticsList[0].label)
+        } else {
+          this.changeData(this.statisticsList[this.clickItem].idList, this.statisticsList[this.clickItem].label)
+        }
+      }
+    },
+    async getChart(requestUrl, type) { // 获取图表数据
+      if (type) {
+        const res = await requestUrl({ teamSearchInfo: this.searchForm, type: type })
+        if (type === '3') {
+          this.echartsOption = this.setChart(res.data)
+        } else if (type === '5') {
+          this.echartsOption2 = this.setChart(res.data)
+        }
+      } else {
+        const res = await requestUrl({ teamSearchInfo: this.searchForm })
+        this.echartsOption = this.setChart(res.data)
+      }
+    },
+    setChart(chartData) { // 设置图表options
+      const childArr = chartData.xaxis.map((item, index) => {
+        return {
+          value: chartData.yaxis[0] && chartData.yaxis[0].data[index] || null,
+          idList: chartData.yaxis[0] && chartData.yaxis[0].idList[index] || []
+        }
+      })
+      const option = {
+        color: ['#3AA1FF'],
+        tooltip: { trigger: 'axis', axisPointer: { type: 'line' }}, // 默认为直线,可选为:'line' | 'shadow'
+        grid: { left: '0', right: '0', top: '10%', bottom: '0', containLabel: true },
+        xAxis: [{ type: 'category', data: chartData.xaxis, axisLabel: { interval: 0, rotate: 15 }, axisTick: { alignWithLabel: true }}],
+        yAxis: [{ type: 'value', axisLine: { show: false }, splitLine: { lineStyle: { type: 'dashed' }}}],
+        series: [{
+          name: '数量', type: 'bar', barWidth: '20px', data: childArr,
+          itemStyle: { normal: { label: { show: true, formatter: '{c}', position: 'top' }}}
+        }]
+      }
+      return option
+    },
+    chartChange(params) { // 点击图表产生触发列表更改
+      this.clickItem = -1
+      this.changeData(params.data.idList, params.name)
+    },
+    changeData(idList, name) { // 点击顶部数据触发列表更改
+      this.$emit('change', this.title, idList, name)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.drawer-statistics {
+  position: relative;
+  width: 100%;
+  padding-bottom: 1px;
+}
+.drawer-padding {
+  padding-bottom: 13px;
+}
+//.fade-drawer-enter-active {
+//  transition: all 1.5s ease
+//}
+//.fade-drawer-leave-active {s
+//  transition: all 1.5s ease
+//}
+//.fade-drawer-enter, .fade-drawer-leave-to {
+//  transition: all 1.5s ease;
+//}
+.statistics {
+  position: relative;
+  width: 100%;
+  height: 474px;
+  padding: 27px 35px;
+  display: grid;
+  grid-template-columns: 40% 60%;
+  h2 {
+    color: #333333;
+    font-size: 16px;
+    margin-bottom: 20px;
+    font-weight: bold;
+  }
+}
+.statistics-data {
+  .data-main{
+    width: 80%;
+    display: grid;
+    grid-template-columns: repeat(2,188px);
+    grid-column-gap: 30px;
+    grid-template-rows: repeat(3,111px);
+    grid-row-gap: 20px;
+  }
+  .data-item {
+    cursor:pointer;
+    box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
+    border-radius: 5px;
+    display: grid;
+    grid-template-rows: repeat(2,50%);
+    align-items: center;
+    padding: 5px 0 5px 11px;
+    label {
+      font-weight: 400;
+      color: #666666;
+      cursor:pointer;
+    }
+    .number {
+      font-weight: 500;
+      color: #333333;
+      font-size: 26px;
+    }
+    .color-red {
+      color:#E02020;
+    }
+  }
+  .active {
+    border: 1px solid #409EFF;
+    box-shadow: 0px 6px 12px rgba(64, 158, 255, 0.2);
+  }
+}
+.statistics-chart {
+  .two-title {
+    width: 100%;
+    display: grid;
+    grid-template-columns: repeat(2,47.5%);
+    grid-column-gap: 5%;
+  }
+  .two-chart-contain {
+    width: 100%;
+    height: 100%;
+    display: grid;
+    grid-template-columns: repeat(2,47.5%);
+    grid-column-gap: 5%;
+  }
+  .chart-contain {
+    width: 100%;
+    height: 380px;
+  }
+}
+.drawer-statistics:after {
+  content: '';
+  width: 100%;
+  height: 14px;
+  background-color: #F2F3F6;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+}
+.drawer {
+  width: 26px;
+  height: 12px;
+  left: 50%;
+  position: absolute;
+  bottom: 1px;
+  transform: translateX(-50%);
+  z-index: 98;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #666666;
+  font-weight: bold;
+  background-color: #ffffff;
+  // border-bottom: 1px solid #999999;
+  // border-left: 1px solid #999999;
+  // border-right: 1px solid #999999;
+  box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.2);
+  border-bottom-left-radius: 2px;
+  border-bottom-right-radius: 2px;
+}
+.drawer-pull {
+  top: 1px;
+}
+</style>

+ 42 - 0
src/views/workbench/mixins/websocket.js

@@ -0,0 +1,42 @@
+import { ws } from '@/apiConfig/requestIP'
+export default {
+  name: 'test',
+  data() {
+    return {
+      webSock: null
+    }
+  },
+  created() {
+    this.initWebSocket()
+  },
+  destroyed() {
+    this.websock.close() // 离开路由之后断开websocket连接
+  },
+  methods: {
+    initWebSocket() { // 初始化weosocket
+      const wsUri = `ws://${ws}/zhihui-websocket/self/remind?ename=${localStorage.getItem('username')}`
+      this.websock = new WebSocket(wsUri)
+      this.websock.onmessage = this.websocketonmessage
+      this.websock.onopen = this.websocketonopen
+      this.websock.onerror = this.websocketonerror
+      this.websock.onclose = this.websocketclose
+    },
+    websocketonopen() { // 连接建立之后执行send方法发送数据
+
+    },
+    websocketonerror() { // 连接建立失败重连
+      console.log('websocket断连')
+      this.initWebSocket()
+    },
+    websocketsend(Data) { // 数据发送
+      this.websock.send(Data)
+    },
+    websocketonmessage(e) { // 数据接收
+      // const data = JSON.parse(e.data)
+      // console.log(data)
+    },
+    websocketclose(e) { // 关闭
+      this.websock.close()
+    }
+  }
+}

+ 9 - 2
src/views/workbench/person/components/myFullCalendar.vue

@@ -189,8 +189,10 @@ export default {
         div.style.paddingTop = '0'
         const icon = document.createElement('i')
         if (item.innerText.match(/排期/)) {
+          item.innerText = item.innerText.replace(/排期:/, '')
           icon.className = 'el-icon-document'
         } else {
+          item.innerText = item.innerText.replace(/日程:/, '')
           icon.className = 'el-icon-date'
         }
         div.appendChild(icon)
@@ -251,7 +253,7 @@ export default {
 >>>.fc-title {
   line-height: 15px;
   padding-left: 4px;
-  color:#333333 !important;
+  font-weight: bold !important;
 }
 >>>.fc-event-container .fc-content {
   text-overflow: inherit;
@@ -280,6 +282,12 @@ export default {
 >>>.fc-today {
   background-color: rgba(235,172,0,0.1);
 }
+>>>.fc th{
+  border-color: #D8D8D8;
+}
+>>>.fc td {
+  border-color: #D8D8D8;
+}
 </style>
 
 // fullcalendar
@@ -353,7 +361,6 @@ export default {
   .fc-content
   span {
   font-size: 6px;
-  font-weight: normal !important;
   display: inline-block;
   position: relative;
   bottom: 3px;

+ 172 - 17
src/views/workbench/person/index.vue

@@ -10,9 +10,15 @@
           <el-tabs v-model="activeName" @tab-click="handleClick">
             <el-tab-pane label="个人日程" name="1" />
             <el-tab-pane label="项目" name="2" />
-            <el-tab-pane label="需求" name="3" />
-            <el-tab-pane label="任务" name="4" />
-            <el-tab-pane label="缺陷" name="5" />
+            <el-tab-pane name="3">
+              <span slot="label">需求<div v-show="tabTips.require" class="tips" /> </span>
+            </el-tab-pane>
+            <el-tab-pane name="4">
+              <span slot="label">任务<div v-show="tabTips.task" class="tips" /> </span>
+            </el-tab-pane>
+            <el-tab-pane name="5">
+              <span slot="label"><span style="margin-right: 5px">缺陷</span> <div v-show="tabTips.bug" class="tips-last" /> </span>
+            </el-tab-pane>
           </el-tabs>
         </div>
       </el-header>
@@ -52,17 +58,68 @@
       </el-container>
       <el-container v-if="activeName === '3'">
         <section class="main-section">
-          <needs-list :search-form="searchForm" type="person" />
+          <statistics-section
+            ref="statistics-section"
+            :search-form="searchForm"
+            type="person"
+            title="需求"
+            :request-obj="{
+              requestData:getPersonalRequireSummary,
+              requestChart:getPersonalRequireDisData
+            }"
+            @showTips="changeTips"
+            @change="changeData"
+          />
+          <needs-list
+            ref="needs-list"
+            :name="listName.requireList"
+            :id-list="idList.requireList"
+            @change="changeStatus"
+          />
         </section>
       </el-container>
       <el-container v-if="activeName === '4'">
         <section class="main-section">
-          <task-list :search-form="searchForm" type="person" />
+          <statistics-section
+            ref="statistics-section"
+            :search-form="searchForm"
+            type="person"
+            title="任务"
+            :request-obj="{
+              requestData:getPersonalTaskSummary,
+              requestChart:getPersonalTaskDisData
+            }"
+            @showTips="changeTips"
+            @change="changeData"
+          />
+          <task-list
+            ref="task-list"
+            :name="listName.taskList"
+            :id-list="idList.taskList"
+            @change="changeStatus"
+          />
         </section>
       </el-container>
-      <el-container v-show="activeName === '5'">
+      <el-container v-if="activeName === '5'">
         <section class="main-section">
-          <bugTableList ref="bugTableDialog" :name="'个人'" />
+          <statistics-section
+            ref="statistics-section"
+            :search-form="searchForm"
+            type="person"
+            title="缺陷"
+            :request-obj="{
+              requestData:getPersonalBugSummary,
+              requestChart:getPersonalBugDisDataBy
+            }"
+            @showTips="changeTips"
+            @change="changeData"
+          />
+          <bug-tableList
+            ref="bug-tableList"
+            :name="listName.bugList"
+            :id-list="idList.bugList"
+            @change="changeStatus"
+          />
         </section>
       </el-container>
       <!-- 工作台简介 -->
@@ -181,7 +238,13 @@ import {
   queryTeamInfoList,
   showTeamAndMemberEnum,
   queryWorkListByTime,
-  deleteSelfSchedule
+  deleteSelfSchedule,
+  getPersonalRequireSummary,
+  getPersonalRequireDisData,
+  getPersonalTaskSummary,
+  getPersonalTaskDisData,
+  getPersonalBugSummary,
+  getPersonalBugDisDataBy
 } from '@/api/workSchedule.js'
 import { scheduleDelete } from '@/api/projectViewDetails'
 import modifySchedule from '@/views/projectManage/projectList/components/modifySchedule'
@@ -193,6 +256,8 @@ import projectList from '@/views/workbench/team/components/projectList'
 import needsList from '@/views/workbench/team/components/needsList'
 import taskList from '@/views/workbench/team/components/taskList'
 import bugTableList from '@/views/workbench/bugTableList.vue'
+import statisticsSection from '@/views/workbench/components/statisticsSection'
+import websocket from '@/views/workbench/mixins/websocket'
 
 export default {
   components: {
@@ -204,7 +269,8 @@ export default {
     bugTableList,
     projectList,
     needsList,
-    taskList
+    taskList,
+    statisticsSection
   },
   provide() {
     return {
@@ -212,6 +278,7 @@ export default {
       controlDe: () => this.controlDe
     }
   },
+  mixins: [websocket],
   data() {
     return {
       activeName: '1', // 顶部导航栏
@@ -263,10 +330,43 @@ export default {
         'rgba(193,67,96,0.2)', 'rgba(211,87,35,0.2)', 'rgba(211,96,118,0.2)', 'rgba(179,201,58,0.2)', 'rgba(64,127,232,0.2)',
         'rgba(86,68,93,0.2)', 'rgba(111,33,8,0.2)', 'rgba(192,53,70,0.2)', 'rgba(92,25,107,0.2)', 'rgba(73,10,61,0.2)',
         'rgba(189,21,80,0.2)', 'rgba( 31,78,95,0.2)', 'rgba(241,0,229,0.2)', 'rgba(114,0,218,0.2)', 'rgba(70,14,68,0.2)'
-      ]
+      ],
+      tabTips: { // 顶部tab红点提示
+        require: true,
+        task: true,
+        bug: true
+      },
+      idList: {// 所选项的idList
+        requireList: [],
+        taskList: [],
+        bugList: []
+      },
+      listName: {
+        requireList: '',
+        taskList: '',
+        bugList: ''
+      },
+      getPersonalRequireSummary: getPersonalRequireSummary, // 需求头部统计数据接口
+      getPersonalRequireDisData: getPersonalRequireDisData, // 需求按状态统计数据接口
+      getPersonalTaskSummary: getPersonalTaskSummary, // 任务头部统计数据接口
+      getPersonalTaskDisData: getPersonalTaskDisData, // 任务按状态统计数据接口
+      getPersonalBugSummary: getPersonalBugSummary, // 缺陷头部统计数据接口
+      getPersonalBugDisDataBy: getPersonalBugDisDataBy// 缺陷按状态统计数据接口
+    }
+  },
+  watch: {
+    activeName: {
+      handler(newV) {
+        Number(newV) > 0
+          ? this.$router.push({ path: this.$route.path, query: { page: newV }})
+          : this.$router.push({ path: this.$route.path, query: { page: '1' }})
+      }
     }
   },
   created() {
+    this.$nextTick(() => {
+      this.activeName = this.$route.query.page ? this.$route.query.page : '1'
+    })
     this.showTeamAndMemberEnum()
   },
   mounted() {
@@ -274,8 +374,8 @@ export default {
   },
   methods: {
     handleClick() {
-      if (this.activeName === '5') {
-        this.$refs.bugTableDialog.bugGetTableList('个人', {})
+      if (this.activeName === '5' && this.$refs.bugTableDialog) {
+        this.$refs.bugTableDialog.bugGetTableList()
       }
     },
     showSchedule(e) { // 查看日程详情
@@ -386,8 +486,8 @@ export default {
             end: moment(item.endTime).add(1, 'day').toDate(),
             detailData: item,
             className: item.origin ? 'schedule1' : 'schedule2',
-            backgroundColor: this.bgColorList[(index - num) % 20],
-            textColor: this.colorList[(index - num) % 20]
+            backgroundColor: this.colorList[(index - num) % 20],
+            textColor: '#FFFFFF'
           }
         })
       }
@@ -442,6 +542,38 @@ export default {
     handleDelete(data) { // 日程列表中的删除
       this.nowDetailData = data
       this.deleteSchedule()
+    },
+    changeTips(e, show) { // 红点展示更改
+      if (e === '需求') {
+        this.tabTips.require = show
+      } else if (e === '任务') {
+        this.tabTips.task = show
+      } else if (e === '缺陷') {
+        this.tabTips.bug = show
+      }
+    },
+    changeData(e, idList, name) { // 数据变更
+      if (e === '需求') {
+        this.idList.requireList = idList
+        this.listName.requireList = name
+      } else if (e === '任务') {
+        this.idList.taskList = idList
+        this.listName.taskList = name
+      } else if (e === '缺陷') {
+        this.idList.bugList = idList
+        this.listName.bugList = name
+      }
+    },
+    changeStatus(e) { // 子组件修改状态
+      this.$refs['statistics-section'].initData()
+    },
+    websocketonmessage(e) { // websocket数据接收
+      const { bugHasReminding, requireHasReminding, taskHasReminding } = JSON.parse(e.data)
+      this.tabTips = { // 顶部tab红点提示
+        require: requireHasReminding,
+        task: taskHasReminding,
+        bug: bugHasReminding
+      }
     }
   }
 }
@@ -463,20 +595,43 @@ export default {
 @include hide-open-header;
 .bg-person {
   @include bg-project;
+  padding-top: 60px;
 }
 .main-header {
   @include main-header;
+  margin-bottom: 0;
+  border-bottom: 1px solid rgba(155, 155, 155, .2);
+  >>>.el-tabs__nav-wrap::after {
+    height: 0;
+  }
   .top-tabs {
     position: absolute;
     left: 50%;
     transform: translate(-50%, 0);
   }
-}
-.main-header::after {
-  @include main-header-after;
+  .tips {
+    height: 8px;
+    width: 8px;
+    background-color: #E02020;
+    border-radius: 50%;
+    position: absolute;
+    top: 18px;
+    right: 14px;
+  }
+  .tips-last {
+    height: 8px;
+    width: 8px;
+    background-color: #E02020;
+    border-radius: 50%;
+    position: absolute;
+    top: 18px;
+    right: 0;
+  }
 }
 .main-section {
   @include main-section;
+  margin-right: 0;
+  width: calc(100% - 10px);
   .title-name {
     font-size:20px;
     font-family:PingFangSC-Medium;

+ 37 - 67
src/views/workbench/team/components/needsList.vue

@@ -1,10 +1,7 @@
 <template>
   <div>
-    <div class="search-control">
-      <span :class="{'color-blue': status===null}" @click="setStatus(null)">全部</span>
-      <span :class="{'color-blue': status===1}" @click="setStatus(1)">进行中的需求</span>
-      <span :class="{'color-blue': status===0}" @click="setStatus(0)">未开始的需求</span>
-      <span :class="{'color-blue': status===2}" @click="setStatus(2)">已上线的需求</span>
+    <div class="table-top">
+      <label>{{ name }}的需求</label>
       <span class="new-tab-open">
         <el-switch
           v-model="newTabOpen"
@@ -72,26 +69,26 @@
           <span>{{ scope.row.pmMemberInfoResponse.name }}</span>
         </template>
       </el-table-column>
+      <el-table-column label="交付日期" min-width="200" align="center" show-overflow-tooltip>
+        <template slot-scope="scope">{{ scope.row.optionsObject === null? '' :scope.row.optionsObject.endTime }}</template>
+      </el-table-column>
       <el-table-column label="需求方向" min-width="150" align="center" show-overflow-tooltip>
         <template v-slot="scope">
           {{ scope.row.rqmtOrntNames }}
         </template>
       </el-table-column>
-      <el-table-column label="跟版客户端" width="200" align="center" prop="referredClientTypeName" show-overflow-tooltip />
-      <el-table-column label="PRD链接" width="300" align="left" prop="mrdUrl" show-overflow-tooltip>
+      <el-table-column label="跟版客户端" width="150" align="center" prop="referredClientTypeName" show-overflow-tooltip />
+      <el-table-column label="PRD链接" width="150" align="left" prop="mrdUrl" show-overflow-tooltip>
         <template v-slot="scope">
           <el-link :href="scope.row.mrdUrl" class="mrdUrl" target="_blank">{{ scope.row.mrdUrl }}</el-link>
         </template>
       </el-table-column>
-      <el-table-column label="交付日期" min-width="200" align="center" show-overflow-tooltip>
-        <template slot-scope="scope">{{ scope.row.optionsObject === null? '' :scope.row.optionsObject.endTime }}</template>
-      </el-table-column>
       <el-table-column label="任务数量" width="100" align="center" prop="taskCount" show-overflow-tooltip />
       <el-table-column label="缺陷数量" width="100" align="center" prop="bugCount" show-overflow-tooltip />
     </el-table>
     <div align="right">
       <el-pagination
-        :page-sizes="[15, 30, 45, total]"
+        :page-sizes="[10, 20, 30, total]"
         :current-page.sync="pages.curIndex"
         :page-size="pages.pageSize"
         background
@@ -104,61 +101,48 @@
   </div>
 </template>
 <script>
-import {
-  requirementSelfList,
-  requirementTeamList
-} from '@/api/workSchedule'
-import '@/styles/PublicStyle/index.scss'
-import { configShowRequireStatusEnum, updateRequirementStatus } from '@/api/requirement'
+import { configShowRequireStatusEnum, updateRequirementStatus, getRequirement } from '@/api/requirement'
 import extraUrgent from '@/assets/extraUrgent.png'
 export default {
   props: {
-    searchForm: {
-      type: Object,
-      default: () => {
-        return {
-          teamId: null,
-          bizId: null
-        }
-      },
+    idList: {
+      type: Array,
+      default: () => [],
       required: true
     },
-    type: {
+    name: {
       type: String,
-      default: 'person',
-      required: true
+      default: '',
+      required: false
     }
   },
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       extraUrgent: extraUrgent, // 紧急图片
       needsDataList: [], // 需求列表
       allStatus: [], // 状态列表
       pages: {
-        pageSize: 15,
+        pageSize: 10,
         curIndex: 1
       },
-      total: 0,
-      status: null // 列表状态
+      total: 0
     }
   },
   watch: {
-    searchForm: {
+    idList: {
       handler(newV) {
         this.getNeedsList()
       },
       deep: true
     },
-    type: {
-      handler(newV) {
-        this.getNeedsList()
-      }
+    name: {
+      handler(newV) {},
+      immediate: true
     }
   },
   created() {
     this.getTaskStatus()
-    this.getNeedsList()
   },
   methods: {
     setStatus(val) {
@@ -180,26 +164,16 @@ export default {
         this.allStatus = []
         this.allStatus = res1.data.requirementStatus
       }
-      // const res = await showRequirementEnum()
-      // if (res.code === 200) {
-      //   this.allStatus = res.data.requirementStatus
-      // }
     },
     async getNeedsList() { // 获取需求列表
-      const params = {
-        teamSearchInfo: this.searchForm,
-        status: this.status,
-        pageInfoDO: this.pages
-      }
-      let res = null
-      if (this.type === 'person') {
-        res = await requirementSelfList(params)
-      } else if (this.type === 'team') {
-        res = await requirementTeamList(params)
+      if (this.idList.length === 0) {
+        this.needsDataList = []
+        return
       }
+      const res = await getRequirement({ ids: this.idList, ...this.pages })
       if (res && res.code === 200) {
-        this.needsDataList = res.data
-        this.total = res.total
+        this.needsDataList = res.data.list
+        this.total = res.data.total
       }
     },
     async changeStatus(e) { // 状态改变
@@ -207,7 +181,8 @@ export default {
       const res = await updateRequirementStatus({ id: e.id, status: e.status, modifier: modifier })
       if (res.code === 200) {
         this.$message({ message: '修改成功', type: 'success', offset: 150 })
-        this.getNeedsList()
+        // this.getNeedsList()
+        this.$emit('change', '需求')// 通知父组件,让父组件去执行子组件的数据更新
       }
     },
     needs_link(id) {
@@ -241,9 +216,13 @@ export default {
 >>>.el-row .el-col {
   margin: 10px 0;
 }
-.new-tab-open {
-  position: absolute;
-  right: 0px;
+.table-top {
+  color: #333333;
+  font-size: 16px;
+  width: 100%;
+  padding: 10px 15px 0 15px;
+  display: flex;
+  justify-content: space-between;
 }
 .color-blue {
 	color:#409EFF;
@@ -309,15 +288,6 @@ export default {
   background: rgba(245,108,108,0.17);
   color: rgba(245,108,108,1);
 }
-.search-control {
-	padding: 30px 17px 0;
-	color: #333B4A;
-	font-size: 14px;
-	span {
-		margin-right: 35px;
-		cursor: pointer;
-	}
-}
 .div_priority {
   text-align: center;
   color: #ffffff;

+ 8 - 2
src/views/workbench/team/components/projectList.vue

@@ -12,6 +12,7 @@
         />
       </span>
     </div>
+    <div class="splice-line" />
     <el-table
       ref="planTable"
       :data="projectList"
@@ -95,7 +96,7 @@ export default {
   },
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       projectList: [], // 需求列表
       allStatus: [ // 状态列表
         { value: 0, name: '未开始' },
@@ -239,7 +240,7 @@ export default {
   @include setStatus(#7ED321)
 }
 .search-control {
-	padding: 30px 17px 0;
+	padding: 10px 17px 10px;
 	color: #333B4A;
 	font-size: 14px;
 	span {
@@ -272,4 +273,9 @@ export default {
     text-overflow: ellipsis;
   }
 }
+.splice-line {
+  height: 14px;
+  width: 100%;
+  background-color: #F2F3F6;
+}
 </style>

+ 38 - 69
src/views/workbench/team/components/taskList.vue

@@ -1,10 +1,7 @@
 <template>
   <div>
-    <div class="search-control">
-      <span :class="{'color-blue': status===null}" @click="setStatus(null)">全部</span>
-      <span :class="{'color-blue': status===1}" @click="setStatus(1)">进行中的任务</span>
-      <span :class="{'color-blue': status===0}" @click="setStatus(0)">未开始的任务</span>
-      <span :class="{'color-blue': status===2}" @click="setStatus(2)">已上线的任务</span>
+    <div class="table-top">
+      <label>{{ name }}的任务</label>
       <span class="new-tab-open">
         <el-switch
           v-model="newTabOpen"
@@ -63,7 +60,7 @@
           </div>
         </template>
       </el-table-column>
-      <el-table-column label="状态" width="150" align="left">
+      <el-table-column label="状态" width="150" align="center">
         <template slot-scope="scope">
           <el-select
             v-model="scope.row.status"
@@ -89,19 +86,19 @@
       <el-table-column label="测试负责人" width="100" align="center" show-overflow-tooltip>
         <template slot-scope="scope">{{ scope.row.qaObject ? scope.row.qaObject.name : '' }}</template>
       </el-table-column>
-      <el-table-column label="所属模块" width="250" align="center" show-overflow-tooltip>
+      <el-table-column label="交付日期" width="150" align="center" show-overflow-tooltip>
+        <template slot-scope="scope">{{ scope.row.optionsObject ? scope.row.optionsObject.endTime : '' }}</template>
+      </el-table-column>
+      <el-table-column label="所属模块" width="200" align="center" show-overflow-tooltip>
         <template slot-scope="scope">{{ scope.row.moduleInfoName }}</template>
       </el-table-column>
-      <el-table-column label="跟版客户端" width="250" align="center" show-overflow-tooltip>
+      <el-table-column label="跟版客户端" width="150" align="center" show-overflow-tooltip>
         <template slot-scope="scope">{{ scope.row.involveAppString || '无' }}</template>
       </el-table-column>
-      <el-table-column label="交付日期" width="250" align="center" show-overflow-tooltip>
-        <template slot-scope="scope">{{ scope.row.optionsObject ? scope.row.optionsObject.endTime : '' }}</template>
-      </el-table-column>
-      <el-table-column label="缺陷数量" width="250" align="center" show-overflow-tooltip>
+      <el-table-column label="缺陷数量" width="100" align="center" show-overflow-tooltip>
         <template slot-scope="scope">{{ scope.row.bugCount }}</template>
       </el-table-column>
-      <el-table-column label="任务进度" width="200" align="center">
+      <el-table-column label="任务进度" width="150" align="center">
         <template slot-scope="scope">
           <el-progress :percentage="Number(scope.row.rate && scope.row.rate.substring(0,4))" color="#409eff" />
         </template>
@@ -122,7 +119,7 @@
     <TestReport v-if="dialogTestReport" ref="TestReport" />
     <DailyReport v-if="dialogDailyReport" ref="DailyReport" />
     <ReleaseReport v-if="dialogClientReport" ref="ClientReport" />
-    <taskDialog v-if="showTaskDialog" :show.sync="showTaskDialog" :task-id="taskId.id" :status-name="taskId.statusString" @getList="get_allTask" />
+    <task-dialog v-if="showTaskDialog" :show.sync="showTaskDialog" :task-id="taskId.id" :status-name="taskId.statusString" @getList="getNew" />
     <!-- 批量排期 -->
     <modify-schedule
       v-if="visibleSchedule"
@@ -139,9 +136,8 @@ import '@/styles/PublicStyle/index.scss'
 import TestReport from '@/views/reportManagement/components/TestingReport' // 提测
 import DailyReport from '@/views/reportManagement/components/DailyReport' // 日报
 import ReleaseReport from '@/views/reportManagement/components/ReleaseReport' // 准出
-import { taskSelfList, taskTeamList } from '@/api/workSchedule'
 import { taskUpdate } from '@/api/projectViewDetails'
-import { configShowTaskEnum, configShowTaskStatusEnum } from '@/api/taskIndex'
+import { configShowTaskEnum, configShowTaskStatusEnum, taskList } from '@/api/taskIndex'
 import modifySchedule from '@/views/projectManage/projectList/components/modifySchedule'
 import taskDialog from '@/views/projectManage/taskList/dialog/taskDialog' // 任务状态修改(已上线/已提测/已准出)
 import { dailyReportCheckStatus, reportreleaseCheckStatus, reportdelivertestCheckStatus } from '@/api/reportTemplate'
@@ -154,25 +150,20 @@ export default {
     modifySchedule
   },
   props: {
-    searchForm: {
-      type: Object,
-      default: () => {
-        return {
-          teamId: null,
-          bizId: null
-        }
-      },
+    idList: {
+      type: Array,
+      default: () => [],
       required: true
     },
-    type: {
+    name: {
       type: String,
-      default: 'person',
-      required: true
+      default: '',
+      required: false
     }
   },
   data() {
     return {
-      newTabOpen: false, // 是否新的tab页打开
+      newTabOpen: true, // 是否新的tab页打开
       imgUrl: imgUrl,
       tableList: [], // 排期bable验证
       showTaskDialog: false, // 状态弹窗
@@ -201,21 +192,19 @@ export default {
     }
   },
   watch: {
-    searchForm: {
+    idList: {
       handler(newV) {
         this.get_allTask()
       },
       deep: true
     },
-    type: {
-      handler(newV) {
-        this.get_allTask()
-      }
+    name: {
+      handler(newV) {},
+      immediate: true
     }
   },
   created() {
     this.getTaskStatus()
-    this.get_allTask()
   },
   methods: {
     setStatus(val) {
@@ -232,17 +221,11 @@ export default {
       this.get_allTask()
     },
     async get_allTask() { // 获取全部任务
-      const params = {
-        teamSearchInfo: this.searchForm,
-        status: this.status,
-        pageInfoDO: this.pages
-      }
-      let res = null
-      if (this.type === 'person') {
-        res = await taskSelfList(params)
-      } else if (this.type === 'team') {
-        res = await taskTeamList(params)
+      if (this.idList.length === 0) {
+        this.all_task = []
+        return
       }
+      const res = await taskList({ ids: this.idList, ...this.pages })
       if (res && res.code === 200) {
         this.all_task = res.data
         this.total = res.total
@@ -286,18 +269,13 @@ export default {
         const resTask = await taskUpdate({ taskInfoDO, user })
         if (resTask.code === 200) {
           this.$message({ message: '修改成功', type: 'success', offset: 150 })
-          this.get_allTask()
+          // this.get_allTask()
+          this.$emit('change', '任务')// 通知父组件,让父组件去执行子组件的数据更新
         }
       }
     },
-    async confirmStatus() { // 确认更改状态
-      const user = { name: localStorage.getItem('username'), ename: localStorage.getItem('realname'), id: '' }
-      const taskInfoDO = this.nowChangeTask
-      taskInfoDO.onlineRealTime = this.changeStatusDate
-      const resTask = await taskUpdate({ taskInfoDO, user })
-      if (resTask.code === 200) {
-        this.$message({ message: '修改成功', type: 'success', offset: 150 })
-      }
+    getNew() { // 状态改变成功回调
+      this.$emit('change', '任务')// 通知父组件,让父组件去执行子组件的数据更新
     },
     handleSelectionChange(val) { // 任务列表删选操作
       this.tableList = []
@@ -340,7 +318,6 @@ export default {
       this.visibleSchedule = true
       this.selectTaskList = this.curcentList
     },
-
     async filtrateTest() { // 提测筛选
       const data = this.curcentList.map(item => { return item.id })
       const res = await reportdelivertestCheckStatus(data)
@@ -351,7 +328,6 @@ export default {
         })
       }
     },
-
     async filtrateAllow() { // 准出筛选
       const data = this.curcentList.map(item => { return item.id })
       const res = await reportreleaseCheckStatus(data)
@@ -362,7 +338,6 @@ export default {
         })
       }
     },
-
     async filtrateDaily() { // 建立日报
       const data = this.curcentList.map(item => { return item.id })
       const res = await dailyReportCheckStatus(data)
@@ -373,7 +348,6 @@ export default {
         })
       }
     },
-
     link_task(id) { // 跳转到任务详情页
       if (this.newTabOpen) {
         const newTab = this.$router.resolve({ name: '任务详情', query: { id: id }})
@@ -423,9 +397,13 @@ export default {
 >>>.el-row .el-col {
   margin: 10px 0;
 }
-.new-tab-open {
-  position: absolute;
-  right: 0px;
+.table-top {
+  color: #333333;
+  font-size: 16px;
+  width: 100%;
+  padding: 10px 15px 0 15px;
+  display: flex;
+  justify-content: space-between;
 }
 .task-main {
   display: flex;
@@ -476,15 +454,6 @@ export default {
 .status2 {
   @include setStatus(#7ED321)
 }
-.search-control {
-	padding: 30px 17px 0;
-	color: #333B4A;
-	font-size: 14px;
-	span {
-		margin-right: 35px;
-		cursor: pointer;
-	}
-}
 .expand i {
   border:1px solid #DCDFE6;
 }

+ 189 - 25
src/views/workbench/team/index.vue

@@ -32,9 +32,15 @@
         <el-tabs v-model="activeName" @tab-click="handleClick">
           <el-tab-pane label="团队日程" name="1" />
           <el-tab-pane label="项目" name="2" />
-          <el-tab-pane label="需求" name="3" />
-          <el-tab-pane label="任务" name="4" />
-          <el-tab-pane label="缺陷" name="5" />
+          <el-tab-pane name="3">
+            <span slot="label">需求<div v-show="tabTips.require" class="tips" /> </span>
+          </el-tab-pane>
+          <el-tab-pane name="4">
+            <span slot="label">任务<div v-show="tabTips.task" class="tips" /> </span>
+          </el-tab-pane>
+          <el-tab-pane name="5">
+            <span slot="label"><span style="margin-right: 5px">缺陷</span> <div v-show="tabTips.bug" class="tips-last" /> </span>
+          </el-tab-pane>
         </el-tabs>
       </div>
     </el-header>
@@ -52,7 +58,7 @@
               <el-checkbox v-for="item in memberList" :key="item.idap" :label="item.idap">
                 <div class="check-point">
                   {{ item.name }}
-                  <div class="point" :style="{'background-color': mapMemberColor.get(item.idap).color}" />
+                  <div class="point" :style="{'background-color': mapMemberColor.get(item.idap).bgColor}" />
                 </div>
               </el-checkbox>
             </el-checkbox-group>
@@ -87,17 +93,68 @@
     </el-container>
     <el-container v-if="activeName === '3'">
       <section class="main-section">
-        <needs-list :search-form="searchForm" type="team" />
+        <statistics-section
+          ref="statistics-section"
+          :search-form="searchForm"
+          type="team"
+          title="需求"
+          :request-obj="{
+            requestData:getTeamRequireSummary,
+            requestChart:getTeamRequireDisData
+          }"
+          @showTips="changeTips"
+          @change="changeData"
+        />
+        <needs-list
+          ref="needs-list"
+          :name="listName.requireList"
+          :id-list="idList.requireList"
+          @change="changeStatus"
+        />
       </section>
     </el-container>
     <el-container v-if="activeName === '4'">
       <section class="main-section">
-        <task-list :search-form="searchForm" type="team" />
+        <statistics-section
+          ref="statistics-section"
+          :search-form="searchForm"
+          type="team"
+          title="任务"
+          :request-obj="{
+            requestData:getTeamTaskSummary,
+            requestChart:getTeamTaskDisData
+          }"
+          @showTips="changeTips"
+          @change="changeData"
+        />
+        <task-list
+          ref="task-list"
+          :name="listName.taskList"
+          :id-list="idList.taskList"
+          @change="changeStatus"
+        />
       </section>
     </el-container>
-    <el-container v-show="activeName === '5'">
+    <el-container v-if="activeName === '5'">
       <section class="main-section">
-        <bugTableList ref="bugTableDialog" :name="'团队'" :team-id="searchForm.teamId" :biz-id="searchForm.bizId" />
+        <statistics-section
+          ref="statistics-section"
+          :search-form="searchForm"
+          type="team"
+          title="缺陷"
+          :request-obj="{
+            requestData:getTeamBugSummary,
+            requestChart:getTeamBugDisDataBy
+          }"
+          @showTips="changeTips"
+          @change="changeData"
+        />
+        <bug-tableList
+          ref="bug-tableList"
+          :name="listName.bugList"
+          :id-list="idList.bugList"
+          @change="changeStatus"
+        />
       </section>
     </el-container>
     <!-- 日程 -->
@@ -131,10 +188,21 @@
 <script>
 import moment from 'moment'
 import { settingGetBizList } from '@/api/defectManage'
-import { queryTeamInfoList, queryTeamWorkListByTime, deleteSelfSchedule, queryTeamMember } from '@/api/workSchedule'
+import {
+  queryTeamInfoList,
+  queryTeamWorkListByTime,
+  deleteSelfSchedule,
+  queryTeamMember,
+  getTeamRequireSummary,
+  getTeamRequireDisData,
+  getTeamTaskSummary,
+  getTeamTaskDisData,
+  getTeamBugSummary,
+  getTeamBugDisDataBy
+} from '@/api/workSchedule'
 import { scheduleDelete } from '@/api/projectViewDetails'
 import modifySchedule from '@/views/projectManage/projectList/components/modifySchedule'
-import ganntViews from './components/ganntViews'
+import ganntViews from '@/views/workbench/team/components/ganntViews'
 import MyFullCalendar from '@/views/workbench/person/components/myFullCalendar'
 import calenderDetail from '@/views/workbench/person/components/calenderDetail'
 import calendarDialog from '@/views/workbench/person/components/calendarFormDialog'
@@ -142,6 +210,8 @@ import projectList from '@/views/workbench/team/components/projectList'
 import needsList from '@/views/workbench/team/components/needsList'
 import taskList from '@/views/workbench/team/components/taskList'
 import bugTableList from '@/views/workbench/bugTableList.vue'
+import statisticsSection from '@/views/workbench/components/statisticsSection'
+import websocket from '@/views/workbench/mixins/websocket'
 
 export default {
   components: {
@@ -153,8 +223,10 @@ export default {
     bugTableList,
     projectList,
     needsList,
-    taskList
+    taskList,
+    statisticsSection
   },
+  mixins: [websocket],
   data() {
     return {
       activeName: '1', // 顶部导航栏
@@ -208,9 +280,44 @@ export default {
         'rgba(86,68,93,0.2)', 'rgba(111,33,8,0.2)', 'rgba(192,53,70,0.2)', 'rgba(92,25,107,0.2)', 'rgba(73,10,61,0.2)',
         'rgba(189,21,80,0.2)', 'rgba( 31,78,95,0.2)', 'rgba(241,0,229,0.2)', 'rgba(114,0,218,0.2)', 'rgba(70,14,68,0.2)'
       ],
-      mapMemberColor: new Map()// 人员对应颜色表
+      mapMemberColor: new Map(), // 人员对应颜色表
+      tabTips: { // 顶部tab红点提示
+        require: true,
+        task: true,
+        bug: true
+      },
+      idList: {// 所选项的idList
+        requireList: [],
+        taskList: [],
+        bugList: []
+      },
+      listName: {
+        requireList: '',
+        taskList: '',
+        bugList: ''
+      },
+      getTeamRequireSummary: getTeamRequireSummary, // 需求头部统计数据接口
+      getTeamRequireDisData: getTeamRequireDisData, // 需求按状态统计数据接口
+      getTeamTaskSummary: getTeamTaskSummary, // 任务头部统计数据接口
+      getTeamTaskDisData: getTeamTaskDisData, // 任务按状态统计数据接口
+      getTeamBugSummary: getTeamBugSummary, // 缺陷头部统计数据接口
+      getTeamBugDisDataBy: getTeamBugDisDataBy// 缺陷按状态统计数据接口
+    }
+  },
+  watch: {
+    activeName: {
+      handler(newV) {
+        Number(newV) > 0
+          ? this.$router.push({ path: this.$route.path, query: { page: newV }})
+          : this.$router.push({ path: this.$route.path, query: { page: '1' }})
+      }
     }
   },
+  created() {
+    this.$nextTick(() => {
+      this.activeName = this.$route.query.page ? this.$route.query.page : '1'
+    })
+  },
   mounted() {
     this.settingGetBizList()
     this.queryTeamInfoList()
@@ -218,8 +325,8 @@ export default {
   },
   methods: {
     handleClick() {
-      if (this.activeName === '5') {
-        this.$refs.bugTableDialog.bugGetTableList('团队', { teamId: this.searchForm.teamId, bizId: this.searchForm.bizId })
+      if (this.activeName === '5' && this.$refs.bugTableDialog) {
+        this.$refs.bugTableDialog.bugGetTableList()
       }
     },
     async settingGetBizList() { // 获取业务线
@@ -251,8 +358,8 @@ export default {
       this.mapMemberColor.clear()
       arr.map((item, index) => {
         this.mapMemberColor.set(item.idap, {
-          color: this.colorList[index % 20],
-          bgColor: this.bgColorList[index % 20]
+          color: '#FFFFFF',
+          bgColor: this.colorList[index % 20]
         })
       })
     },
@@ -401,6 +508,38 @@ export default {
           }
         })
       }
+    },
+    changeTips(e, show) { // 红点展示更改
+      if (e === '需求') {
+        this.tabTips.require = show
+      } else if (e === '任务') {
+        this.tabTips.task = show
+      } else if (e === '缺陷') {
+        this.tabTips.bug = show
+      }
+    },
+    changeData(e, idList, name) { // 数据变更
+      if (e === '需求') {
+        this.idList.requireList = idList
+        this.listName.requireList = name
+      } else if (e === '任务') {
+        this.idList.taskList = idList
+        this.listName.taskList = name
+      } else if (e === '缺陷') {
+        this.idList.bugList = idList
+        this.listName.bugList = name
+      }
+    },
+    changeStatus(e) { // 子组件修改状态
+      this.$refs['statistics-section'].initData()
+    },
+    websocketonmessage(e) { // websocket数据接收
+      const { bugHasReminding, requireHasReminding, taskHasReminding } = JSON.parse(e.data)
+      this.tabTips = { // 顶部tab红点提示
+        require: requireHasReminding,
+        task: taskHasReminding,
+        bug: bugHasReminding
+      }
     }
   }
 }
@@ -412,28 +551,53 @@ export default {
 .el-container{
   width: 100%;
 }
-.hideSidebar .main-header {
-  width: calc(100% - 54px);
-}
-.openSidebar .main-header {
-  width: calc(100% - 210px);
-}
+// .hideSidebar .main-header {
+//   width: calc(100% - 54px);
+// }
+// .openSidebar .main-header {
+//   width: calc(100% - 210px);
+// }
+@include hide-open-header;
 .bg-team {
   @include bg-project;
+  padding-top: 60px;
 }
 .main-header {
   @include main-header;
+  margin-bottom: 0;
+  margin-right: 0;
+  border-bottom: 1px solid rgba(155, 155, 155, .2);
+  >>>.el-tabs__nav-wrap::after {
+    height: 0;
+  }
   .top-tabs {
     position: absolute;
     left: 50%;
     transform: translate(-50%, 0);
   }
-}
-.main-header::after {
-  @include main-header-after;
+  .tips {
+    height: 8px;
+    width: 8px;
+    background-color: #E02020;
+    border-radius: 50%;
+    position: absolute;
+    top: 18px;
+    right: 14px;
+  }
+  .tips-last {
+    height: 8px;
+    width: 8px;
+    background-color: #E02020;
+    border-radius: 50%;
+    position: absolute;
+    top: 18px;
+    right: 0;
+  }
 }
 .main-section {
   @include main-section;
+  margin-right: 0;
+  width: calc(100% - 10px);
   .title-name {
     font-size:20px;
     font-family:PingFangSC-Medium;

+ 1 - 0
vue.config.js

@@ -33,6 +33,7 @@ module.exports = {
       warnings: false,
       errors: true
     },
+    disableHostCheck: true, // webpack4.0 开启热更新
     proxy: {
       // change xxx-api/login => mock/login
       // detail: https://cli.vuejs.org/config/#devserver-proxy