Browse Source

任务统计

wangziqian 5 years ago
parent
commit
762906563c

+ 91 - 0
src/api/statisticsApi/taskStatistics.js

@@ -0,0 +1,91 @@
+import request from '@/utils/request'
+import { TeamManagement } from '@/apiConfig/api'
+
+// 任务累计数据
+export function getSummary(data) {
+  return request({
+    url: TeamManagement + '/task/getSummary',
+    method: 'post',
+    data
+  })
+}
+// 任务状态累计流图
+export function getCumulativeFlowDiagram(data) {
+  return request({
+    url: TeamManagement + '/task/getCumulativeFlowDiagram',
+    method: 'post',
+    data
+  })
+}
+// 任务趋势图
+export function getTaskCountTrend(data) {
+  return request({
+    url: TeamManagement + '/task/getTaskCountTrend',
+    method: 'post',
+    data
+  })
+}
+// 所属需求方向分布
+export function getRequirementOrientationDistributeData(data) {
+  return request({
+    url: TeamManagement + '/task/getRequirementOrientationDistributeData',
+    method: 'post',
+    data
+  })
+}
+// 任务周期统计
+export function getTaskPeriodicData(data) {
+  return request({
+    url: TeamManagement + '/task/getTaskPeriodicData',
+    method: 'post',
+    data
+  })
+}
+// 研发交付周期分布图
+export function getTaskRdDeliveryPeriodicData(data) {
+  return request({
+    url: TeamManagement + '/task/getTaskRdDeliveryPeriodicData',
+    method: 'post',
+    data
+  })
+}
+// 任务分布图
+export function getDistributeData(data) {
+  return request({
+    url: TeamManagement + '/task/getDistributeData',
+    method: 'post',
+    data
+  })
+}
+// 状态停留时长分布
+export function getStatusStayData(data) {
+  return request({
+    url: TeamManagement + '/task/getStatusStayData',
+    method: 'post',
+    data
+  })
+}
+// 排期模块分布
+export function getModuleDistributeData(data) {
+  return request({
+    url: TeamManagement + '/task/getModuleDistributeData',
+    method: 'post',
+    data
+  })
+}
+// 排期发生变更的任务
+export function getTaskUnlockData(data) {
+  return request({
+    url: TeamManagement + '/task/getTaskUnlockData',
+    method: 'post',
+    data
+  })
+}
+// 缺陷统计
+export function getBugStatisticData(data) {
+  return request({
+    url: TeamManagement + '/task/getBugStatisticData',
+    method: 'post',
+    data
+  })
+}

+ 0 - 26
src/views/quality/components/moduleDistribute.vue

@@ -1,26 +0,0 @@
-<template>
-  <section />
-</template>
-<script>
-export default {
-  props: {
-    chartData: {
-      type: Object,
-      default: () => null,
-      required: false
-    }
-  },
-  watch: {
-    chartData: {
-      handler(newV) {
-        console.log(newV)
-      }
-    },
-    deep: true,
-    immediate: true
-  }
-}
-</script>
-<style scoped lang="scss">
-
-</style>

+ 1 - 1
src/views/quality/components/statusStayChart.vue

@@ -39,7 +39,7 @@ export default {
     setChart() {
       if (!this.chartData) return
       const newArr = this.chartData.yaxis.filter(item => { return item.name !== '全部' })
-      const colorArr = ['#3AA1FF', '#4ECB73', '#975FE5', '#FBD437', '#13C2C2']
+      const colorArr = ['#409EFF', '#F8CE5C', '#F2904F', '#5EE2BE', '#D873F5', '#7479F5']
       this.echartsOption = {
         color: colorArr,
         tooltip: {

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

@@ -107,6 +107,9 @@
         <el-tab-pane name="2">
           <span slot="label" class="tab-item">累计上线</span>
         </el-tab-pane>
+        <el-tab-pane name="3">
+          <span slot="label" class="tab-item">累计PRD评审通过</span>
+        </el-tab-pane>
       </el-tabs>
       <div class="chart-item">
         <h3>新增趋势图</h3>

+ 583 - 0
src/views/quality/taskStatistics.vue

@@ -0,0 +1,583 @@
+<template>
+  <el-container class="defect-container">
+    <el-header class="defect-main" style="height: auto;">
+      <el-form :model="chartForm" class="demo-form-inline" :inline="true">
+        <el-form-item label="时间:">
+          <div class="date-select">
+            <span :class="[dateType==='week'?'date-active':'']" class="date-item" @click.stop="dateType='week';setDate('week')">本周</span>
+            <span :class="[dateType==='month'?'date-active':'']" class="date-item" @click.stop="dateType='month';setDate('month')">本月</span>
+            <span :class="[dateType==='year'?'date-active':'']" class="date-item" @click.stop="dateType='year';setDate('year')">本年</span>
+          </div>
+          <el-date-picker
+            v-model="stratAndEnd"
+            type="daterange"
+            align="right"
+            unlink-panels
+            range-separator="至"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            size="small"
+            value-format="yyyy.MM.dd"
+            :default-time="['00:00:00','23:59:59']"
+            @change="dateType = ''"
+          />
+        </el-form-item>
+        <el-form-item label="责任团队:">
+          <el-select
+            v-model="chartForm.team"
+            placeholder="请选择"
+            size="small"
+            clearable
+            filterable
+            multiple
+          >
+            <el-option-group
+              v-for="group in teamOptions"
+              :key="group.label"
+              :label="group.label"
+            >
+              <el-option
+                v-for="item in group.options"
+                :key="item.teamId"
+                :label="item.teamName"
+                :value="item.teamId"
+              />
+            </el-option-group>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="技术模块:">
+          <el-cascader
+            v-model="chartForm.moduleIds"
+            size="small"
+            clearable
+            collapse-tags
+            :props="{ multiple: true }"
+            :options="moduleList"
+            placeholder="请选择"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" size="mini" @click="onSubmit">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="data-total">
+        <div v-for="(item,index) in Summary" :key="'Summary'+index" class="data-item" :class="['item'+ (index + 1)]">
+          <article>
+            <div class="item-top">
+              <div class="item-circle">
+                <img v-show="index === 0" src="../../../src/assets/defect_images/add.png">
+                <img v-show="index === 1" src="../../../src/assets/defect_images/repair.png">
+                <img v-show="index === 2" src="../../../src/assets/defect_images/reopen.png">
+                <img v-show="index === 3" src="../../../src/assets/defect_images/reopen.png">
+              </div>
+              <span>{{ item.label }}</span>
+              <el-tooltip v-if="index === 0" class="item" effect="dark" content="统计时间区间内,新增的缺陷的数量" placement="top-start">
+                <i class="el-icon-info" />
+              </el-tooltip>
+              <el-tooltip v-if="index === 1" class="item" effect="dark" content="统计时间区间内,缺陷执行过更新状态为“已完成”的操作并且当前缺陷状态是“已完成”的缺陷数量" placement="top-start">
+                <i class="el-icon-info" />
+              </el-tooltip>
+              <el-tooltip v-if="index === 2" class="item" effect="dark" content="统计时间区间内,缺陷执行过更新状态为“Reopen”的操作并且当前缺陷状态为任意状态的缺陷数量" placement="top-start">
+                <i class="el-icon-info" />
+              </el-tooltip>
+            </div>
+            <div class="item-title">{{ item.total }}</div>
+            <div class="item-line" />
+            <div v-show="Number(item.relativeRatio)>=0" class="item-up">环比:<i class="el-icon-caret-top" />{{ item.relativeRatio }}%</div>
+            <div v-show="Number(item.relativeRatio)<0" class="item-down">环比:<i class="el-icon-caret-bottom" />{{ item.relativeRatio.substring(1,item.relativeRatio.length) }}%</div>
+            <div v-show="item.relativeRatio === '--'" class="item-down">环比:{{ item.relativeRatio }}%</div>
+          </article>
+        </div>
+      </div>
+    </el-header>
+    <el-main class="charts-main">
+      <div class="chart-item">
+        <h3>状态累积流量图</h3>
+        <status-chart :chart-data="cumulativeData" />
+      </div>
+      <el-tabs v-model="activeTab" class="tab-change" @tab-click="onSubmit">
+        <el-tab-pane name="1">
+          <span slot="label" class="tab-item">累计新增</span>
+        </el-tab-pane>
+        <el-tab-pane name="2">
+          <span slot="label" class="tab-item">累计上线</span>
+        </el-tab-pane>
+      </el-tabs>
+      <div class="chart-item">
+        <h3>新增趋势图</h3>
+        <tendency-chart :chart-data="tendencyData" />
+      </div>
+      <div class="chart-item">
+        <h3>周期统计<span>(根据排期计算)</span></h3>
+        <div class="chart-item-tip">
+          <i class="el-icon-warning-outline" />
+          <span>仅统计状态已变更“已排期”且排期不为空的任务</span>
+        </div>
+        <cycle-statistic :chart-data="cycleData" />
+      </div>
+      <div class="chart-item">
+        <h3>研发交付周期分布图<span>(根据排期计算)</span></h3>
+        <div class="chart-item-tip">
+          <i class="el-icon-warning-outline" />
+          <span>仅统计状态已变更“已排期”且排期不为空的任务;横坐标表示任务交付日期,纵坐标代表研发交付周期(研发、联调、上线类型排期的总周期)</span>
+        </div>
+        <development-cycle :chart-data="developmentCycleData" />
+      </div>
+      <div class="chart-item">
+        <h3>任务分布图</h3>
+        <distribution-chart
+          :chart-data="distributeData"
+          :status-list="distributeStatusList"
+          :status.sync="distributeStatus"
+          :active-tab="activeTab"
+          @change="getDistributeData()"
+        />
+      </div>
+      <div class="chart-item">
+        <h3>状态停留分布图</h3>
+        <status-stay-chart :chart-data="statusStayData" />
+      </div>
+      <div class="chart-item">
+        <h3>模块分布图</h3>
+        <belong-chart :chart-data="moduleDistributeData" />
+      </div>
+      <div class="chart-item">
+        <h3>排期发生变更的任务({{ changeTotal }})</h3>
+        <change-require-chart :chart-data="changeTaskData" />
+      </div>
+      <div class="chart-item">
+        <h3>缺陷统计</h3>
+        <bug-list :chart-data="bugListData" />
+      </div>
+    </el-main>
+  </el-container>
+</template>
+<script>
+import moment from 'moment'
+moment.locale('zh-cn')
+import { settingQueryBizModuleList } from '@/api/defectManage'
+import { teamQueryTeamInfoList } from '@/api/configure'
+import {
+  getSummary,
+  getCumulativeFlowDiagram,
+  getTaskCountTrend,
+  getTaskPeriodicData,
+  getTaskRdDeliveryPeriodicData,
+  getDistributeData,
+  getStatusStayData,
+  getModuleDistributeData,
+  getTaskUnlockData,
+  getBugStatisticData
+} from '@/api/statisticsApi/requireStatistics'
+import statusChart from './components/statusChart'
+import tendencyChart from './components/tendencyChart'
+import cycleStatistic from './components/cycleStatistic'
+import developmentCycle from './components/developmentCycle'
+import distributionChart from './components/distributionChart'
+import statusStayChart from './components/statusStayChart'
+import belongChart from './components/belongChart'
+import changeRequireChart from './components/changeRequireChart'
+import bugList from './components/bugList'
+export default {
+  components: {
+    statusChart,
+    tendencyChart,
+    cycleStatistic,
+    developmentCycle,
+    distributionChart,
+    statusStayChart,
+    belongChart,
+    changeRequireChart,
+    bugList
+  },
+  data() {
+    return {
+      bugCountTimeType: 1, // 获取趋缺陷势图数据接口入参:1本周 2本月 3本年
+      timeTypeList: [ // 日期选择
+        { code: 1, label: '周' },
+        { code: 2, label: '月' },
+        { code: 3, label: '年' }
+      ],
+      teamOptions: [], // 团队列表
+      chartForm: {}, // 筛选表单
+      stratAndEnd: [], // 开始结束日期
+      dateType: 'week', // 时间选择类型
+      moduleList: [], // 任务方向列表
+      Summary: [], // 顶部数据
+      activeTab: '1', // tab标签
+      cumulativeData: null, // 任务状态累计流数据
+      tendencyData: null, // 趋势图数据
+      cycleData: [], // 周期统计数据
+      developmentCycleData: [], // 研发交付周期分布数据
+      distributeStatus: 1, // 任务分布图任务状态
+      distributeStatusList: [
+        { code: 1, label: '任务状态' },
+        { code: 2, label: '任务等级' },
+        { code: 3, label: '开发负责人' },
+        { code: 4, label: '测试负责人' },
+        { code: 5, label: '跟版客户端' },
+        { code: 6, label: '直接归属' }
+      ], // 任务分布图任务状态列表
+      moduleDistributeData: null, // 任务模块分布
+      distributeData: null, // 任务分布图数据
+      statusStayData: null, // 状态停留图数据
+      moduleDistribute: null, // 模块分布图数据
+      changeTaskData: null, // 排期变更任务数据
+      changeTotal: 0, // 变更总数
+      bugListData: {} // 缺陷统计数据
+    }
+  },
+  computed: {
+    globalParams: { // 通用接口参数
+      get() {
+        const moduleIds = this.chartForm.moduleIds
+        const team = this.chartForm.team
+        const params = {
+          startTime: this.stratAndEnd[0] || null,
+          endTime: this.stratAndEnd[1] || null,
+          bizId: Number(localStorage.getItem('bizId')),
+          teamIds: team && team.length > 0 ? team : null,
+          moduleIds: moduleIds && moduleIds.length > 0 ? moduleIds : null
+        }
+        return params
+      }
+    },
+    timeType: {
+      get() {
+        let timeType = 0
+        switch (this.dateType) {
+          case 'week':
+            timeType = 1
+            break
+          case 'month':
+            timeType = 2
+            break
+          case 'year':
+            timeType = 3
+            break
+          default:
+            timeType = 0
+            break
+        }
+        return timeType
+      }
+    }
+  },
+  created() {
+    this.$store.state.data.status = true
+    this.setDate(this.dateType)
+    this.getTeamList()
+    this.getMoudlesList()
+  },
+  methods: {
+    onSubmit() {
+      if (this.activeTab === '2' && this.distributeStatus === 1) {
+        this.distributeStatus = 2
+      }
+      this.getSummary()
+      this.getCumulativeFlowDiagram()
+      this.getTaskCountTrend()
+      this.getCycleData()
+      this.getDevelopmentCycle()
+      this.getModuleDistributeData()
+      this.getDistributeData()
+      this.getStatusStayData()
+      this.getTaskUnlockData()
+      this.getBugStatisticData()
+    },
+    setDate(type) { // 日期筛选
+      let startDate = null
+      let endDate = null
+      switch (type) {
+        case 'week':
+          startDate = moment().startOf('week').format('YYYY.MM.DD')
+          endDate = moment().endOf('week').format('YYYY.MM.DD')
+          this.bugCountTimeType = this.timeTypeList[0].code
+          break
+        case 'month':
+          startDate = moment().startOf('month').format('YYYY.MM.DD')
+          endDate = moment().endOf('month').format('YYYY.MM.DD')
+          this.bugCountTimeType = this.timeTypeList[1].code
+          break
+        case 'year':
+          startDate = moment().startOf('year').format('YYYY.MM.DD')
+          endDate = moment().endOf('year').format('YYYY.MM.DD')
+          this.bugCountTimeType = this.timeTypeList[2].code
+          break
+      }
+      this.stratAndEnd = [startDate, endDate]
+      this.onSubmit()
+    },
+    async getMoudlesList() { // 技术模块
+      const res = await settingQueryBizModuleList(Number(localStorage.getItem('bizId')))
+      if (res.code === 200) {
+        this.moduleList = this.handlerModules(res.data)
+      }
+    },
+    handlerModules(arr) {
+      return arr.map(item => ({
+        ...item,
+        value: item.id,
+        label: item.moduleName,
+        children: item.childModules.length ? this.handlerModules(item.childModules) : null
+      }))
+    },
+    async getTeamList() { // 获取我的团队和所有团队
+      const myRes = await teamQueryTeamInfoList({ type: 0 })// 我的团队
+      const allRes = await teamQueryTeamInfoList({ type: 1 })// 所有团队
+      this.teamOptions = [
+        { label: '我的团队', options: [...myRes.data.list] },
+        { label: '全部团队', options: [...allRes.data.list] }
+      ]
+    },
+    async getSummary() { // 获取顶部数据
+      const res = await getSummary(this.globalParams)
+      if (res.code === 200) this.Summary = res.data || []
+    },
+    async getTaskCountTrend() { // 趋势图日期变动
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab),
+        timeType: this.timeType
+      }
+      const res = await getTaskCountTrend(params)
+      if (res.code === 200) this.tendencyData = res.data
+    },
+    async getCumulativeFlowDiagram() { // 任务状态累计流图
+      const params = {
+        ...this.globalParams,
+        timeType: this.timeType
+      }
+      const res = await getCumulativeFlowDiagram(params)
+      if (res.code === 200) this.cumulativeData = res.data
+    },
+    async getCycleData() { // 周期统计数据
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab)
+      }
+      const res = await getTaskPeriodicData(params)
+      if (res.code === 200) this.cycleData = res.data
+    },
+    async getDevelopmentCycle() { // 获取研发交付周期分布数据
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab)
+      }
+      const res = await getTaskRdDeliveryPeriodicData(params)
+      if (res.code === 200) this.developmentCycleData = res.data
+    },
+    async getDistributeData() { // 获取任务分布图
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab),
+        distributeType: this.distributeStatus
+      }
+      const res = await getDistributeData(params)
+      if (res.code === 200) this.distributeData = res.data
+    },
+    async getStatusStayData() { // 状态停留时长分布
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab)
+      }
+      const res = await getStatusStayData(params)
+      if (res.code === 200) this.statusStayData = res.data
+    },
+    async getModuleDistributeData() { // 获取模块分布
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab)
+      }
+      const res = await getModuleDistributeData(params)
+      if (res.code === 200) this.moduleDistributeData = this.handlerData(res.data)
+    },
+    handlerData(arr) { // 处理任务模块分布图数据
+      let num = 0
+      const bfs = (arr) => {
+        for (const item of arr) {
+          num = num + item.value
+          if (item.children && item.children.length > 0) {
+            bfs(item.children)
+          } else {
+            delete item.children
+          }
+        }
+      }
+      bfs(arr)
+      return { children: arr, name: '总量', value: num }
+    },
+    async getTaskUnlockData() { // 排期发生变更的任务
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab),
+        distributeType: this.distributeStatus
+      }
+      const res = await getTaskUnlockData(params)
+      if (res.code === 200) {
+        this.changeTaskData = res.data || []
+        this.changeTaskData.map(item => {
+          this.changeTotal += item.count
+        })
+      }
+    },
+    async getBugStatisticData() { // 缺陷统计
+      const params = {
+        ...this.globalParams,
+        type: Number(this.activeTab)
+      }
+      const res = await getBugStatisticData(params)
+      if (res.code === 200) this.bugListData = res.data
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.defect-container {
+  width: 100%;
+  height:100%;
+  background:#F2F3F6;
+  display: inline-block;
+  color: #666666;
+  h3 {
+    font-size: 18px;
+    margin: 0;
+    padding: 0;
+    span {
+      color: #666666;
+    }
+  }
+  .defect-main {
+    padding: 20px 20px 0 20px;
+    height:100%;
+    width: calc(100%-60px);
+    background:#ffffff;
+    margin: 10px;
+    border-radius: 4px;
+    .date-select {
+      line-height: 20px;
+      display: inline-block;
+      .date-item {
+        display: inline-block;
+        width: 40px;
+        text-align: center;
+        cursor: pointer;
+        border-radius: 2px;
+      }
+      .date-active {
+        color: #FFFFFF;
+        background-color: #409EFF;
+      }
+    }
+  }
+}
+.data-total {
+  display: grid;
+  width: 100%;
+  margin: auto;
+  grid-template-columns: repeat(4,22%);
+  grid-gap: 4%;
+  padding-bottom: 40px;
+  ul,li{
+    padding:0;margin:0;list-style:none
+  }
+  .data-item {
+    position: relative;
+    width: 100%;
+    border-radius: 8px;
+    article {
+      display: flex;
+      flex-direction: column;
+      align-items: flex-start;
+      color: #FFFFFF;
+    }
+    .item-top {
+      width: 100%;
+      display: flex;
+      align-items: center;
+      padding: 10px 20px;
+      font-size: 13px;
+      .item-circle {
+        height: 36px;
+        width: 36px;
+        border-radius: 50%;
+        background:rgba(255,255,255,0.24);
+        margin-right: 10px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        img {
+          height: 20px;
+          width: 20px;
+        }
+      }
+      span {
+        margin-right: 5px;
+      }
+    }
+    .item-line {
+      width: 100%;
+      border-bottom:1px solid rgba(255,255,255,0.2);
+    }
+    .item-title {
+      font-size: 40px;
+      padding: 5% 20px;
+    }
+    .item-up,.item-down {
+      font-size: 13px;
+      padding: 10px 20px 0 20px;
+      margin-bottom: 20px;
+    }
+    .item-up i {
+      color:#F32850
+    }
+    .item-down i {
+      color:#9FFF39
+    }
+  }
+  .item1 {
+    background-image: url('../../../src/assets/defect_images/数据1.png');
+    background-size: 100% 100%;
+    background-position: 50% 50%;
+    box-shadow:0px 50px 24px -25px rgba(64,137,255,0.30);
+  }
+  .item2 {
+    background-image: url('../../../src/assets/defect_images/数据2.png');
+    background-size: 100% 100%;
+    background-position: 50% 50%;
+    box-shadow:0px 50px 24px -25px rgba(78,188,250,0.40);
+  }
+  .item3 {
+    background-image: url('../../../src/assets/defect_images/数据3.png');
+    background-size: 100% 100%;
+    background-position: 50% 50%;
+    box-shadow:0px 50px 24px -25px rgba(255,136,134,0.30);
+  }
+  .item4 {
+    background-image: url('../../../src/assets/defect_images/数据4.png');
+    background-size: 100% 100%;
+    background-position: 50% 50%;
+    box-shadow:0px 50px 24px -25px rgba(83,97,255,0.30);
+  }
+}
+.charts-main {
+  padding-bottom: 20px;
+  width:calc(100%-60px);
+  background:#ffffff;
+  margin: 10px;
+  border-radius: 4px;
+  .chart-item {
+    margin-bottom: 30px;
+  }
+  .tab-change {
+    overflow: hidden;
+    margin-top: 40px;
+  }
+  .chart-item-tip {
+    margin-top: 12px;
+    margin-bottom: 10px;
+    font-size: 12px;
+    color: #E6A23C;
+  }
+}
+</style>