Jelajahi Sumber

reactor:宜宾司法大屏改造

zhangchong 2 minggu lalu
induk
melakukan
d18b36aa57

+ 1 - 1
.env.development

@@ -3,6 +3,6 @@ VITE_MODE_NAME=development
 # socket API
 VITE_API_SOCKET_URL=http://110.188.24.28:50100/hubs/hotline
 # 基础请求地址
-VITE_API_URL=http://110.188.24.28:50100
+VITE_API_URL=http://110.188.24.28:50300
 # 防止部署多套系统到同一域名不同目录时,变量共用的问题 设置不同的前缀
 VITE_STORAGE_NAME=dev

+ 19 - 19
src/api/judicial.ts

@@ -14,67 +14,67 @@ export const leftTopData = (params?: object) => {
     params,
   });
 };
-
 /**
- * @description 中数据
+ * @description 中上地图数据
  */
-export const leftCenterData = (params?: object) => {
+export const centerTopData = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-accept-type-statistics`,
+    url: `/api/v1/JudicialManagementScreen/order-area-query`,
     method: "get",
     params,
   });
 };
 /**
- * @description 下数据
+ * @description 下数据
  */
-export const leftBottomData = (params?: object) => {
+export const centerBottomData = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-event-type-statistics`,
+    url: `/api/v1/JudicialManagementScreen/order-overview`,
     method: "get",
     params,
   });
 };
 /**
- * @description 中上地图数据
+ * @description 部门满意度统计(左下数据)
  */
-export const centerTopData = (params?: object) => {
+export const orgSatis = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-area-query`,
+    url: `/api/v1/JudicialManagementScreen/order-visitorg-statistics`,
     method: "get",
     params,
   });
 };
 /**
- * @description 中下数据
+ * @description 工单执法分类月份趋势(右上数据)
  */
-export const centerBottomData = (params?: object) => {
+export const monthlyTrend = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-overview`,
+    url: `/api/v1/JudicialManagementScreen/order-monthly-trend`,
     method: "get",
     params,
   });
 };
 /**
- * @description 右上数据
+ * @description 问题类型统计(右中数据)
  */
-export const rightTopData = (params?: object) => {
+export const problemType = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-visitorg-statistics`,
+    url: `/api/v1/JudicialManagementScreen/order-eventType-statistics`,
     method: "get",
     params,
   });
 };
 /**
- * @description 右中数据
+ * @description 执法领域统计(右下数据)
  */
-export const rightCenterData = (params?: object) => {
+export const lawArea = (params?: object) => {
   return request({
-    url: `/api/v1/JudicialManagementScreen/order-monthly-trend`,
+    url: `/api/v1/JudicialManagementScreen/order-industry-statistics`,
     method: "get",
     params,
   });
 };
+
 /**
  * @description 右下数据
  */

+ 1 - 0
src/utils/mitt.ts

@@ -18,6 +18,7 @@ declare type MittType = {
   EnforcementOrderHandlingDetail: string; // 大屏展示 催办中工单概览(司法行政)
   monitorInfo: string; // 监听分机消息
   monitorInfoTel: string; // 监听分机变化消息
+  SelectArea: object; // 选择区域
 };
 // 类型
 const emitter: Emitter<MittType> = mitt<MittType>();

+ 17 - 10
src/views/judicial/center-bottom.vue

@@ -3,16 +3,15 @@
     <div class="center_bottom-title flex">
       <div class="flex items-center">
         <img src="@/assets/img/home/title_arrow.png" alt="" />
-        今日行政执法工单概览
+        今日工单概况
       </div>
     </div>
     <div class="center_bottom-content">
       <div class="table-header">
-        <div class="table-header-item">来源</div>
-        <div class="table-header-item">标题</div>
-        <div class="table-header-item">受理类型</div>
-        <div class="table-header-item">热点类型</div>
-        <div class="table-header-item">区域</div>
+        <div class="table-header-item">超期状态</div>
+        <div class="table-header-item">工单标题</div>
+        <div class="table-header-item">热点分类</div>
+        <div class="table-header-item">事发地址</div>
         <div class="table-header-item">执法部门</div>
       </div>
       <div class="scroll" v-loading="loading">
@@ -23,14 +22,23 @@
           v-if="list.length"
         >
           <div class="item" v-for="(item, index) in list" :key="index">
-            <TextTooltip :content="item.sourceChannel"></TextTooltip>
+            <span>
+              <span class="exceedSoon" v-if="item.expiredStatus === 1">{{
+                item.expiredStatusText
+              }}</span>
+              <span class="exceed" v-if="item.expiredStatus === 2">{{
+                item.expiredStatusText
+              }}</span>
+              <span class="normal" v-if="item.expiredStatus === 0">{{
+                item.expiredStatusText
+              }}</span>
+            </span>
             <TextTooltip :content="item.title"></TextTooltip>
-            <TextTooltip :content="item.acceptType"></TextTooltip>
             <TextTooltip :content="item.hotspotName"></TextTooltip>
             <TextTooltip :content="item.areaName"></TextTooltip>
             <TextTooltip
               :content="
-                item.enforcementOrdersHandler.map((item) => item.key).join(',')
+                item.enforcementOrdersHandler?.map((item) => item.key).join(',')
               "
             >
             </TextTooltip>
@@ -90,7 +98,6 @@ onUnmounted(() => {
   }
 
   &-title {
-    font-size: 20px;
     color: #fff;
     justify-content: space-between;
     align-items: center;

+ 12 - 25
src/views/judicial/center-map.vue

@@ -16,7 +16,7 @@
           <div class="custom-tooltip-style-box-text">
             <div class="custom-tooltip-style-box-text-item">
               <span class="custom-tooltip-style-box-text-item-title"
-                >工单量</span
+                >工单量</span
               ><span class="custom-tooltip-style-box-text-item-value">{{
                 currentSelectData.orderCountNum
               }}</span>
@@ -24,31 +24,17 @@
             </div>
             <div class="custom-tooltip-style-box-text-item">
               <span class="custom-tooltip-style-box-text-item-title"
-                >行政执法工单:</span
-              ><span class="custom-tooltip-style-box-text-item-value">{{
-                currentSelectData.enforcementOrderNum
-              }}</span>
-              <b> 件</b>
-            </div>
-            <div class="custom-tooltip-style-box-text-item">
-              <span class="custom-tooltip-style-box-text-item-title"
-                >线索属实工单:</span
-              ><span class="custom-tooltip-style-box-text-item-value">{{
-                currentSelectData.theClueIsTrueNum
-              }}</span>
-              <b> 件</b>
-            </div>
-            <div class="custom-tooltip-style-box-text-item">
-              <span class="custom-tooltip-style-box-text-item-title"
-                >线索不属实工单:</span
-              ><span class="custom-tooltip-style-box-text-item-value">{{
-                currentSelectData.theClueIsNotTrueNum
-              }}</span>
+                >行政执法工单/非行政执法工单</span
+              ><span class="custom-tooltip-style-box-text-item-value"
+                >{{ currentSelectData.enforcementOrderNum }}/{{
+                  currentSelectData.theClueIsNotTrueNum
+                }}</span
+              >
               <b> 件</b>
             </div>
             <div class="custom-tooltip-style-box-text-item">
               <span class="custom-tooltip-style-box-text-item-title"
-                >推诿工单</span
+                >推诿工单</span
               ><span class="custom-tooltip-style-box-text-item-value">{{
                 currentSelectData.passTheBuckOrderNum
               }}</span>
@@ -70,6 +56,7 @@ import { loadingOptions } from "@/utils/constants";
 import { centerTopData } from "api/judicial";
 import { useThemeConfig } from "@/stores/themeConfig";
 import { storeToRefs } from "pinia";
+import mittBus from "@/utils/mitt";
 
 const storesThemeConfig = useThemeConfig();
 const { themeConfig } = storeToRefs(storesThemeConfig);
@@ -166,11 +153,11 @@ onMounted(async () => {
     } else {
       currentSelectData.value = {};
     }
+    mittBus.emit("SelectArea", currentSelectData.value);
   });
   centerMapRef.value.chart.on("click", async (params: any) => {
     showTip.value = !!currentSelectData.value.name;
     await nextTick();
-    console.log(tooltipBox.value, mapContainer.value, "11");
     tooltipBox.value.style.left = `10px`;
     tooltipBox.value.style.bottom = `10px`;
   });
@@ -194,7 +181,7 @@ onMounted(async () => {
   // 给子盒子自定义样式
   .custom-tooltip-style {
     width: 355px;
-    height: 259px;
+    height: 200px;
     background-image: url("@/assets/img/home/tool_tip_bg.png");
     background-size: 100% 100%;
     color: #fff;
@@ -243,7 +230,7 @@ onMounted(async () => {
 
           &-title {
             display: inline-block;
-            width: 180px;
+            width: 210px;
             overflow: hidden;
             text-overflow: ellipsis;
           }

+ 0 - 2
src/views/judicial/container.vue

@@ -2,7 +2,6 @@
   <div class="container-box">
     <div class="container-left">
       <LeftTop class="container-left-top" :dateArray="dateArray" />
-      <!--      <LeftCenter class="container-left-center" :dateArray="dateArray"/>-->
       <LeftBottom class="container-left-bottom" :dateArray="dateArray" />
     </div>
     <div class="container-center">
@@ -18,7 +17,6 @@
 </template>
 <script setup lang="ts">
 import LeftTop from "@/views/judicial/left-top.vue";
-import LeftCenter from "@/views/judicial/left-center.vue";
 import LeftBottom from "@/views/judicial/left-bottom.vue";
 import CenterMap from "@/views/judicial/center-map.vue";
 import CenterBottom from "@/views/judicial/center-bottom.vue";

+ 130 - 300
src/views/judicial/left-bottom.vue

@@ -3,35 +3,26 @@
     <div class="left_bottom-title flex">
       <div class="flex items-center">
         <img src="@/assets/img/home/title_arrow.png" alt="" />
-        高频事项分析
+        部门满意度排行
       </div>
-      <el-dropdown @command="handleCommand">
-        <el-button link class="link-button">
-          {{ areaText }}
-          <el-icon class="el-icon--right">
-            <arrow-down />
-          </el-icon>
-        </el-button>
-        <template #dropdown>
-          <el-dropdown-menu>
-            <el-dropdown-item
-              v-for="item in areaList"
-              :key="item.id"
-              :command="item"
-              >{{ item.areaName }}</el-dropdown-item
-            >
-          </el-dropdown-menu>
-        </template>
-      </el-dropdown>
     </div>
-    <div class="left_bottom-content">
+    <div class="left_bottom-content" v-loading="loading">
       <template v-if="list.length">
-        <v-chart
-          class="chart"
-          :option="option"
-          :loading="loading"
-          :loading-options="loadingOptions"
-        />
+        <div class="scroll">
+          <div class="scroll-item" v-for="(item, index) in list" :key="index">
+            <span class="scroll-item-area" :class="index <= 2 ? 'three' : ''"
+              >NO.{{ index + 1 }}</span
+            >
+            <TextTooltip
+              :content="item.visitOrgName"
+              className="scroll-item-title-ju"
+            ></TextTooltip>
+            <span class="scroll-item-hotspot-ju"
+              ><CountUp :endVal="item.visitCount" :duration="2" />(件)</span
+            >
+            <span class="scroll-item-num">{{ item.satisfiedRate }}%</span>
+          </div>
+        </div>
       </template>
       <empty v-else />
     </div>
@@ -39,19 +30,20 @@
 </template>
 <script setup lang="ts">
 import { defineAsyncComponent, onMounted, ref, watch } from "vue";
-import { ArrowDown } from "@element-plus/icons-vue";
-import { getArea } from "@/api/home";
 import dayjs from "dayjs";
-import { loadingOptions } from "@/utils/constants";
-import { graphic } from "echarts/core";
-import { arraySortByKey } from "@/utils/tools";
-import { leftBottomData } from "api/judicial";
-import { useThemeConfig } from "@/stores/themeConfig";
-import { storeToRefs } from "pinia";
+import { orgSatis } from "api/judicial";
+import mittBus from "@/utils/mitt";
 
+const TextTooltip = defineAsyncComponent(
+  () => import("@/components/TextTooltip/index.vue")
+);
 const Empty = defineAsyncComponent(
   () => import("@/components/Empty/index.vue")
 );
+const CountUp = defineAsyncComponent(
+  () => import("@/components/Count-up/index.vue")
+);
+
 const props = defineProps({
   dateArray: {
     type: Array,
@@ -60,7 +52,6 @@ const props = defineProps({
 });
 const date = ref([]);
 const loading = ref(false);
-const option = ref<any>({});
 watch(
   () => props.dateArray,
   (val: any) => {
@@ -71,304 +62,143 @@ watch(
 
 watch(
   () => props.dateArray,
-  () => {
+  (val: any) => {
     getData();
   }
 );
-const storesThemeConfig = useThemeConfig();
-const { themeConfig } = storeToRefs(storesThemeConfig);
-const areaCode = ref(themeConfig.value.cityCode);
-const areaText = ref(themeConfig.value.cityName);
 const list = ref<any>([]);
+const AreaCode = ref(null);
 const getData = async () => {
   loading.value = true;
   try {
-    const { result } = await leftBottomData({
+    const { result } = await orgSatis({
       StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
       EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
-      AreaCode: areaCode.value,
+      AreaCode: AreaCode.value,
     });
-    list.value = result;
-    const category = arraySortByKey(list.value, "sumCount");
-    const charts = {
-      // 按顺序排列从大到小
-      cityList: category.map((item: any) => item.eventTypeName),
-      cityData: category.map((item: any) => item.sumCount),
-    };
-    const top10CityList = charts.cityList;
-    const top10CityData = charts.cityData;
-    const color = ["#ff9500", "#02d8f9", "#027fff"];
-    const color1 = ["#ffb349", "#70e9fc", "#4aa4ff"];
-
-    let lineY = [];
-    let lineT = [];
-    for (let i = 0; i < charts.cityList.length; i++) {
-      let x = i;
-      if (x > 1) {
-        x = 2;
-      }
-      const data = {
-        name: charts.cityList[i],
-        color: color[x],
-        value: top10CityData[i],
-        barGap: "-100%",
-        itemStyle: {
-          show: true,
-          color: new graphic.LinearGradient(
-            0,
-            0,
-            1,
-            0,
-            [
-              {
-                offset: 0,
-                color: color[x],
-              },
-              {
-                offset: 1,
-                color: color1[x],
-              },
-            ],
-            false
-          ),
-          borderRadius: 10,
-        },
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 15,
-            shadowColor: "rgba(0, 0, 0, 0.1)",
-          },
-        },
-      };
-      const data1 = {
-        value: top10CityData[0],
-        itemStyle: {
-          color: "#001235",
-          borderRadius: 10,
-        },
-      };
-      lineY.push(data);
-      lineT.push(data1);
-    }
-
-    option.value = {
-      backgroundColor: "rgba(0, 0, 0, 0)",
-      title: {
-        show: false,
-      },
-      tooltip: {
-        trigger: "axis",
-        axisPointer: {
-          type: "shadow",
-        },
-        formatter: function (params: any) {
-          let tar;
-          if (params[0].seriesIndex === 0) {
-            tar = params[0];
-          } else {
-            tar = params[1];
-          }
-          return tar.marker + tar.name + "<br/>" + " 当前数量: " + tar.value;
-        },
-
-        enterable: true, //滚动条
-        confine: true,
-        extraCssText: "max-width:90%;max-height:83%;overflow:auto;",
-        //改变提示框的位置 不超出屏幕显示
-        position: function (point, params, dom, rect, size) {
-          //其中point为当前鼠标的位置,
-          //size中有两个属性:viewSize和contentSize,分别为外层div和tooltip提示框的大小
-          // 鼠标坐标和提示框位置的参考坐标系是:以外层div的左上角那一点为原点,x轴向右,y轴向下
-          // 提示框位置
-          let x = 0; // x坐标位置
-          let y = 0; // y坐标位置
-          // 当前鼠标位置
-          const pointX = point[0];
-          const pointY = point[1];
-          // 提示框大小
-          const boxWidth = size.contentSize[0];
-          const boxHeight = size.contentSize[1];
-          // boxWidth > pointX 说明鼠标左边放不下提示框
-          if (boxWidth > pointX) {
-            x = 5;
-          } else {
-            // 左边放的下
-            x = pointX - boxWidth;
-          }
-          // boxHeight > pointY 说明鼠标上边放不下提示框
-          if (boxHeight > pointY) {
-            y = 5;
-          } else {
-            // 上边放得下
-            y = pointY - boxHeight;
-          }
-          return [x, y];
-        },
-      },
-      grid: {
-        borderWidth: 0,
-        top: "5%",
-        left: "5%",
-        right: "15%",
-        bottom: "10%",
-      },
-      color: color,
-      yAxis: [
-        {
-          type: "category",
-          inverse: true,
-          axisTick: {
-            show: false,
-          },
-          axisLine: {
-            show: false,
-          },
-          axisLabel: {
-            show: false,
-            inside: false,
-          },
-          data: top10CityList,
-        },
-        {
-          type: "category",
-          inverse: true,
-          axisLine: {
-            show: false,
-          },
-          axisTick: {
-            show: false,
-          },
-          axisLabel: {
-            show: true,
-            inside: false,
-            verticalAlign: "middle",
-            lineHeight: "40",
-            color: "#fff",
-            fontSize: "14",
-            fontFamily: "PingFangSC-Regular",
-            formatter: function (val) {
-              return `${val}`;
-            },
-          },
-          splitArea: {
-            show: false,
-          },
-          splitLine: {
-            show: false,
-          },
-          data: top10CityData,
-        },
-      ],
-      xAxis: {
-        type: "value",
-        axisTick: {
-          show: false,
-        },
-        axisLine: {
-          show: false,
-        },
-        splitLine: {
-          show: false,
-        },
-        axisLabel: {
-          show: false,
-        },
-      },
-      series: [
-        {
-          type: "bar",
-          zLevel: 2,
-          barWidth: "10px",
-          data: lineY,
-          label: {
-            color: "#b3ccf8",
-            show: true,
-            position: [0, "-18px"],
-            fontSize: 16,
-            formatter: function (a) {
-              let num = "";
-              let str = "";
-              if (a.dataIndex + 1 < 10) {
-                num = "0" + (a.dataIndex + 1);
-              } else {
-                num = a.dataIndex + 1;
-              }
-              if (a.dataIndex === 0) {
-                str = `{color1|${num}} {color4|${a.name}}`;
-              } else if (a.dataIndex === 1) {
-                str = `{color2|${num}} {color4|${a.name}}`;
-              } else {
-                str = `{color3|${num}} {color4|${a.name}}`;
-              }
-              return str;
-            },
-            rich: {
-              color1: {
-                color: "#ff9500",
-                fontWeight: 700,
-              },
-              color2: {
-                color: "#02d8f9",
-                fontWeight: 700,
-              },
-              color3: {
-                color: "#027fff",
-                fontWeight: 700,
-              },
-              color4: {
-                color: "#e5eaff",
-              },
-            },
-          },
-        },
-      ],
-    };
+    // list.value = [...result, ...result, ...result, ...result];
+    list.value = [...result];
     loading.value = false;
   } catch (e) {
     console.log(e);
     loading.value = false;
   }
 };
-// 选择省市区
-const handleCommand = (command: any) => {
-  areaCode.value = command.id;
-  areaText.value = command.areaName;
-  getData();
-};
-const areaList = ref([]);
-const getAreaData = async () => {
-  try {
-    const res = await getArea();
-    areaList.value = res.result ?? [];
-  } catch (e) {
-    console.log(e);
-  }
-};
 onMounted(() => {
   getData();
-  getAreaData();
+  mittBus.on("SelectArea", (data: any) => {
+    AreaCode.value = data.areaCode;
+    getData();
+  });
 });
 </script>
 <style scoped lang="scss">
 .left_bottom {
   padding: 0 30px;
+  font-size: 15px;
 
   &-title {
-    font-size: 20px;
     color: #fff;
     justify-content: space-between;
     align-items: center;
   }
 
   &-content {
-    height: calc(100% - 30px);
+    margin-top: 15px;
+    height: 100%;
+
+    .scroll {
+      height: 500px;
+      overflow: auto;
+
+      &-item {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        padding: 8px 5px;
+        color: #d2d4d5;
+        margin-bottom: 15px;
+
+        &:nth-child(odd) {
+          background-color: rgba(255, 255, 255, 0.1);
+          border-radius: 5px;
+        }
+
+        &-area {
+          display: inline-block;
+          background-color: #69bbf6;
+          padding: 0 6px;
+          color: #333;
+          border-top-left-radius: 4px;
+          border-bottom-left-radius: 4px;
+          white-space: nowrap;
+          position: relative;
+
+          &:after {
+            content: "";
+            border-left: 8px solid #69bbf6;
+            border-right: 12px solid transparent;
+            border-top: 12px solid transparent;
+            border-bottom: 10px solid transparent;
+            width: 0;
+            height: 0;
+            position: absolute;
+            right: -19px;
+          }
+        }
+
+        .three {
+          background-color: #eca455;
+          position: relative;
+        }
+
+        .three:after {
+          content: "";
+          border-left: 8px solid #eca455;
+          border-right: 12px solid transparent;
+          border-top: 12px solid transparent;
+          border-bottom: 10px solid transparent;
+          width: 0;
+          height: 0;
+          position: absolute;
+          right: -19px;
+        }
+
+        &-title {
+          width: 50%;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          margin-left: 20px;
+        }
+
+        &-hotspot {
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          flex: 1;
+          margin-left: 30px;
+        }
+      }
+    }
   }
 }
 
-:deep(.link-button) {
-  cursor: pointer;
-  color: #7dbdec;
-  display: flex;
-  align-items: center;
+.count-up-wrap {
+  display: inline-block;
+}
+</style>
+<style lang="scss">
+.scroll-item-title-ju {
+  width: 50%;
+  margin-left: 20px;
+}
+
+.scroll-item-hotspot-ju {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  flex: 1;
+  margin-left: 30px;
 }
 </style>

+ 0 - 246
src/views/judicial/left-center.vue

@@ -1,246 +0,0 @@
-<template>
-  <div class="left-center">
-    <div class="left-center-title flex items-center">
-      <img src="@/assets/img/home/title_arrow.png" alt="" />
-      受理类型办件分析
-    </div>
-    <div class="left-center-content">
-      <template v-if="xData.length">
-        <v-chart
-          class="chart"
-          :option="option"
-          :loading="loading"
-          :loading-options="loadingOptions"
-        />
-      </template>
-      <empty v-else />
-    </div>
-  </div>
-</template>
-<script setup lang="ts">
-import { ref, onMounted, watch, defineAsyncComponent } from "vue";
-import dayjs from "dayjs";
-import { loadingOptions } from "@/utils/constants";
-import { leftCenterData } from "api/judicial";
-
-const Empty = defineAsyncComponent(
-  () => import("@/components/Empty/index.vue")
-);
-const props = defineProps({
-  dateArray: {
-    type: Array,
-    default: () => [],
-  },
-});
-
-const date = ref([]);
-watch(
-  () => props.dateArray,
-  (val: any) => {
-    date.value = val;
-  },
-  { immediate: true }
-);
-
-watch(
-  () => props.dateArray,
-  (val: any) => {
-    getData();
-  }
-);
-
-const option = ref({});
-const xData = ref([]);
-const loading = ref(true);
-const getData = async () => {
-  loading.value = true;
-  try {
-    const { result } = await leftCenterData({
-      StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
-      EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
-    });
-    xData.value = result.map((item: any) => item.acceptType);
-    const totalData = result.reduce((pre: any, cur: any) => {
-      pre.push(cur.sumCount);
-      return pre;
-    }, []);
-    const handlingCount = result.reduce((pre: any, cur: any) => {
-      pre.push(cur.handlingCount);
-      return pre;
-    }, []);
-    const filedCount = result.reduce((pre: any, cur: any) => {
-      pre.push(cur.filedCount);
-      return pre;
-    }, []);
-    const overTimeCount = result.reduce((pre: any, cur: any) => {
-      pre.push(cur.overTimeCount);
-      return pre;
-    }, []);
-    // mock 数据
-    let dataArr = {
-      xData: xData.value,
-      result: [
-        { name: "总数", data: totalData },
-        { name: "已办", data: handlingCount },
-        { name: "在办", data: filedCount },
-        { name: "超期", data: overTimeCount },
-      ],
-      series: [],
-    };
-    const diamondData = dataArr.result.reduce((pre, cur, index) => {
-      pre[index] = cur.data.map(
-        (el, id) => el + (pre[index - 1] ? pre[index - 1][id] : 0)
-      );
-      return pre;
-    }, []);
-    const color = [
-      [
-        { offset: 0, color: "#efff37" },
-        { offset: 1, color: "#d5e700" },
-      ],
-      [
-        { offset: 0, color: "#32ffee" },
-        { offset: 1, color: "#00e8d5" },
-      ],
-      [
-        { offset: 0, color: "#46c9ff" },
-        { offset: 1, color: "#00b4ff" },
-      ],
-      [
-        { offset: 0, color: "#54a0ff" },
-        { offset: 1, color: "#1f83ff" },
-      ],
-    ];
-
-    dataArr.series = dataArr.result.reduce((p, c, i, array) => {
-      p.push(
-        {
-          z: i + 1,
-          stack: true,
-          type: "bar",
-          name: c.name,
-          barWidth: 15,
-          data: c.data,
-          itemStyle: {
-            color: {
-              type: "linear",
-              x: 0,
-              x2: 0,
-              y: 0,
-              y2: 1,
-              colorStops: color[i],
-            },
-          },
-        },
-        {
-          z: i + 10,
-          name: c.name,
-          type: "pictorialBar",
-          symbolPosition: "end",
-          symbol: "circle",
-          symbolOffset: [0, "-50%"],
-          symbolSize: [15, 12.5],
-          data: diamondData[i],
-          itemStyle: {
-            color: color[i + 1] ? color[i + 1][0].color : null,
-          },
-          tooltip: { show: false },
-        }
-      );
-
-      return p;
-    }, []);
-
-    // 最上边顶
-    dataArr.series.push({
-      name: dataArr.result[dataArr.result.length - 1].name,
-      z: 20,
-      type: "pictorialBar",
-      symbolPosition: "end",
-      data: diamondData[diamondData.length - 1],
-      symbol: "circle",
-      symbolOffset: ["0%", "-50%"],
-      symbolSize: [15, 12.5],
-      itemStyle: {
-        color: color[color.length - 1][0].color,
-      },
-      tooltip: { show: false },
-    });
-
-    // 最下边底
-    dataArr.series.push({
-      name: dataArr.result[0].name,
-      z: 30,
-      type: "pictorialBar",
-      symbolPosition: "start",
-      data: diamondData[0],
-      symbol: "circle",
-      symbolOffset: ["0%", "50%"],
-      symbolSize: [15, 12.5],
-      itemStyle: {
-        color: color[0][0].color,
-      },
-      tooltip: { show: false },
-    });
-    setOption(dataArr);
-    loading.value = false;
-  } catch (e) {
-    console.log(e);
-    loading.value = false;
-  }
-};
-const setOption = (data: any) => {
-  option.value = {
-    tooltip: {
-      trigger: "axis",
-      backgroundColor: "rgba(0,0,0,0.7)",
-      borderColor: "#000",
-      borderWidth: 1,
-      textStyle: { color: "#fff" },
-    },
-    xAxis: {
-      axisTick: { show: true },
-      axisLine: { lineStyle: { color: "rgba(255,255,255, .2)" } },
-      axisLabel: { fontSize: 12, color: "#fff" },
-      data: data.xData,
-    },
-    yAxis: [
-      {
-        name: "单位:件",
-        nameTextStyle: { color: "#fff", fontSize: 14 },
-        nameGap: 25,
-        splitLine: { lineStyle: { color: "rgba(255,255,255, .05)" } },
-        axisLine: { show: false },
-        axisLabel: { fontSize: 12, color: "#fff" },
-      },
-    ],
-    grid: { top: "20%", left: "10%", right: "3%", bottom: "10%" },
-    legend: {
-      data: data.result.map((item) => item.name),
-      textStyle: { fontSize: 12, color: "#fff" },
-      itemWidth: 15,
-      itemHeight: 10,
-      top: "10%",
-      right: "20",
-    },
-    series: data.series,
-  };
-};
-onMounted(() => {
-  getData();
-});
-</script>
-<style scoped lang="scss">
-.left-center {
-  padding: 0 30px;
-
-  &-title {
-    font-size: 20px;
-    color: #fff;
-  }
-
-  .left-center-content {
-    height: calc(100% - 30px);
-  }
-}
-</style>

+ 31 - 21
src/views/judicial/left-top.vue

@@ -4,13 +4,10 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/done_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">行政执法工单</div>
+          <div class="left-top-content-item-text-title">工单总量</div>
           <div class="left-top-content-item-text-num">
             <b>
-              <CountUp
-                :endVal="state.order.enforcementOrderNum"
-                :duration="2"
-              />
+              <CountUp :endVal="state.order.orderCountNum" :duration="2" />
             </b>
           </div>
@@ -19,10 +16,13 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/wait_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">线索属实工单</div>
+          <div class="left-top-content-item-text-title">推诿工单</div>
           <div class="left-top-content-item-text-num">
             <b>
-              <CountUp :endVal="state.order.theClueIsTrueNum" :duration="2" />
+              <CountUp
+                :endVal="state.order.passTheBuckOrderNum"
+                :duration="2"
+              />
             </b>
           </div>
@@ -31,11 +31,11 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/ex_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">线索不属实工单</div>
+          <div class="left-top-content-item-text-title">执法类工单</div>
           <div class="left-top-content-item-text-num">
             <b>
               <CountUp
-                :endVal="state.order.theClueIsNotTrueNum"
+                :endVal="state.order.enforcementOrderNum"
                 :duration="2"
               />
             </b>
@@ -46,11 +46,11 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/delay_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">推诿工单</div>
+          <div class="left-top-content-item-text-title">待核实工单</div>
           <div class="left-top-content-item-text-num">
             <b>
               <CountUp
-                :endVal="state.order.passTheBuckOrderNum"
+                :endVal="state.order.theClueIsNotTrueNum"
                 :duration="2"
               />
             </b>
@@ -61,10 +61,13 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/my_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">满意工单</div>
+          <div class="left-top-content-item-text-title">非执法类工单</div>
           <div class="left-top-content-item-text-num">
             <b>
-              <CountUp :endVal="state.order.satisfiedCount" :duration="2" />
+              <CountUp
+                :endVal="state.order.theClueIsNotTrueNum"
+                :duration="2"
+              />
             </b>
           </div>
@@ -73,10 +76,10 @@
       <div class="flex left-top-content-item">
         <img src="@/assets/img/home/provice_order.png" alt="" />
         <div class="left-top-content-item-text">
-          <div class="left-top-content-item-text-title">工单总量</div>
+          <div class="left-top-content-item-text-title">满意工单</div>
           <div class="left-top-content-item-text-num">
             <b>
-              <CountUp :endVal="state.order.orderCountNum" :duration="2" />
+              <CountUp :endVal="state.order.satisfiedCount" :duration="2" />
             </b>
           </div>
@@ -89,6 +92,7 @@
 import { defineAsyncComponent, onMounted, reactive, ref, watch } from "vue";
 import dayjs from "dayjs";
 import { leftTopData } from "api/judicial";
+import mittBus from "@/utils/mitt";
 
 const CountUp = defineAsyncComponent(
   () => import("@/components/Count-up/index.vue")
@@ -118,21 +122,23 @@ watch(
 const duration = ref(2);
 const state = reactive({
   order: {
-    enforcementOrderNum: 0,
-    orderCountNum: 0,
-    passTheBuckOrderNum: 0,
-    satisfiedCount: 0,
-    theClueIsNotTrueNum: 0,
-    theClueIsTrueNum: 0,
+    enforcementOrderNum: 0, // 行政执法工单
+    orderCountNum: 0, // 工单总量
+    passTheBuckOrderNum: 0, // 推诿工单
+    satisfiedCount: 0, // 满意工单
+    theClueIsNotTrueNum: 0, // 待核实工单
+    theClueIsTrueNum: 0, // 线索属实工单
   },
 });
 // 获取工单统计
+const AreaCode = ref(null);
 const getWorkOrder = async () => {
   loading.value = true;
   try {
     const { result } = await leftTopData({
       StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
       EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
+      AreaCode: AreaCode.value,
     });
     state.order = result;
     loading.value = false;
@@ -144,6 +150,10 @@ const getWorkOrder = async () => {
 
 onMounted(() => {
   getWorkOrder();
+  mittBus.on("SelectArea", (data: any) => {
+    AreaCode.value = data.areaCode;
+    getWorkOrder();
+  });
 });
 </script>
 <style scoped lang="scss">

+ 11 - 18
src/views/judicial/right-bottom.vue

@@ -3,16 +3,7 @@
     <div class="left_bottom-title flex">
       <div class="flex items-center">
         <img src="@/assets/img/home/title_arrow.png" alt="" />
-        占比分析
-      </div>
-      <div class="left_bottom-title-tabs">
-        <span
-          :class="{ active: activeIndex === 0 }"
-          class="mr-5 left_bottom-title-tabs-item"
-          @click="change(0)"
-          >来源方式</span
-        >
-        <!--        <span :class="{active: activeIndex === 1}" class="left_bottom-title-tabs-item" @click="change(1)"  @click="change(0)">受理类型</span>-->
+        执法领域统计
       </div>
     </div>
     <div class="left_bottom-content">
@@ -32,7 +23,8 @@
 import { ref, onMounted, watch, computed, defineAsyncComponent } from "vue";
 import dayjs from "dayjs";
 import { loadingOptions } from "@/utils/constants";
-import { rightBottomData } from "api/judicial";
+import { lawArea, rightBottomData } from "api/judicial";
+import mittBus from "@/utils/mitt";
 
 const Empty = defineAsyncComponent(
   () => import("@/components/Empty/index.vue")
@@ -64,19 +56,17 @@ const option = ref<any>({});
 const activeText = computed(() => {
   return activeIndex.value === 0 ? "来源占比" : "类型占比";
 });
-const change = (index: number) => {
-  /*  activeIndex.value = index;
-  getData();*/
-};
+
 const dataList = ref([]);
 const loading = ref(false);
+const AreaCode = ref(null);
 const getData = async () => {
   loading.value = true;
   try {
-    const { result } = await rightBottomData({
+    const { result } = await lawArea({
       StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
       EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
-      IsSource: activeIndex.value === 0,
+      AreaCode: AreaCode.value,
     });
     const legEndTime = result
       .map((item: any) => item.name)
@@ -232,6 +222,10 @@ const setOption = (legEndTime: any, data: any) => {
 };
 onMounted(() => {
   getData();
+  mittBus.on("SelectArea", (data: any) => {
+    AreaCode.value = data.areaCode;
+    getData();
+  });
 });
 </script>
 <style scoped lang="scss">
@@ -239,7 +233,6 @@ onMounted(() => {
   padding: 0 30px;
 
   &-title {
-    font-size: 20px;
     color: #fff;
     justify-content: space-between;
     align-items: center;

+ 38 - 17
src/views/judicial/right-center.vue

@@ -3,16 +3,8 @@
     <div class="right_center-title">
       <div class="flex items-center">
         <img src="@/assets/img/home/title_arrow.png" alt="" />
-        工单月份趋势图
+        问题类型统计
       </div>
-      <el-date-picker
-        v-model="yearValue"
-        type="year"
-        placeholder="选择年份"
-        :clearable="false"
-        value-format="YYYY"
-        @change="changeYear"
-      />
     </div>
     <div class="right_center-content h100">
       <template v-if="list.length">
@@ -28,26 +20,51 @@
   </div>
 </template>
 <script setup lang="ts">
-import { defineAsyncComponent, onMounted, ref } from "vue";
-import dayjs from "dayjs";
+import { defineAsyncComponent, onMounted, ref, watch } from "vue";
 import { loadingOptions } from "@/utils/constants";
 import { graphic } from "echarts/core";
-import { rightCenterData } from "api/judicial";
+import { problemType } from "@/api/judicial";
+import dayjs from "dayjs";
+import mittBus from "@/utils/mitt";
 
 const Empty = defineAsyncComponent(
   () => import("@/components/Empty/index.vue")
 );
-const yearValue = ref(dayjs().year().toString());
+
+const props = defineProps({
+  dateArray: {
+    type: Array,
+    default: () => [],
+  },
+});
+const date = ref([]);
+watch(
+  () => props.dateArray,
+  (val: any) => {
+    date.value = val;
+  },
+  { immediate: true }
+);
+
+watch(
+  () => props.dateArray,
+  (val: any) => {
+    getData();
+  }
+);
+
 const list = ref<any>([]);
 const loading = ref(false);
 const option = ref<any>({});
-const changeYear = () => {
-  getData();
-};
+const AreaCode = ref(null);
 const getData = async () => {
   loading.value = true;
   try {
-    const { result } = await rightCenterData({ Year: yearValue.value });
+    const { result } = await problemType({
+      StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
+      EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
+      AreaCode: AreaCode.value,
+    });
     const xData = result.map((item: any) => {
       return item.month + "月";
     });
@@ -193,6 +210,10 @@ const setOption = (xData: any, data: any) => {
 };
 onMounted(() => {
   getData();
+  mittBus.on("SelectArea", (data: any) => {
+    AreaCode.value = data.areaCode;
+    getData();
+  });
 });
 </script>
 <style scoped lang="scss">

+ 168 - 152
src/views/judicial/right-top.vue

@@ -3,194 +3,210 @@
     <div class="right_center-title flex">
       <div class="flex items-center">
         <img src="@/assets/img/home/title_arrow.png" alt="" />
-        部门满意度排行榜
+        工单执法分类月份趋势
       </div>
+      <el-date-picker
+        v-model="yearValue"
+        type="year"
+        placeholder="选择年份"
+        :clearable="false"
+        value-format="YYYY"
+        @change="changeYear"
+      />
     </div>
     <div class="right_center-content" v-loading="loading">
       <template v-if="list.length">
-        <div class="scroll">
-          <div class="scroll-item" v-for="(item, index) in list" :key="index">
-            <span class="scroll-item-area" :class="index <= 2 ? 'three' : ''"
-              >NO.{{ index + 1 }}</span
-            >
-            <TextTooltip
-              :content="item.visitOrgName"
-              className="scroll-item-title-ju"
-            ></TextTooltip>
-            <span class="scroll-item-hotspot-ju"
-              ><CountUp :endVal="item.visitCount" :duration="2" />(件)</span
-            >
-            <span class="scroll-item-num">{{ item.satisfiedRate }}%</span>
-          </div>
-        </div>
+        <v-chart
+          class="chart"
+          :option="option"
+          :loading="loading"
+          :loading-options="loadingOptions"
+        />
       </template>
       <empty v-else />
     </div>
   </div>
 </template>
 <script setup lang="ts">
-import { onMounted, ref, watch, defineAsyncComponent } from "vue";
+import { onMounted, ref, defineAsyncComponent } from "vue";
 import dayjs from "dayjs";
-import { rightTopData } from "api/judicial";
+import { monthlyTrend } from "@/api/judicial";
+import { loadingOptions } from "@/utils/constants";
+import { graphic } from "echarts/core";
 
-const TextTooltip = defineAsyncComponent(
-  () => import("@/components/TextTooltip/index.vue")
-);
 const Empty = defineAsyncComponent(
   () => import("@/components/Empty/index.vue")
 );
-const CountUp = defineAsyncComponent(
-  () => import("@/components/Count-up/index.vue")
-);
-const props = defineProps({
-  dateArray: {
-    type: Array,
-    default: () => [],
-  },
-});
-const date = ref([]);
-watch(
-  () => props.dateArray,
-  (val: any) => {
-    date.value = val;
-  },
-  { immediate: true }
-);
-
-watch(
-  () => props.dateArray,
-  (val: any) => {
-    getData();
-  }
-);
+const yearValue = ref(dayjs().year().toString());
 const list = ref<any>([]);
 const loading = ref(false);
+const option = ref<any>({});
+const changeYear = () => {
+  getData();
+};
 const getData = async () => {
   loading.value = true;
   try {
-    const { result } = await rightTopData({
-      StartTime: dayjs(date.value[0]).format("YYYY-MM-DD"),
-      EndTime: dayjs(date.value[1]).format("YYYY-MM-DD"),
+    const { result } = await monthlyTrend({ Year: yearValue.value });
+    const xData = result.map((item: any) => {
+      return item.month + "月";
     });
-    list.value = result;
+    list.value = result.map((item: any) => {
+      return item.sumCount;
+    });
+    setOption(xData, list.value);
+    loading.value = false;
     loading.value = false;
   } catch (e) {
-    console.log(e);
     loading.value = false;
+    console.log(e);
   }
 };
+const setOption = (xData: any, data: any) => {
+  option.value = {
+    tooltip: {
+      show: true,
+      trigger: "axis",
+      formatter: (params) => {
+        let arr = [...params];
+        let str = "";
+        arr.forEach((item, index) => {
+          str += item.marker + "工单数量 " + item.data + "<br />";
+        });
+        str = arr[0].name + "<br />" + str;
+        return str;
+      },
+    },
+    grid: {
+      top: "15%",
+      left: "8%",
+      right: "5%",
+      bottom: "18%",
+      containLabel: true,
+    },
+    xAxis: [
+      {
+        type: "category",
+        axisLine: {
+          onZero: true,
+          lineStyle: {
+            color: "#81b0d0",
+          },
+        },
+        axisLabel: {
+          interval: 0,
+          align: "center",
+          margin: 10,
+          color: "#fff",
+          rotate: 0,
+        },
+        splitLine: {
+          show: false,
+        },
+        axisTick: {
+          show: false,
+        },
+        boundaryGap: false,
+        data: xData,
+      },
+    ],
+    yAxis: [
+      {
+        type: "value",
+        splitLine: {
+          show: true,
+          lineStyle: {
+            color: "rgba(221, 228, 241,.3)",
+          },
+        },
+        axisLabel: {
+          interval: 0,
+          align: "center",
+          margin: 10,
+          color: "#fff",
+          rotate: 0,
+        },
+      },
+    ],
+    series: [
+      {
+        data: data,
+        type: "line",
+        smooth: true,
+
+        symbolSize: 0.01,
+
+        areaStyle: {
+          color: new graphic.LinearGradient(0, 0, 0, 0.9, [
+            {
+              offset: 0,
+              // color: 'rgba(58,77,233,0.8)'
+              color: "rgba(170, 201 ,234,0.8)",
+            },
+            {
+              offset: 1,
+              // color: 'rgba(58,77,233,0)'
+              color: "rgba(170, 201 ,234,0)",
+            },
+            {
+              offset: 0,
+              color: "rgba(170, 201 ,234,1)",
+              // color: 'rgba(58,77,233,0.9)'
+            },
+          ]),
+        },
+        itemStyle: {
+          // color: "#3D5DF4",
+          color: "#4EA6FE",
+        },
+        lineStyle: {
+          // 线条加阴影
+          // 设置阴影颜色
+          // shadowColor: "#3D5DF4",
+          shadowColor: "#4EA6FE",
+          shadowOffsetX: 0,
+          // 设置阴影沿y轴偏移量为9
+          shadowOffsetY: 9,
+          // 设置阴影的模糊大小
+          shadowBlur: 10,
+          // 设置线条渐变色
+          color: new graphic.LinearGradient(0, 0, 1, 0, [
+            { offset: 0, color: "#94C2FD00" },
+            // { offset: 0.2, color: "#3D5DF4" },
+            { offset: 0.2, color: "#4EA6FE" },
+            { offset: 0.1, color: "#5B8FF900" },
+          ]),
+        },
+
+        emphasis: {
+          scale: 1000,
+          itemStyle: {
+            color: "#3D5DF4",
+            borderColor: "#FFFFFF",
+            borderWidth: 3,
+            borderType: "solid",
+          },
+        },
+      },
+    ],
+  };
+};
 onMounted(() => {
   getData();
 });
 </script>
 <style scoped lang="scss">
 .right_center {
-  padding: 0 30px;
-  font-size: 15px;
-
-  &-title {
-    font-size: 20px;
-    color: #fff;
-    justify-content: space-between;
-    align-items: center;
-  }
-
   &-content {
     margin-top: 15px;
     height: 100%;
-
-    .scroll {
-      height: 300px;
-      overflow: hidden;
-
-      &-item {
-        display: flex;
-        justify-content: space-between;
-        align-items: center;
-        padding: 8px 5px;
-        color: #d2d4d5;
-        margin-bottom: 15px;
-
-        &:nth-child(odd) {
-          background-color: rgba(255, 255, 255, 0.1);
-          border-radius: 5px;
-        }
-
-        &-area {
-          display: inline-block;
-          background-color: #69bbf6;
-          padding: 0 6px;
-          color: #333;
-          border-top-left-radius: 4px;
-          border-bottom-left-radius: 4px;
-          white-space: nowrap;
-          position: relative;
-
-          &:after {
-            content: "";
-            border-left: 8px solid #69bbf6;
-            border-right: 12px solid transparent;
-            border-top: 12px solid transparent;
-            border-bottom: 10px solid transparent;
-            width: 0;
-            height: 0;
-            position: absolute;
-            right: -19px;
-          }
-        }
-
-        .three {
-          background-color: #eca455;
-          position: relative;
-        }
-
-        .three:after {
-          content: "";
-          border-left: 8px solid #eca455;
-          border-right: 12px solid transparent;
-          border-top: 12px solid transparent;
-          border-bottom: 10px solid transparent;
-          width: 0;
-          height: 0;
-          position: absolute;
-          right: -19px;
-        }
-
-        &-title {
-          width: 50%;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          margin-left: 20px;
-        }
-
-        &-hotspot {
-          overflow: hidden;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          flex: 1;
-          margin-left: 30px;
-        }
-      }
-    }
   }
-}
-
-.count-up-wrap {
-  display: inline-block;
-}
-</style>
-<style lang="scss">
-.scroll-item-title-ju {
-  width: 50%;
-  margin-left: 20px;
-}
 
-.scroll-item-hotspot-ju {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  flex: 1;
-  margin-left: 30px;
+  &-title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-right: 20px;
+  }
 }
 </style>