Browse Source

coding...

刘洋 1 year ago
parent
commit
9a27255fe6

+ 2 - 1
auto-imports.d.ts

@@ -5,5 +5,6 @@
 // Generated by unplugin-auto-import
 export {}
 declare global {
-
+  const DialogPlugin: typeof import('tdesign-vue-next')['DialogPlugin']
+  const MessagePlugin: typeof import('tdesign-vue-next')['MessagePlugin']
 }

+ 18 - 0
src/api/report.js

@@ -114,3 +114,21 @@ export const warningTop = (params) =>
     url: '/api/sop/analyse/processing',
     params,
   });
+//考勤top
+export const attendanceTop = (params) =>
+  request({
+    url: '/api/sop/analyse/attendance',
+    params,
+  });
+//供应商预警均值走势接口
+export const supWarningTrend = (params) =>
+  request({
+    url: '/api/sop/analyse/trend',
+    params,
+  });
+//总体概况接口
+export const sopWarningAnalysis = (params) =>
+  request({
+    url: '/api/sop/analyse/overview',
+    params,
+  });

+ 1 - 1
src/components/common/china-point-chart/index.vue

@@ -41,7 +41,7 @@ const convertData = (data) => {
   for (var i = 0; i < data.length; i++) {
     var geoCoord =
       geoCoordMap[
-        data[i].name.endsWith('省') ? data[i].name.slice(0, -1) : data[i].name
+        data[i].name?.endsWith('省') ? data[i].name.slice(0, -1) : data[i].name
       ];
     if (geoCoord) {
       res.push({

+ 21 - 26
src/components/common/table-loop/index.vue

@@ -11,7 +11,7 @@
         class="td"
         v-for="(item, index) in columns"
         :key="index"
-        :style="item.styles || {}"
+        :style="item.style || {}"
       >
         {{ item.label }}
       </div>
@@ -45,11 +45,13 @@
           class="td"
           v-for="(v, i) in columns"
           :key="i"
-          :style="item.styles || {}"
+          :style="item.style || {}"
         >
-          <span v-if="v.prop !== 'rate'">{{ item[v.prop] }}</span>
-          <div class="process-box" v-else>
-            <div class="process" :style="{ width: item.rate }"></div>
+          <div class="cell">
+            <span v-if="v.prop !== 'rate'">{{ item[v.prop] }}</span>
+            <div class="process-box" v-else>
+              <div class="process" :style="{ width: item.rate }"></div>
+            </div>
           </div>
         </div>
       </swiper-slide>
@@ -84,42 +86,35 @@ onMounted(() => {
 .table-loop {
   height: 100%;
   padding-top: 10px;
+  overflow: auto;
   .thead {
     height: 36px;
-    background-color: #f7f7f7;
     font-size: 12px;
     .td {
       text-align: center;
       line-height: 36px;
       color: #8c8c8c !important;
+      background-color: #f7f7f7;
     }
   }
   .slide {
-    .col1,
-    .col2,
-    .col3 {
+    .td {
       color: #262626;
+      .cell {
+        height: 100%;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+      }
     }
   }
-  .col1 {
-    width: 34px;
-  }
-  .col2 {
-    width: calc((100% - 84px) / 2);
-  }
-
-  .col3,
-  .col5 {
-    width: calc((100% - 84px) / 4);
-  }
-  .col4 {
-    width: 50px;
+  .thead,
+  .slide {
+    white-space: nowrap;
   }
   .td {
-    float: left;
-    display: flex;
-    justify-content: center;
-    align-items: center;
+    display: inline-block;
+
     height: 100%;
     &.index {
       width: 34px;

+ 4 - 1
src/utils/tool.js

@@ -157,7 +157,10 @@ export const screen = (element) => {
     element.webkitRequestFullscreen();
   }
 };
-
+/* 百分比值 */
+export const division = (a, b, fixed = 2) => {
+  return ((a * 100) / b).toFixed(fixed);
+};
 /* 复制对象 */
 export const objCopy = (obj) => {
   if (obj === undefined) {

+ 4 - 4
src/views/report/dispatch-analysis/index.vue

@@ -209,17 +209,17 @@ const tableColumns = [
   {
     prop: 'name',
     label: '城市',
-    styles: { width: 'calc((100% - 84px) / 2)', color: '#262626' },
+    style: { width: 'calc((100% - 84px) / 2)' },
   },
   {
     prop: 'count',
     label: '总数',
-    styles: { width: 'calc((100% - 84px) / 4)', color: '#262626' },
+    style: { width: 'calc((100% - 84px) / 4)' },
   },
   {
-    prop: 'name',
+    prop: 'rate',
     label: '百分比',
-    styles: { width: '50px', color: '#262626' },
+    style: { width: '50px' },
   },
   { prop: 'compare', label: '增减' },
 ];

+ 429 - 16
src/views/report/sop-analysis/index.vue

@@ -17,7 +17,11 @@
         <div class="col1">
           <div class="card">
             <div class="title">
-              <t-select v-model="sort1" style="width: calc(100% - 50px)">
+              <t-select
+                v-model="sort1"
+                style="width: calc(100% - 50px)"
+                @change="changeSort('CRM', 1)"
+              >
                 <t-option
                   value="PENDING"
                   label="项目预警待处理TOP10"
@@ -33,12 +37,19 @@
               </t-select>
             </div>
             <div class="chart-wrap">
-              <table-loop :data="tableDataHandle(result1)"></table-loop>
+              <table-loop
+                :data="tableDataHandle(result1)"
+                :columns="tableColumns1"
+              ></table-loop>
             </div>
           </div>
           <div class="card">
             <div class="title">
-              <t-select v-model="sort2" style="width: calc(100% - 50px)">
+              <t-select
+                v-model="sort2"
+                style="width: calc(100% - 50px)"
+                @change="changeSort('REGION', 2)"
+              >
                 <t-option
                   value="PENDING"
                   label="大区预警待处理TOP10"
@@ -53,10 +64,20 @@
                 ></t-option>
               </t-select>
             </div>
+            <div class="chart-wrap">
+              <table-loop
+                :data="tableDataHandle(result2)"
+                :columns="tableColumns2"
+              ></table-loop>
+            </div>
           </div>
           <div class="card">
             <div class="title">
-              <t-select v-model="sort3" style="width: calc(100% - 50px)">
+              <t-select
+                v-model="sort3"
+                style="width: calc(100% - 50px)"
+                @change="changeSort('SUPPLIER', 3)"
+              >
                 <t-option
                   value="PENDING"
                   label="供应商预警待处理TOP10"
@@ -71,16 +92,144 @@
                 ></t-option>
               </t-select>
             </div>
+            <div class="chart-wrap">
+              <table-loop
+                :data="tableDataHandle(result3)"
+                :columns="tableColumns3"
+              ></table-loop>
+            </div>
           </div>
         </div>
         <div class="col2">
-          <div class="card"></div>
+          <div class="card">
+            <div class="tab-box">
+              <div
+                class="tab"
+                @click="centerGroup = 'REGION'"
+                :class="{ active: centerGroup === 'REGION' }"
+                >按大区</div
+              >
+              <div
+                class="tab"
+                @click="centerGroup = 'SUPPLIER'"
+                :class="{ active: centerGroup === 'SUPPLIER' }"
+                >按人力供应商</div
+              >
+            </div>
+            <div class="clear-both"></div>
+            <div class="list-wrap">
+              <div
+                class="list-item"
+                v-for="(item, index) in result7"
+                :key="index"
+              >
+                <div class="item-head"> {{ item.region_name }} </div>
+                <div class="item-body">
+                  <div class="row1">
+                    <div class="grid-item">
+                      <div class="label">派单数</div>
+                      <p>{{ item.crmNum }}</p>
+                    </div>
+                    <div class="grid-item">
+                      <div class="label">完成进度(%)</div>
+                      <p>{{ division(item.finishCrmNum, item.crmNum) }}</p>
+                    </div>
+                    <div class="grid-item">
+                      <div class="label">平均处理时限(小时)</div>
+                      <p class="red">{{ (item.avgMinutes / 60).toFixed(2) }}</p>
+                    </div>
+                  </div>
+                  <div class="row2">
+                    <div class="label">预警处理进度</div>
+                    <div class="process-box">
+                      <div class="grid-item">
+                        <span>总体</span>
+                        <div class="bar-box">
+                          <div
+                            class="bar"
+                            :style="{
+                              width:
+                                division(
+                                  item.finishViolationNum + item.finishDelayNum,
+                                  item.violationNum + item.delayNum
+                                ) + '%',
+                            }"
+                          ></div>
+                        </div>
+                        <span>{{ item.violationNum + item.delayNum }}</span>
+                      </div>
+                      <div class="grid-item">
+                        <span>违规</span>
+                        <div class="bar-box">
+                          <div
+                            class="bar"
+                            :style="{
+                              width:
+                                division(
+                                  item.finishViolationNum,
+                                  item.violationNum
+                                ) + '%',
+                            }"
+                          ></div>
+                        </div>
+                        <span>{{ item.violationNum }}</span>
+                      </div>
+                      <div class="grid-item">
+                        <span>延期</span>
+                        <div class="bar-box">
+                          <div
+                            class="bar"
+                            :style="{
+                              width:
+                                division(item.finishDelayNum, item.delayNum) +
+                                '%',
+                            }"
+                          ></div>
+                        </div>
+                        <span>{{ item.delayNum }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
           <div class="card"></div>
         </div>
         <div class="col3">
-          <div class="card"></div>
-          <div class="card"></div>
-          <div class="card"></div>
+          <div class="card">
+            <div class="title">
+              <span>项目考勤异常TOP10</span>
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                :data="tableDataHandle(result4)"
+                :columns="tableColumns4"
+              ></table-loop>
+            </div>
+          </div>
+          <div class="card">
+            <div class="title">
+              <span>项目考勤异常TOP10</span>
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                :data="tableDataHandle(result5)"
+                :columns="tableColumns5"
+              ></table-loop>
+            </div>
+          </div>
+          <div class="card">
+            <div class="title">
+              <span>供应商考勤异常TOP5</span>
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                :data="tableDataHandle(result6)"
+                :columns="tableColumns6"
+              ></table-loop>
+            </div>
+          </div>
         </div>
       </div>
     </div>
@@ -88,11 +237,17 @@
 </template>
 
 <script setup name="DispatchAnalysis">
-import { ref, computed, watch } from 'vue';
-import SelectServiceUnit from '@/components/common/select-service-unit/index.vue';
+import { ref, computed, watch, onMounted } from 'vue';
 import TableLoop from '@/components/common/table-loop/index.vue';
 import { useRequest } from 'vue-request';
-import { sopServiceList, warningTop } from '@/api/report';
+import { division } from '@/utils/tool';
+import {
+  sopServiceList,
+  warningTop,
+  attendanceTop,
+  supWarningTrend,
+  sopWarningAnalysis,
+} from '@/api/report';
 const curTimeRange = ref([]);
 const serviceOptions = ref([]);
 
@@ -103,6 +258,7 @@ const timeParams = computed(() => {
   };
 });
 const serviceId = ref('');
+const centerGroup = ref('REGION');
 const timeChange = (time) => {
   sopServiceList(timeParams.value).then((res) => {
     serviceOptions.value = res || [];
@@ -113,10 +269,140 @@ const sort1 = ref('PENDING');
 const sort2 = ref('PENDING');
 const sort3 = ref('PENDING');
 
-const { data: result1, loading: loading1, run: run1 } = useRequest(warningTop); //服务单元概览
+const { data: result1, loading: loading1, run: run1 } = useRequest(warningTop);
+const { data: result2, loading: loading2, run: run2 } = useRequest(warningTop);
+const { data: result3, loading: loading3, run: run3 } = useRequest(warningTop);
+const {
+  data: result4,
+  loading: loading4,
+  run: run4,
+} = useRequest(attendanceTop);
+const {
+  data: result5,
+  loading: loading5,
+  run: run5,
+} = useRequest(attendanceTop);
+const {
+  data: result6,
+  loading: loading6,
+  run: run6,
+} = useRequest(attendanceTop);
+const {
+  data: result7,
+  loading: loading7,
+  run: run7,
+} = useRequest(sopWarningAnalysis);
+const {
+  data: result8,
+  loading: loading8,
+  run: run8,
+} = useRequest(supWarningTrend);
 const tableDataHandle = (data) => {
   return data || [];
 };
+const tableColumns1 = [
+  {
+    prop: 'a',
+    label: '客户名称',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'b',
+    label: '大区',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'c',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'd', label: '预警数', style: { width: '60px' } },
+];
+const tableColumns2 = [
+  {
+    prop: 'a',
+    label: '大区',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'b',
+    label: '大区经理',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'c',
+    label: '预警数',
+    style: { width: '70px' },
+  },
+  { prop: 'd', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns3 = [
+  {
+    prop: 'a',
+    label: '供应商',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'b',
+    label: '大区',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'c',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'd', label: '预警数', style: { width: '70px' } },
+  { prop: 'e', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns4 = [
+  {
+    prop: 'a',
+    label: '客户名称',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'b',
+    label: '考勤对象',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'c',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'd', label: '异常数', style: { width: '60px' } },
+];
+const tableColumns5 = [
+  {
+    prop: 'a',
+    label: '大区',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'b',
+    label: '大区经理',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'c',
+    label: '异常数',
+    style: { width: '70px' },
+  },
+  { prop: 'd', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns6 = [
+  {
+    prop: 'a',
+    label: '供应商',
+    style: { width: '80px' },
+  },
+  { prop: 'd', label: '异常数', style: { width: '70px' } },
+  { prop: 'e', label: '均值', style: { width: '60px' } },
+];
 watch(serviceId, (serviceId) => {
   sort1.value = sort2.value = sort3.value = 'PENDING';
   run1({
@@ -124,17 +410,53 @@ watch(serviceId, (serviceId) => {
     serviceId,
     sort: sort1.value,
   });
-  run1({
+  run2({
     group: 'REGION',
     serviceId,
-    sort: sort1.value,
+    sort: sort2.value,
   });
-  run1({
+  run3({
     group: 'SUPPLIER',
     serviceId,
-    sort: sort1.value,
+    sort: sort3.value,
   });
+  run4({
+    group: 'CRM',
+    serviceId,
+    ...timeParams.value,
+  });
+  run5({
+    group: 'REGION',
+    serviceId,
+    ...timeParams.value,
+  });
+  run6({
+    group: 'SUPPLIER',
+    serviceId,
+    ...timeParams.value,
+  });
+  run7({
+    group: centerGroup.value,
+    serviceId,
+  });
+  run8({ serviceId });
 });
+watch(centerGroup, () => {
+  run7({
+    group: centerGroup.value,
+    serviceId,
+  });
+});
+const changeSort = (group, index) => {
+  const sort =
+    index == 1 ? sort1.value : index == 2 ? sort2.value : sort3.value;
+  const run = index == 1 ? run1 : index == 2 ? run2 : run3;
+  run({
+    group,
+    serviceId,
+    sort,
+  });
+};
 </script>
 
 <style lang="less" scoped>
@@ -180,6 +502,97 @@ watch(serviceId, (serviceId) => {
         .card {
           &:first-child {
             height: calc((100% - 30px) * 2 / 3 + 15px);
+            .list-wrap {
+              height: calc(100% - 42px);
+              overflow: auto;
+              margin-top: 10px;
+              .list-item {
+                .item-head {
+                  height: 32px;
+                  background: #f7f7f7;
+                  padding: 0 10px;
+                  line-height: 32px;
+                  color: #262626;
+                }
+                .item-body {
+                  padding: 4px 8px;
+                  .label {
+                    color: #8c8c8c;
+                    font-size: 12px;
+                  }
+                  .row2 {
+                    margin-top: 5px;
+                    .process-box {
+                      display: flex;
+                      .grid-item {
+                        width: 33.33%;
+                        display: flex;
+                        align-items: center;
+                        span {
+                          font-size: 12px;
+                          color: #262626;
+                          &:last-child {
+                            padding-right: 20px;
+                          }
+                        }
+                        .bar-box {
+                          flex: 1;
+                          max-width: 100px;
+                          margin: 0 10px;
+                          height: 6px;
+                          border-radius: 3px;
+                          background: #d9d9d9;
+                          .bar {
+                            background: #4080ff;
+                            height: 100%;
+                          }
+                        }
+                      }
+                    }
+                  }
+                  .row1 {
+                    display: flex;
+                    .grid-item {
+                      width: 33.33%;
+                      p {
+                        font-size: 16px;
+                        font-weight: bold;
+                        color: #165dff;
+                        &.red {
+                          color: #f53f3f;
+                        }
+                        &.green {
+                          color: #00b42a;
+                        }
+                      }
+                    }
+                  }
+                }
+              }
+            }
+            .tab-box {
+              height: 32px;
+              padding: 5px 8px;
+              background: #f0f0f0;
+              border-radius: 3px;
+              float: left;
+              display: flex;
+              align-items: center;
+              .tab {
+                height: 22px;
+                padding: 0 6px;
+                line-height: 22px;
+                color: #8c8c8c;
+                cursor: pointer;
+                &.active {
+                  background-color: #fff;
+                  border-radius: 2px;
+                }
+                &:last-child {
+                  margin-left: 5px;
+                }
+              }
+            }
           }
           &:last-child {
             height: calc((100% - 30px) / 3);