wangliang 1 tahun lalu
induk
melakukan
85ab82a368
1 mengubah file dengan 710 tambahan dan 710 penghapusan
  1. 710 710
      src/views/report/sop-analysis/index.vue

+ 710 - 710
src/views/report/sop-analysis/index.vue

@@ -1,710 +1,710 @@
-<template>
-  <div class="sop-analysis">
-    <report-header
-      title="SOP预警监控"
-      v-model:dateRange="curTimeRange"
-      @timeChange="timeChange"
-    >
-      <t-select
-        style="width: 200px"
-        :options="serviceOptions"
-        v-model="serviceId"
-        :keys="{ label: 'name', value: 'id' }"
-        filterable
-      ></t-select>
-    </report-header>
-    <div class="page-main">
-      <div class="scroll-content">
-        <div class="col1">
-          <div class="card">
-            <div class="title">
-              <t-select
-                v-model="sort1"
-                style="width: calc(100% - 50px)"
-                @change="changeSort('CRM', 1)"
-              >
-                <t-option
-                  value="PENDING"
-                  label="项目预警待处理TOP10"
-                ></t-option>
-                <t-option
-                  value="SLOWEST"
-                  label="项目预警处理最慢TOP10"
-                ></t-option>
-                <t-option
-                  value="FASTEST"
-                  label="项目预警处理最快TOP10"
-                ></t-option>
-              </t-select>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart1?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart1"
-                :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)"
-                @change="changeSort('REGION', 2)"
-              >
-                <t-option
-                  value="PENDING"
-                  label="大区预警待处理TOP10"
-                ></t-option>
-                <t-option
-                  value="SLOWEST"
-                  label="大区预警处理最慢TOP10"
-                ></t-option>
-                <t-option
-                  value="FASTEST"
-                  label="大区预警处理最快TOP10"
-                ></t-option>
-              </t-select>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart2?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart2"
-                :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)"
-                @change="changeSort('SUPPLIER', 3)"
-              >
-                <t-option
-                  value="PENDING"
-                  label="供应商预警待处理TOP10"
-                ></t-option>
-                <t-option
-                  value="SLOWEST"
-                  label="供应商预警处理最慢TOP10"
-                ></t-option>
-                <t-option
-                  value="FASTEST"
-                  label="供应商预警处理最快TOP10"
-                ></t-option>
-              </t-select>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart3?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart3"
-                :data="tableDataHandle(result3)"
-                :columns="tableColumns3"
-              ></table-loop>
-            </div>
-          </div>
-        </div>
-        <div class="col2">
-          <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" v-if="result7?.length">
-              <div
-                class="list-item"
-                v-for="(item, index) in result7"
-                :key="index"
-              >
-                <div class="item-head">
-                  {{
-                    centerGroup === 'SUPPLIER'
-                      ? item.supplierName
-                      : item.regionName
-                  }}
-                </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 class="none-data" v-else>暂无数据</div>
-          </div>
-          <div class="card">
-            <div class="title">
-              <span class="label">供应商预警均值走势</span>
-            </div>
-            <div class="chart-wrap">
-              <my-chart :options="options8"></my-chart>
-            </div>
-          </div>
-        </div>
-        <div class="col3">
-          <div class="card">
-            <div class="title">
-              <span class="label">项目考勤异常TOP10</span>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart4?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart4"
-                :data="tableDataHandle(result4)"
-                :columns="tableColumns4"
-              ></table-loop>
-            </div>
-          </div>
-          <div class="card">
-            <div class="title">
-              <span class="label">项目考勤异常TOP10</span>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart5?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart5"
-                :data="tableDataHandle(result5)"
-                :columns="tableColumns5"
-              ></table-loop>
-            </div>
-          </div>
-          <div class="card">
-            <div class="title">
-              <span class="label">供应商考勤异常TOP5</span>
-              <FullscreenIcon
-                class="cursor-pointer"
-                @click="chart6?.maximize"
-                color="#595959"
-              />
-            </div>
-            <div class="chart-wrap">
-              <table-loop
-                ref="chart6"
-                :data="tableDataHandle(result6)"
-                :columns="tableColumns6"
-              ></table-loop>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup name="SopAnalysis">
-import { ref, computed, watch, onMounted } from 'vue';
-import TableLoop from '@/components/common/table-loop/index.vue';
-import { useRequest } from 'vue-request';
-import { division, dateFormat } from '@/utils/tool';
-import {
-  sopServiceList,
-  warningTop,
-  attendanceTop,
-  supWarningTrend,
-  sopWarningAnalysis,
-} from '@/api/report';
-import { createLineOption } from '@/utils/chart';
-import { FullscreenIcon } from 'tdesign-icons-vue-next';
-const chart1 = ref();
-const chart2 = ref();
-const chart3 = ref();
-const chart4 = ref();
-const chart5 = ref();
-const chart6 = ref();
-const curTimeRange = ref([]);
-const serviceOptions = ref([]);
-
-const timeParams = computed(() => {
-  return {
-    startTime: new Date(curTimeRange.value[0] + ' 00:00:00').getTime(),
-    endTime: new Date(curTimeRange.value[1] + ' 23:59:59').getTime(),
-  };
-});
-const serviceId = ref('');
-const centerGroup = ref('REGION');
-const timeChange = (time) => {
-  sopServiceList(timeParams.value).then((res) => {
-    serviceOptions.value = res || [];
-    res?.length && (serviceId.value = res[0].id);
-  });
-};
-const sort1 = ref('PENDING');
-const sort2 = ref('PENDING');
-const sort3 = ref('PENDING');
-
-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 options8 = computed(() => {
-  let res = result8.value || [];
-  let xData = [],
-    sData = [];
-  if (res.length) {
-    xData = res.map((item) => item.timeStr);
-  }
-  if (res.length && res[0].sopSupplierAvgViewInfo?.length) {
-    res = res.map((item) => {
-      item.sopSupplierAvgViewInfo = item.sopSupplierAvgViewInfo || [];
-      item.sopSupplierAvgViewInfo.sort((a, b) => a.supplierId - b.supplierId);
-      return item;
-    });
-
-    sData = res[0].sopSupplierAvgViewInfo.map((item) => ({
-      name: item.supplierName,
-      data: res.map((v) => {
-        return (
-          v.sopSupplierAvgViewInfo.find((x) => x.supplierId == item.supplierId)
-            ?.rate || []
-        );
-      }),
-    }));
-  }
-  return createLineOption({ xData, sData });
-});
-
-const tableDataHandle = (data) => {
-  return data || [];
-};
-const tableColumns1 = [
-  {
-    prop: 'crmName',
-    label: '客户名称',
-    style: { width: '60px' },
-  },
-  {
-    prop: 'region_name',
-    label: '大区',
-    style: { width: '70px' },
-  },
-  {
-    prop: 'real_name',
-    label: '区域协调人',
-    style: { width: '70px' },
-  },
-  { prop: 'avgWarn', label: '预警数', style: { width: '60px' } },
-];
-const tableColumns2 = [
-  {
-    prop: 'region_name',
-    label: '大区',
-    style: { width: '60px' },
-  },
-  {
-    prop: 'real_name',
-    label: '大区经理',
-    style: { width: '70px' },
-  },
-  {
-    prop: 'avgWarn',
-    label: '预警数',
-    style: { width: '70px' },
-  },
-  { prop: 'avgMinutes', label: '均值', style: { width: '60px' } },
-];
-
-const tableColumns3 = [
-  {
-    prop: 'supplier',
-    label: '供应商',
-    style: { width: '60px' },
-  },
-  {
-    prop: 'region_name',
-    label: '大区',
-    style: { width: '70px' },
-  },
-  {
-    prop: 'real_name',
-    label: '区域协调人',
-    style: { width: '70px' },
-  },
-  { prop: 'avgWarn', label: '预警数', style: { width: '70px' } },
-  { prop: 'avgMinutes', label: '均值', style: { width: '60px' } },
-];
-
-const tableColumns4 = [
-  {
-    prop: 'crmName',
-    label: '客户名称',
-    style: { width: '60px' },
-  },
-  {
-    prop: 'dname',
-    label: '考勤对象',
-    style: { width: '70px' },
-  },
-  {
-    prop: 'real_name',
-    label: '区域协调人',
-    style: { width: '70px' },
-  },
-  { prop: 'total', label: '异常数', style: { width: '60px' } },
-];
-const tableColumns5 = [
-  {
-    prop: 'region_name',
-    label: '大区',
-    style: { width: '60px' },
-  },
-  {
-    prop: 'real_name',
-    label: '大区经理',
-    style: { width: '70px' },
-  },
-  {
-    prop: 'total',
-    label: '异常数',
-    style: { width: '70px' },
-  },
-  { prop: 'avg', label: '均值', style: { width: '60px' } },
-];
-
-const tableColumns6 = [
-  {
-    prop: 'supplier',
-    label: '供应商',
-    style: { width: '80px' },
-  },
-  { prop: 'total', label: '异常数', style: { width: '70px' } },
-  { prop: 'avg', label: '均值', style: { width: '60px' } },
-];
-watch(serviceId, (serviceId) => {
-  sort1.value = sort2.value = sort3.value = 'PENDING';
-  run1({
-    group: 'CRM',
-    serviceId,
-    sort: sort1.value,
-  });
-  run2({
-    group: 'REGION',
-    serviceId,
-    sort: sort2.value,
-  });
-  run3({
-    group: 'SUPPLIER',
-    serviceId,
-    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>
-.sop-analysis {
-  .page-main {
-    height: calc(100vh - 57px);
-    padding: 15px;
-    overflow: auto;
-    .scroll-content {
-      height: 100%;
-      min-height: 600px;
-      min-width: 1000px;
-      display: flex;
-
-      .chart-wrap {
-        height: calc(100% - 36px);
-      }
-      .col1,
-      .col3 {
-        width: 268px;
-        height: 100%;
-        .card {
-          height: calc((100% - 30px) / 3);
-          &:not(:first-child) {
-            margin-top: 15px;
-          }
-        }
-      }
-      .col2 {
-        width: calc(100% - 566px);
-        margin-left: 15px;
-        margin-right: 15px;
-        .card {
-          &:first-child {
-            height: calc((100% - 30px) * 2 / 3 + 15px);
-            .none-data {
-              height: calc(100% - 42px);
-              margin-top: 10px;
-              display: flex;
-              justify-content: center;
-              align-items: center;
-              color: #ccc;
-              font-size: 14px;
-            }
-            .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);
-            margin-top: 15px;
-          }
-        }
-      }
-    }
-
-    .card {
-      padding: 4px 10px 10px 10px;
-      background-color: #fff;
-      border: 1px solid #e5e5e5;
-      border-radius: 4px;
-      .title {
-        height: 36px;
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        .label {
-          color: #262626;
-          font-size: 14px;
-          // font-weight: bold;
-        }
-      }
-    }
-  }
-}
-</style>
+<template>
+  <div class="sop-analysis">
+    <report-header
+      title="SOP预警监控"
+      v-model:dateRange="curTimeRange"
+      @timeChange="timeChange"
+    >
+      <t-select
+        style="width: 200px"
+        :options="serviceOptions"
+        v-model="serviceId"
+        :keys="{ label: 'name', value: 'id' }"
+        filterable
+      ></t-select>
+    </report-header>
+    <div class="page-main">
+      <div class="scroll-content">
+        <div class="col1">
+          <div class="card">
+            <div class="title">
+              <t-select
+                v-model="sort1"
+                style="width: calc(100% - 50px)"
+                @change="changeSort('CRM', 1)"
+              >
+                <t-option
+                  value="PENDING"
+                  label="项目预警待处理TOP10"
+                ></t-option>
+                <t-option
+                  value="SLOWEST"
+                  label="项目预警处理最慢TOP10"
+                ></t-option>
+                <t-option
+                  value="FASTEST"
+                  label="项目预警处理最快TOP10"
+                ></t-option>
+              </t-select>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart1?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart1"
+                :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)"
+                @change="changeSort('REGION', 2)"
+              >
+                <t-option
+                  value="PENDING"
+                  label="大区预警待处理TOP10"
+                ></t-option>
+                <t-option
+                  value="SLOWEST"
+                  label="大区预警处理最慢TOP10"
+                ></t-option>
+                <t-option
+                  value="FASTEST"
+                  label="大区预警处理最快TOP10"
+                ></t-option>
+              </t-select>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart2?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart2"
+                :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)"
+                @change="changeSort('SUPPLIER', 3)"
+              >
+                <t-option
+                  value="PENDING"
+                  label="供应商预警待处理TOP10"
+                ></t-option>
+                <t-option
+                  value="SLOWEST"
+                  label="供应商预警处理最慢TOP10"
+                ></t-option>
+                <t-option
+                  value="FASTEST"
+                  label="供应商预警处理最快TOP10"
+                ></t-option>
+              </t-select>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart3?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart3"
+                :data="tableDataHandle(result3)"
+                :columns="tableColumns3"
+              ></table-loop>
+            </div>
+          </div>
+        </div>
+        <div class="col2">
+          <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" v-if="result7?.length">
+              <div
+                class="list-item"
+                v-for="(item, index) in result7"
+                :key="index"
+              >
+                <div class="item-head">
+                  {{
+                    centerGroup === 'SUPPLIER'
+                      ? item.supplierName
+                      : item.regionName
+                  }}
+                </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 class="none-data" v-else>暂无数据</div>
+          </div>
+          <div class="card">
+            <div class="title">
+              <span class="label">供应商预警均值走势</span>
+            </div>
+            <div class="chart-wrap">
+              <my-chart :options="options8"></my-chart>
+            </div>
+          </div>
+        </div>
+        <div class="col3">
+          <div class="card">
+            <div class="title">
+              <span class="label">项目考勤异常TOP10</span>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart4?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart4"
+                :data="tableDataHandle(result4)"
+                :columns="tableColumns4"
+              ></table-loop>
+            </div>
+          </div>
+          <div class="card">
+            <div class="title">
+              <span class="label">大区考勤异常TOP10</span>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart5?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart5"
+                :data="tableDataHandle(result5)"
+                :columns="tableColumns5"
+              ></table-loop>
+            </div>
+          </div>
+          <div class="card">
+            <div class="title">
+              <span class="label">供应商考勤异常TOP5</span>
+              <FullscreenIcon
+                class="cursor-pointer"
+                @click="chart6?.maximize"
+                color="#595959"
+              />
+            </div>
+            <div class="chart-wrap">
+              <table-loop
+                ref="chart6"
+                :data="tableDataHandle(result6)"
+                :columns="tableColumns6"
+              ></table-loop>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="SopAnalysis">
+import { ref, computed, watch, onMounted } from 'vue';
+import TableLoop from '@/components/common/table-loop/index.vue';
+import { useRequest } from 'vue-request';
+import { division, dateFormat } from '@/utils/tool';
+import {
+  sopServiceList,
+  warningTop,
+  attendanceTop,
+  supWarningTrend,
+  sopWarningAnalysis,
+} from '@/api/report';
+import { createLineOption } from '@/utils/chart';
+import { FullscreenIcon } from 'tdesign-icons-vue-next';
+const chart1 = ref();
+const chart2 = ref();
+const chart3 = ref();
+const chart4 = ref();
+const chart5 = ref();
+const chart6 = ref();
+const curTimeRange = ref([]);
+const serviceOptions = ref([]);
+
+const timeParams = computed(() => {
+  return {
+    startTime: new Date(curTimeRange.value[0] + ' 00:00:00').getTime(),
+    endTime: new Date(curTimeRange.value[1] + ' 23:59:59').getTime(),
+  };
+});
+const serviceId = ref('');
+const centerGroup = ref('REGION');
+const timeChange = (time) => {
+  sopServiceList(timeParams.value).then((res) => {
+    serviceOptions.value = res || [];
+    res?.length && (serviceId.value = res[0].id);
+  });
+};
+const sort1 = ref('PENDING');
+const sort2 = ref('PENDING');
+const sort3 = ref('PENDING');
+
+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 options8 = computed(() => {
+  let res = result8.value || [];
+  let xData = [],
+    sData = [];
+  if (res.length) {
+    xData = res.map((item) => item.timeStr);
+  }
+  if (res.length && res[0].sopSupplierAvgViewInfo?.length) {
+    res = res.map((item) => {
+      item.sopSupplierAvgViewInfo = item.sopSupplierAvgViewInfo || [];
+      item.sopSupplierAvgViewInfo.sort((a, b) => a.supplierId - b.supplierId);
+      return item;
+    });
+
+    sData = res[0].sopSupplierAvgViewInfo.map((item) => ({
+      name: item.supplierName,
+      data: res.map((v) => {
+        return (
+          v.sopSupplierAvgViewInfo.find((x) => x.supplierId == item.supplierId)
+            ?.rate || []
+        );
+      }),
+    }));
+  }
+  return createLineOption({ xData, sData });
+});
+
+const tableDataHandle = (data) => {
+  return data || [];
+};
+const tableColumns1 = [
+  {
+    prop: 'crmName',
+    label: '客户名称',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'region_name',
+    label: '大区',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'real_name',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'avgWarn', label: '预警数', style: { width: '60px' } },
+];
+const tableColumns2 = [
+  {
+    prop: 'region_name',
+    label: '大区',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'real_name',
+    label: '大区经理',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'avgWarn',
+    label: '预警数',
+    style: { width: '70px' },
+  },
+  { prop: 'avgMinutes', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns3 = [
+  {
+    prop: 'supplier',
+    label: '供应商',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'region_name',
+    label: '大区',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'real_name',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'avgWarn', label: '预警数', style: { width: '70px' } },
+  { prop: 'avgMinutes', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns4 = [
+  {
+    prop: 'crmName',
+    label: '客户名称',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'dname',
+    label: '考勤对象',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'real_name',
+    label: '区域协调人',
+    style: { width: '70px' },
+  },
+  { prop: 'total', label: '异常数', style: { width: '60px' } },
+];
+const tableColumns5 = [
+  {
+    prop: 'region_name',
+    label: '大区',
+    style: { width: '60px' },
+  },
+  {
+    prop: 'real_name',
+    label: '大区经理',
+    style: { width: '70px' },
+  },
+  {
+    prop: 'total',
+    label: '异常数',
+    style: { width: '70px' },
+  },
+  { prop: 'avg', label: '均值', style: { width: '60px' } },
+];
+
+const tableColumns6 = [
+  {
+    prop: 'supplier',
+    label: '供应商',
+    style: { width: '80px' },
+  },
+  { prop: 'total', label: '异常数', style: { width: '70px' } },
+  { prop: 'avg', label: '均值', style: { width: '60px' } },
+];
+watch(serviceId, (serviceId) => {
+  sort1.value = sort2.value = sort3.value = 'PENDING';
+  run1({
+    group: 'CRM',
+    serviceId,
+    sort: sort1.value,
+  });
+  run2({
+    group: 'REGION',
+    serviceId,
+    sort: sort2.value,
+  });
+  run3({
+    group: 'SUPPLIER',
+    serviceId,
+    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>
+.sop-analysis {
+  .page-main {
+    height: calc(100vh - 57px);
+    padding: 15px;
+    overflow: auto;
+    .scroll-content {
+      height: 100%;
+      min-height: 600px;
+      min-width: 1000px;
+      display: flex;
+
+      .chart-wrap {
+        height: calc(100% - 36px);
+      }
+      .col1,
+      .col3 {
+        width: 268px;
+        height: 100%;
+        .card {
+          height: calc((100% - 30px) / 3);
+          &:not(:first-child) {
+            margin-top: 15px;
+          }
+        }
+      }
+      .col2 {
+        width: calc(100% - 566px);
+        margin-left: 15px;
+        margin-right: 15px;
+        .card {
+          &:first-child {
+            height: calc((100% - 30px) * 2 / 3 + 15px);
+            .none-data {
+              height: calc(100% - 42px);
+              margin-top: 10px;
+              display: flex;
+              justify-content: center;
+              align-items: center;
+              color: #ccc;
+              font-size: 14px;
+            }
+            .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);
+            margin-top: 15px;
+          }
+        }
+      }
+    }
+
+    .card {
+      padding: 4px 10px 10px 10px;
+      background-color: #fff;
+      border: 1px solid #e5e5e5;
+      border-radius: 4px;
+      .title {
+        height: 36px;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        .label {
+          color: #262626;
+          font-size: 14px;
+          // font-weight: bold;
+        }
+      }
+    }
+  }
+}
+</style>