Эх сурвалжийг харах

新增子系统页面、加载优化

AaronBruin 1 сар өмнө
parent
commit
65528c7e04
90 өөрчлөгдсөн 8709 нэмэгдсэн , 925 устгасан
  1. 84 151
      index.html
  2. BIN
      public/IBMS.png
  3. 1 1
      src/api/home.js
  4. 3 2
      src/api/login.js
  5. 1 1
      src/api/system/access.js
  6. 1 1
      src/api/system/broadcast.js
  7. 1 1
      src/api/system/building.js
  8. 1 1
      src/api/system/elevator.js
  9. 1 1
      src/api/system/endpoint.js
  10. 1 1
      src/api/system/energy.js
  11. 1 1
      src/api/system/illuminating.js
  12. 1 1
      src/api/system/information.js
  13. 1 1
      src/api/system/invade.js
  14. 1 1
      src/api/system/meeting.js
  15. 1 1
      src/api/system/monitor.js
  16. 1 1
      src/api/system/passageway.js
  17. 1 1
      src/api/system/passenger.js
  18. 1 1
      src/api/system/tenement.js
  19. 1 1
      src/api/system/visitor.js
  20. 0 0
      src/assets/icons/svg/airConditioner.svg
  21. 1 0
      src/assets/icons/svg/dynamicEnvironment.svg
  22. 1 0
      src/assets/icons/svg/fireAlarm.svg
  23. 1 0
      src/assets/icons/svg/floodlighting.svg
  24. 1 0
      src/assets/icons/svg/sceneLighting.svg
  25. 1 0
      src/assets/icons/svg/wastewater.svg
  26. 0 0
      src/assets/icons/svg/wastewaterCopy.svg
  27. 0 0
      src/assets/icons/svg/wirelessIntercom.svg
  28. 1 1
      src/components/waterdrop/index.vue
  29. 67 3
      src/layout/components/AppMain.vue
  30. 3 2
      src/layout/components/Sidebar/Logo.vue
  31. 7 5
      src/layout/components/Sidebar/index.vue
  32. 67 0
      src/views/system/airConditioner/index.vue
  33. 211 0
      src/views/system/airConditioner/modules/blowingIn.vue
  34. 151 0
      src/views/system/airConditioner/modules/consume.vue
  35. 211 0
      src/views/system/airConditioner/modules/energy.vue
  36. 239 0
      src/views/system/airConditioner/modules/running.vue
  37. 211 0
      src/views/system/airConditioner/modules/temperatureTrend.vue
  38. 64 0
      src/views/system/dynamicEnvironment/index.vue
  39. 211 0
      src/views/system/dynamicEnvironment/modules/blowingIn.vue
  40. 236 0
      src/views/system/dynamicEnvironment/modules/deviceList.vue
  41. 211 0
      src/views/system/dynamicEnvironment/modules/energy.vue
  42. 211 0
      src/views/system/dynamicEnvironment/modules/temperatureTrend.vue
  43. 3 3
      src/views/system/energy/modules/running.vue
  44. 16 14
      src/views/system/entranceguard/passable.vue
  45. 74 0
      src/views/system/fireAlarm/index.vue
  46. 236 0
      src/views/system/fireAlarm/modules/deviceList.vue
  47. 228 0
      src/views/system/fireAlarm/modules/eventList.vue
  48. 211 0
      src/views/system/fireAlarm/modules/running.vue
  49. 234 0
      src/views/system/fireAlarm/modules/switchAll.vue
  50. 53 0
      src/views/system/floodlighting/index.vue
  51. 236 0
      src/views/system/floodlighting/modules/deviceList.vue
  52. 245 0
      src/views/system/floodlighting/modules/eventList.vue
  53. 537 0
      src/views/system/floodlighting/modules/running.vue
  54. 281 0
      src/views/system/floodlighting/modules/switchAll.vue
  55. 0 25
      src/views/system/lighting/modules/deviceList.vue
  56. 32 34
      src/views/system/message/index.vue
  57. 208 166
      src/views/system/message/modules/carbonEmission.vue
  58. 189 47
      src/views/system/message/modules/consume.vue
  59. 2 2
      src/views/system/message/modules/running.vue
  60. 1 1
      src/views/system/message/modules/sameDay.vue
  61. 91 183
      src/views/system/message/modules/tiring.vue
  62. 4 4
      src/views/system/passageway/Personnel.vue
  63. 20 11
      src/views/system/passageway/access.vue
  64. 1 1
      src/views/system/passageway/index.vue
  65. 53 0
      src/views/system/sceneLighting/index.vue
  66. 236 0
      src/views/system/sceneLighting/modules/deviceList.vue
  67. 245 0
      src/views/system/sceneLighting/modules/eventList.vue
  68. 537 0
      src/views/system/sceneLighting/modules/running.vue
  69. 281 0
      src/views/system/sceneLighting/modules/switchAll.vue
  70. 7 7
      src/views/system/tenement/modules/carbonEmission.vue
  71. 1 1
      src/views/system/tenement/modules/consume.vue
  72. 1 1
      src/views/system/tenement/modules/eventList.vue
  73. 5 5
      src/views/system/tenement/modules/sameDay.vue
  74. 24 12
      src/views/system/tenement/modules/tiring.vue
  75. 6 6
      src/views/system/video/index.vue
  76. 45 49
      src/views/system/video/modules/consume.vue
  77. 34 34
      src/views/system/video/modules/eventList.vue
  78. 191 107
      src/views/system/video/modules/running.vue
  79. 121 31
      src/views/system/video/modules/sameDay.vue
  80. 65 0
      src/views/system/wastewater/index.vue
  81. 211 0
      src/views/system/wastewater/modules/blowingIn.vue
  82. 151 0
      src/views/system/wastewater/modules/consume.vue
  83. 239 0
      src/views/system/wastewater/modules/malfunction.vue
  84. 239 0
      src/views/system/wastewater/modules/running.vue
  85. 67 0
      src/views/system/wirelessIntercom/index.vue
  86. 211 0
      src/views/system/wirelessIntercom/modules/blowingIn.vue
  87. 151 0
      src/views/system/wirelessIntercom/modules/consume.vue
  88. 237 0
      src/views/system/wirelessIntercom/modules/malfunction.vue
  89. 239 0
      src/views/system/wirelessIntercom/modules/running.vue
  90. 1 1
      vite.config.js

+ 84 - 151
index.html

@@ -6,7 +6,7 @@
   <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
   <meta name="renderer" content="webkit">
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
-  <link rel="icon" href="/favicon.ico">
+  <link rel="icon" href="/IBMS.png">
   <title>IBMS楼宇集成管理平台</title>
   <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
   <style>
@@ -32,188 +32,121 @@
       padding: 0.2em 0;
     }
 
-    #loader-wrapper {
-      position: fixed;
-      top: 0;
-      left: 0;
+    .loading_card {
       width: 100%;
-      height: 100%;
-      z-index: 999999;
-    }
-
-    #loader {
-      display: block;
-      position: relative;
-      left: 50%;
-      top: 50%;
-      width: 150px;
-      height: 150px;
-      margin: -75px 0 0 -75px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -webkit-animation: spin 2s linear infinite;
-      -ms-animation: spin 2s linear infinite;
-      -moz-animation: spin 2s linear infinite;
-      -o-animation: spin 2s linear infinite;
-      animation: spin 2s linear infinite;
-      z-index: 1001;
-    }
-
-    #loader:before {
-      content: "";
-      position: absolute;
-      top: 5px;
-      left: 5px;
-      right: 5px;
-      bottom: 5px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -webkit-animation: spin 3s linear infinite;
-      -moz-animation: spin 3s linear infinite;
-      -o-animation: spin 3s linear infinite;
-      -ms-animation: spin 3s linear infinite;
-      animation: spin 3s linear infinite;
-    }
-
-    #loader:after {
-      content: "";
-      position: absolute;
-      top: 15px;
-      left: 15px;
-      right: 15px;
-      bottom: 15px;
-      border-radius: 50%;
-      border: 3px solid transparent;
-      border-top-color: #FFF;
-      -moz-animation: spin 1.5s linear infinite;
-      -o-animation: spin 1.5s linear infinite;
-      -ms-animation: spin 1.5s linear infinite;
-      -webkit-animation: spin 1.5s linear infinite;
-      animation: spin 1.5s linear infinite;
-    }
-
-
-    @-webkit-keyframes spin {
-      0% {
-        -webkit-transform: rotate(0deg);
-        -ms-transform: rotate(0deg);
-        transform: rotate(0deg);
-      }
-
-      100% {
-        -webkit-transform: rotate(360deg);
-        -ms-transform: rotate(360deg);
-        transform: rotate(360deg);
-      }
+      height: 100vh;
+      background-color: #0f2a42;
+      display: flex;
+      justify-content: center;
+      align-items: center;
     }
 
-    @keyframes spin {
-      0% {
-        -webkit-transform: rotate(0deg);
-        -ms-transform: rotate(0deg);
-        transform: rotate(0deg);
-      }
-
-      100% {
-        -webkit-transform: rotate(360deg);
-        -ms-transform: rotate(360deg);
-        transform: rotate(360deg);
-      }
+    .load_15 {
+      width: 100px;
+      height: 100px;
+      display: inline-block;
     }
 
-
-    #loader-wrapper .loader-section {
-      position: fixed;
-      top: 0;
-      width: 51%;
-      height: 100%;
-      background: #7171C6;
-      z-index: 1000;
-      -webkit-transform: translateX(0);
-      -ms-transform: translateX(0);
-      transform: translateX(0);
+    .load_15 .sk-cube {
+      width: 33%;
+      height: 33%;
+      background-color: #07aae7;
+      float: left;
+      -webkit-animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
+      animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
     }
 
-    #loader-wrapper .loader-section.section-left {
-      left: 0;
+    .load_15 .sk-cube1 {
+      -webkit-animation-delay: 0.2s;
+      animation-delay: 0.2s;
     }
 
-    #loader-wrapper .loader-section.section-right {
-      right: 0;
+    .load_15 .sk-cube2 {
+      -webkit-animation-delay: 0.3s;
+      animation-delay: 0.3s;
     }
 
+    .load_15 .sk-cube3 {
+      -webkit-animation-delay: 0.4s;
+      animation-delay: 0.4s;
+    }
 
-    .loaded #loader-wrapper .loader-section.section-left {
-      -webkit-transform: translateX(-100%);
-      -ms-transform: translateX(-100%);
-      transform: translateX(-100%);
-      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    .load_15 .sk-cube4 {
+      -webkit-animation-delay: 0.1s;
+      animation-delay: 0.1s;
     }
 
-    .loaded #loader-wrapper .loader-section.section-right {
-      -webkit-transform: translateX(100%);
-      -ms-transform: translateX(100%);
-      transform: translateX(100%);
-      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    .load_15 .sk-cube5 {
+      -webkit-animation-delay: 0.2s;
+      animation-delay: 0.2s;
     }
 
-    .loaded #loader {
-      opacity: 0;
-      -webkit-transition: all 0.3s ease-out;
-      transition: all 0.3s ease-out;
+    .load_15 .sk-cube6 {
+      -webkit-animation-delay: 0.3s;
+      animation-delay: 0.3s;
     }
 
-    .loaded #loader-wrapper {
-      visibility: hidden;
-      -webkit-transform: translateY(-100%);
-      -ms-transform: translateY(-100%);
-      transform: translateY(-100%);
-      -webkit-transition: all 0.3s 1s ease-out;
-      transition: all 0.3s 1s ease-out;
+    .load_15 .sk-cube7 {
+      -webkit-animation-delay: 0s;
+      animation-delay: 0s;
     }
 
-    .no-js #loader-wrapper {
-      display: none;
+    .load_15 .sk-cube8 {
+      -webkit-animation-delay: 0.1s;
+      animation-delay: 0.1s;
     }
 
-    .no-js h1 {
-      color: #222222;
+    .load_15 .sk-cube9 {
+      -webkit-animation-delay: 0.2s;
+      animation-delay: 0.2s;
     }
 
-    #loader-wrapper .load_title {
-      font-family: 'Open Sans';
-      color: #FFF;
-      font-size: 19px;
-      width: 100%;
-      text-align: center;
-      z-index: 9999999999999;
-      position: absolute;
-      top: 60%;
-      opacity: 1;
-      line-height: 30px;
+    @-webkit-keyframes sk-cubeGridScaleDelay {
+
+      0%,
+      70%,
+      100% {
+        -webkit-transform: scale3D(1, 1, 1);
+        transform: scale3D(1, 1, 1);
+      }
+
+      35% {
+        -webkit-transform: scale3D(0, 0, 1);
+        transform: scale3D(0, 0, 1);
+      }
     }
 
-    #loader-wrapper .load_title span {
-      font-weight: normal;
-      font-style: italic;
-      font-size: 13px;
-      color: #FFF;
-      opacity: 0.5;
+    @keyframes sk-cubeGridScaleDelay {
+
+      0%,
+      70%,
+      100% {
+        -webkit-transform: scale3D(1, 1, 1);
+        transform: scale3D(1, 1, 1);
+      }
+
+      35% {
+        -webkit-transform: scale3D(0, 0, 1);
+        transform: scale3D(0, 0, 1);
+      }
     }
   </style>
 </head>
 
 <body>
   <div id="app">
-    <div id="loader-wrapper">
-      <div id="loader"></div>
-      <div class="loader-section section-left"></div>
-      <div class="loader-section section-right"></div>
-      <div class="load_title">正在加载系统资源,请耐心等待</div>
+    <div class="loading_card">
+      <div class="load_15">
+        <div class="sk-cube sk-cube1"></div>
+        <div class="sk-cube sk-cube2"></div>
+        <div class="sk-cube sk-cube3"></div>
+        <div class="sk-cube sk-cube4"></div>
+        <div class="sk-cube sk-cube5"></div>
+        <div class="sk-cube sk-cube6"></div>
+        <div class="sk-cube sk-cube7"></div>
+        <div class="sk-cube sk-cube8"></div>
+        <div class="sk-cube sk-cube9"></div>
+      </div>
     </div>
   </div>
   <script type="module" src="/src/main.js"></script>

BIN
public/IBMS.png


+ 1 - 1
src/api/home.js

@@ -7,6 +7,6 @@ export function getHome(query) {
         url: '/Home/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 3 - 2
src/api/login.js

@@ -1,5 +1,5 @@
 import request from '@/utils/request'
-
+const Url = "http://182.43.195.17:8010"
 // 登录方法
 export function login(username, password, code, uuid) {
   const data = {
@@ -15,7 +15,8 @@ export function login(username, password, code, uuid) {
       repeatSubmit: false
     },
     method: 'post',
-    data: data
+    data: data,
+    baseURL: import.meta.env.DEV ? '/dev-api' : Url,
   })
 }
 

+ 1 - 1
src/api/system/access.js

@@ -7,6 +7,6 @@ export function getAccess(query) {
         url: '/Hikvision/access',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/broadcast.js

@@ -7,6 +7,6 @@ export function getBroadcast(query) {
         url: '/broadcast/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/building.js

@@ -7,6 +7,6 @@ export function getIntell(query) {
         url: '/intell/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/elevator.js

@@ -7,6 +7,6 @@ export function getElevator(query) {
         url: '/elevator/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/endpoint.js

@@ -7,6 +7,6 @@ export function getEndpoint(query) {
         url: '/Hikvision/endpoint',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/energy.js

@@ -7,6 +7,6 @@ export function getEnergy(query) {
         url: '/energy/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/illuminating.js

@@ -7,6 +7,6 @@ export function getIlluminating(query) {
         url: '/illuminating/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/information.js

@@ -7,6 +7,6 @@ export function getInformation(query) {
         url: '/information/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/invade.js

@@ -7,6 +7,6 @@ export function getInvade(query) {
         url: '/Hikvision/invade',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/meeting.js

@@ -7,6 +7,6 @@ export function getDataAnalysis(query) {
         url: '/Conference/dataAnalysis',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/monitor.js

@@ -7,6 +7,6 @@ export function getHikvision(query) {
         url: '/Hikvision/monitor',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/passageway.js

@@ -7,6 +7,6 @@ export function getAccess(query) {
         url: '/Access/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/passenger.js

@@ -7,6 +7,6 @@ export function getPassenger(query) {
         url: '/Hikvision/passenger',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/tenement.js

@@ -7,6 +7,6 @@ export function getProperty(query) {
         url: '/property/count',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

+ 1 - 1
src/api/system/visitor.js

@@ -7,6 +7,6 @@ export function getVisitor(query) {
         url: '/Hikvision/visitor',
         method: 'get',
         params: query,
-        baseURL: import.meta.env.DEV ? '/APP' : 'Url',
+        baseURL: import.meta.env.DEV ? '/APP' : Url,
     })
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/assets/icons/svg/airConditioner.svg


+ 1 - 0
src/assets/icons/svg/dynamicEnvironment.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1750239067823" class="icon" viewBox="0 0 1036 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3715" xmlns:xlink="http://www.w3.org/1999/xlink" width="202.34375" height="200"><path d="M141.22097813 270.1539552c0-27.30666667 22.7555552-54.61333333 50.06222187-54.61333333h200.24888853v-68.26666667c0-13.65333333 9.10222187-22.7555552 18.2044448-22.7555552h213.90222187c13.65333333 0 22.7555552 9.10222187 18.2044448 22.7555552v68.26666667h200.24888853c27.30666667 0 50.06222187 27.30666667 50.06222294 54.61333333v18.2044448c0 27.30666667-22.7555552 54.61333333-50.06222294 54.61333333h-655.36c-27.30666667 0-50.06222187-27.30666667-50.06222186-54.61333333l4.55111146-18.2044448z m882.9155552 441.45777813c0 27.30666667-22.7555552 54.61333333-50.06222186 54.61333334h-4.55111147c-27.30666667 0-50.06222187-27.30666667-50.06222187-54.61333334v-40.96h-81.92v172.94222187c0 31.85777813-22.7555552 59.1644448-54.61333333 59.1644448h-523.37777813c-31.85777813 0-54.61333333-27.30666667-54.61333334-59.1644448v-172.94222187h-86.47111146v40.96c0 27.30666667-22.7555552 54.61333333-50.06222187 54.61333334h-4.55111147c-27.30666667 0-50.06222187-27.30666667-50.06222186-54.61333334v-163.84c0-27.30666667 22.7555552-54.61333333 50.06222186-54.61333333h4.55111147c27.30666667 0 50.06222187 27.30666667 50.06222187 54.61333333v27.30666667h86.47111146v-118.32888853c0-31.85777813 22.7555552-59.1644448 54.61333334-59.1644448h518.82666666c31.85777813 0 54.61333333 27.30666667 54.61333334 59.1644448v118.32888853h81.92v-27.30666667c0-27.30666667 22.7555552-54.61333333 50.06222186-54.61333333h4.55111147c27.30666667 0 50.06222187 27.30666667 50.06222187 54.61333333v163.84h4.55111146z" p-id="3716"></path></svg>

+ 1 - 0
src/assets/icons/svg/fireAlarm.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1750295064008" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2955" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M954.1632 1024H512c-24.4224 0-44.2368-17.6128-44.2368-39.424 0-21.7088 19.8144-39.3216 44.2368-39.3216h442.1632c24.4224 0 44.2368 17.6128 44.2368 39.3728 0 21.76-19.8144 39.3728-44.2368 39.3728z m-265.3184-275.712h-44.1856v157.5424h-88.4736v-157.5424c0-56.2688 33.7408-108.288 88.4736-136.3968a195.84 195.84 0 0 1 176.8448 0c54.784 28.16 88.4736 80.128 88.4736 136.3968v157.5424h-221.1328v-157.5424z m-176.8448 0v157.5424c-48.5376 0-87.9616 34.816-88.4736 77.9776-107.776-1.1776-210.4832-40.9088-285.0816-110.2336C63.8464 804.2496 23.6544 711.168 26.9312 615.168c-8.9088-99.328 27.648-197.632 101.376-272.5376 59.1872-59.0848 29.7984-185.088 29.7984-185.088C256.2048 220.5184 307.2 326.5536 290.816 433.2544 395.3664 307.5584 412.416 140.3904 335.0016 0c0 0 280.2176 100.0448 309.5552 354.4576a387.584 387.584 0 0 0 44.2368-196.9152s173.6704 113.408 176.5888 434.0224c-66.6624-45.2608-156.416-52.8384-231.424-19.5584-75.0592 33.28-122.368 101.632-121.9584 176.2816z"  p-id="2956"></path></svg>

+ 1 - 0
src/assets/icons/svg/floodlighting.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1750239070589" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3872" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M174.39255688 838.60894719v83.05923187a70.33182094 70.33182094 0 0 0 70.40539031 70.33182094h534.40412812a70.33182094 70.33182094 0 0 0 70.40538938-70.33182094v-83.05923187zM779.20207531 32.00007312H244.79794719A70.33182094 70.33182094 0 0 0 174.39255688 102.40546344v680.438295h675.21490781V102.40546344A70.33182094 70.33182094 0 0 0 779.20207531 32.00007312zM465.28379094 611.42833812l20.819985-169.20835593H381.93028344L553.4928425 203.41549437l-1.69208344 167.66340938h90.26897906z" p-id="3873"></path></svg>

+ 1 - 0
src/assets/icons/svg/sceneLighting.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1750239046252" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2928" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 0c108.739048 0 198.985143 52.72381 216.393143 121.904762H853.333333v97.523809H170.666667v-97.523809h124.94019C313.014857 52.72381 403.260952 0 512 0z m292.571429 268.190476l-73.142858 512H292.571429L219.428571 268.190476h585.142858zM414.47619 414.47619h-73.142857l24.380953 195.04762h73.142857l-24.380953-195.04762z m292.571429 414.476191v97.523809h-97.523809v97.52381H414.47619v-97.52381h-97.523809v-97.523809h390.095238z" p-id="2929"></path></svg>

+ 1 - 0
src/assets/icons/svg/wastewater.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1750239041064" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2771" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M891.5456 469.0944l-37.2224 55.808H745.472v-17.664l4.1472-99.3792-132.2496-55.808v-2.7136c0-24.5248-16.5376-44.9536-37.1712-53.0944V171.008h243.8144V102.912h-254.8224C565.0432 74.3424 541.5936 51.2 511.3344 51.2c-30.3104 0-55.1424 23.1424-57.856 51.712H198.6048v69.4272H442.368v125.2352c-22.016 8.192-37.1712 28.6208-37.1712 53.0944v2.7648l-132.2496 55.808 4.096 99.328v16.384H168.3456l-37.1712-55.808H51.2v411.0848h75.776l28.928-51.712h136.3968l-2.7648 2.7136-39.936 34.048v51.712L248.1664 972.8h526.2336l-2.7648-55.808V865.28l-38.6048-34.048-2.7136-2.7136h137.728l28.928 51.712H972.8v-411.136h-81.2544z" p-id="2772"></path></svg>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/assets/icons/svg/wastewaterCopy.svg


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
src/assets/icons/svg/wirelessIntercom.svg


+ 1 - 1
src/components/waterdrop/index.vue

@@ -5,7 +5,7 @@
             <div class="wave"
                 :style="{ backgroundColor: `${color}`, '--heightTop': heightTop, '--circleBackGround': circleBackGround, }">
                 <div :style="{ fontSize: fontSize, }" style="z-index: 2;text-align: center;white-space: pre-wrap;">
-                    {{ dropTitle }}
+                    {{ dropTitle == 'undefined%' ? '0%' : dropTitle }}
                 </div>
             </div>
         </div>

+ 67 - 3
src/layout/components/AppMain.vue

@@ -8,8 +8,10 @@
       <!-- </transition> -->
     </router-view>
     <iframe-toggle />
-    <iframe id="myIframe" :src="srcUrl" style="width: 100%;height: 100%;" frameborder="0" @mousemove="handleScroll"
-      @load="onLoad"></iframe>
+    <!-- <iframe id="myIframe" :src="srcUrl" style="width: 100%;height: 100%;" frameborder="0"
+      v-show="pathUrl == '/index'"></iframe>
+    <iframe id="myIframeil" :src="srcUrl1" style="width: 100%;height: 100%;" frameborder="0"
+      v-if="pathUrl != '/index' && pathShow"></iframe> -->
   </section>
 </template>
 
@@ -18,7 +20,10 @@ import iframeToggle from "./IframeToggle/index"
 import useTagsViewStore from '@/store/modules/tagsView'
 const route = useRoute()
 const tagsViewStore = useTagsViewStore()
-const srcUrl = 'http://192.168.11.68:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUdp'
+const pathUrl = ref('')
+const pathShow = ref(false)
+const srcUrl = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUdp'
+const srcUrl1 = ref('')
 onMounted(() => {
   addIframe()
 })
@@ -39,6 +44,55 @@ onMounted(() => {
 //   console.log('Iframe scrolled!');
 // }
 watch(() => route, (newVal) => {
+  pathUrl.value = newVal.path
+  console.log(pathUrl.value, 998);
+  if (pathUrl.value == '/system/building') {
+    // 楼宇自控
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd1'
+  } else if (pathUrl.value == '/system/lighting') {
+    // 照明系统
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd3'
+  } else if (pathUrl.value == '/system/elevator') {
+    // 电梯系统
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd2'
+  } else if (pathUrl.value == '/system/broadcast') {
+    // 公共广播
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU12'
+  } else if (pathUrl.value == '/system/tenement') {
+    // 物业管理
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU13'
+  } else if (pathUrl.value == '/system/message') {
+    // 信息发布
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd8'
+  } else if (pathUrl.value == '/system/energy') {
+    // 能源管理
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd4'
+  } else if (pathUrl.value == '/system/video') {
+    // 视频监控
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd5'
+  } else if (pathUrl.value == '/system/intruderalarm') {
+    // 入侵报警
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd6'
+  } else if (pathUrl.value == '/system/passageway') {
+    // 出入口控制
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU15'
+  } else if (pathUrl.value == '/system/inspection') {
+    // 电子巡查
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd7'
+  } else if (pathUrl.value == '/system/visitor') {
+    // 访客系统
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU11'
+  } else if (pathUrl.value == '/system/passengerFlow') {
+    // 客流统计
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU10'
+  } else if (pathUrl.value == '/system/entranceguard') {
+    // 门禁系统
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTUd9'
+  } else if (pathUrl.value == '/system/receptiondesk') {
+    // 会议系统
+    srcUrl1.value = 'http://192.168.11.38:9191/ViewRun?T_ViewID=41rwFKnP9R7DAQXGYkz2S8Vcba3qTU14'
+  }
+  pathShow.value = true
   addIframe()
 }, { deep: true, immediate: true } // 开启深度监听
 )
@@ -67,6 +121,16 @@ function addIframe() {
   user-select: none;
 }
 
+#myIframeil {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 1;
+  user-select: none;
+}
+
 .hasTagsView {
   .app-main {
     /* 84 = navbar + tags-view = 50 + 34 */

+ 3 - 2
src/layout/components/Sidebar/Logo.vue

@@ -9,7 +9,7 @@
         </template>
       </el-image>
       <div class="weather_title">
-        <span>{{ weathers || '未知' }} {{ temperatures || '未知' }}℃</span>
+        <span v-memo="[weathers, temperatures]">{{ weathers || '未知' }} {{ temperatures || '未知' }}℃</span>
         <span>{{ currentTime }} {{ hourMinuteSecond }}</span>
       </div>
     </div>
@@ -156,6 +156,7 @@ const getWeather = async (params) => {
       weathers.value = weatherArr.weather
       temperatures.value = weatherArr.temperature
       weatherList.forEach(el => {
+        console.log(el.name, weatherArr.weather, 335)
         if (el.name === weatherArr.weather) {
           imgUrl.value = el.url
         }
@@ -177,7 +178,7 @@ const expandFold = () => {
 }
 const intervalId = ref(null)
 onMounted(() => {
-  let totalSeconds  = 1000 * 60 * 10
+  let totalSeconds = 1000 * 60 * 10
   intervalId.value = setInterval(getIPCity, totalSeconds);
   getIPCity()
 })

+ 7 - 5
src/layout/components/Sidebar/index.vue

@@ -158,7 +158,7 @@ function goBack(event) {
   } else {
     router.push({ path: path || "/" });
   }
-  switchModel(event)
+  // switchModel(event)
 }
 function switchModel(params) {
   let num = 0
@@ -170,10 +170,12 @@ function switchModel(params) {
     num = 2
   }
   const iframe = document.getElementById("myIframe");
-  iframe.contentWindow.postMessage(
-    `window.iot3d.ParkSwitch(${num});`,
-    "*"
-  );
+  if (iframe) {
+    iframe.contentWindow.postMessage(
+      `window.iot3d.ParkSwitch(${num});`,
+      "*"
+    );
+  }
 }
 const timeTag = ref(null)
 const isLongPress = ref(false);

+ 67 - 0
src/views/system/airConditioner/index.vue

@@ -0,0 +1,67 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <consume :resultData="leftData" style="height: 35%;" />
+                    <running :resultData="MonitorList" style="flex: 1;" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>空调系统</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <blowingIn :resultData="leftData.OnedayPowerConsumption" style="flex: 1;" />
+                    <energy :resultData="leftData.OnedayPowerConsumption" style="flex: 1;" />
+                    <temperatureTrend :resultData="leftData.OnedayWaterConsumption" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Air">
+import { getEnergy } from "@/api/system/energy"
+import { getAccess } from "@/api/system/passageway"
+import layout from "@/components/layout_/index.vue";
+import consume from './modules/consume.vue'
+import running from './modules/running.vue'
+import blowingIn from './modules/blowingIn.vue'
+import energy from './modules/energy.vue'
+import temperatureTrend from './modules/temperatureTrend.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+const MonitorList = ref([])
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getEnergyData, 10000);
+    getEnergyData()
+    getAccessData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getEnergyData() {
+    getEnergy().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+
+function getAccessData() {
+    getAccess().then((res) => {
+        if (res.code == 200) {
+            MonitorList.value = res.data.DeviceList
+        }
+    })
+}
+
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 211 - 0
src/views/system/airConditioner/modules/blowingIn.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag type="right" value="能耗趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时气能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#49e645',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(58, 226, 56, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(58, 226, 56, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 151 - 0
src/views/system/airConditioner/modules/consume.vue

@@ -0,0 +1,151 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="设备统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">{{ resultData.EnergyCount }}</div>
+                    <div class="_running_mains_left_conter_text">空调总量</div>
+                </div>
+            </div>
+            <div class="_running_mains_right">
+                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
+                    <div class="_running_mains_right_item_tuan">
+                        <span class="_running_mains_right_item_tuan_flag"
+                            :style="{ backgroundColor: item.color }"></span>
+                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_running_mains_right_item__txt">
+                        <span>{{ item.state }}</span>
+                        <span :style="{ color: item.color, 'font-size': '12px', 'margin-left': '5px' }">
+                            {{ item.unit }}
+                        </span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const runningList = ref([
+    { name: '设备在线', state: 0, color: '#409eff', unit: '台' },
+    { name: '设备离线', state: 0, color: '#15acaa', unit: '台' },
+])
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        runningList.value[0].state = newVal.CarbonEmissions
+        runningList.value[1].state = newVal.EnergyIntensity
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+const heightVideo = ref(0)
+// 生命周期
+onMounted(() => {
+    var element = document.getElementById("videoWidth");
+    var width = element.offsetWidth;
+    heightVideo.value = width
+});
+</script>
+<style lang="scss" scoped>
+._running {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+
+        &_left {
+            width: 50%;
+            height: var(--heightRun);
+            position: relative;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
+        }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+
+
+    }
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
+    }
+}
+</style>

+ 211 - 0
src/views/system/airConditioner/modules/energy.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag type="right" value="送风温度趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时电能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#00F7FF',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(0, 247, 255, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(0, 247, 255, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 239 - 0
src/views/system/airConditioner/modules/running.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag value="空调设备列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.Name }}
+                        </el-text>
+                    </div>
+                    <div style="justify-content: flex-start;width: 60px;"
+                        :class="item.State == 0 ? 'blue_title' : 'red_title'">
+                        {{ item.State == 0 ? '在线' : '离线' }}</div>
+                    <div class="view_item">{{ item.Date }}</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+
+            &_btn {
+                color: red;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 211 - 0
src/views/system/airConditioner/modules/temperatureTrend.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag type="right" value="温度变化趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '温度',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#f5c400',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(245, 196, 0, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(245, 196, 0, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 64 - 0
src/views/system/dynamicEnvironment/index.vue

@@ -0,0 +1,64 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <blowingIn :resultData="leftData.OnedayPowerConsumption" style="flex: 1;" />
+                    <temperatureTrend :resultData="leftData.OnedayWaterConsumption" style="flex: 1;" />
+                    <energy :resultData="leftData.OnedayPowerConsumption" style="flex: 1;" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>视频</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <deviceList :resultData="IlluminatingDevice" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Air">
+import { getEnergy } from "@/api/system/energy"
+import { getIlluminating } from "@/api/system/illuminating"
+import layout from "@/components/layout_/index.vue";
+import blowingIn from './modules/blowingIn.vue'
+import energy from './modules/energy.vue'
+import temperatureTrend from './modules/temperatureTrend.vue'
+import deviceList from './modules/deviceList.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+const IlluminatingDevice = ref([])
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getEnergyData, 10000);
+    getEnergyData()
+    getIlluminatingData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getEnergyData() {
+    getEnergy().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+
+function getIlluminatingData() {
+    getIlluminating().then((res) => {
+        if (res.code == 200) {
+            IlluminatingDevice.value = res.data.IlluminatingDevice
+        }
+    })
+}
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 211 - 0
src/views/system/dynamicEnvironment/modules/blowingIn.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag value="空气质量"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时气能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#49e645',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(58, 226, 56, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(58, 226, 56, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 236 - 0
src/views/system/dynamicEnvironment/modules/deviceList.vue

@@ -0,0 +1,236 @@
+<template>
+    <div class="_deviceList">
+        <HeadlineTag value="设备列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="point_box" style="margin-top: 10px;">
+            <el-input v-model="value" placeholder="按巡查点名称搜索" />
+        </div>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
+                        <el-icon color="#168cdb">
+                            <el-icon>
+                                <Opportunity />
+                            </el-icon>
+                        </el-icon>
+                        {{ item.DeviceName }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated :class="item.SwitchStatus == 0 ? 'blue_title' : 'red_title'"
+                        style="flex: .2;">
+                        {{ item.SwitchStatus == 0 ? '开启' : '关闭' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated
+                        :class="item.OnlinePresence == 0 ? 'blue_title' : 'red_title'" style="flex: .2">
+                        {{ item.OnlinePresence == 0 ? '在线' : '离线' }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import { Opportunity, Aim } from '@element-plus/icons-vue'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+
+const value = ref('')
+
+const headerList = ref(['名称', '状态', '时间'])
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss">
+._deviceList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            padding: 10px;
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>
+
+<style lang="scss" scoped>
+.point_box {
+    margin: 10px 30px;
+}
+
+.point_box :deep(.el-input__wrapper) {
+    background-color: transparent !important;
+    box-shadow: 0 0 0 1px rgb(58, 86, 117) inset !important;
+}
+
+.point_box :deep(.el-input__wrapper.is-focus) {
+    box-shadow: 0 0 0 1px #409EFF inset !important;
+}
+
+.point_box :deep(.el-input__inner) {
+    color: #ffffff !important;
+}
+</style>

+ 211 - 0
src/views/system/dynamicEnvironment/modules/energy.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag value="湿度环测数据"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时电能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#00F7FF',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(0, 247, 255, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(0, 247, 255, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 211 - 0
src/views/system/dynamicEnvironment/modules/temperatureTrend.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag value="温度环测数据"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '温度',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#f5c400',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(245, 196, 0, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(245, 196, 0, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 3 - 3
src/views/system/energy/modules/running.vue

@@ -38,9 +38,9 @@ const props = defineProps({
 })
 
 const runningList = ref([
-    { name: '耗强度', state: 0, color: '#15acaa' },
-    { name: '碳排总量', state: 0, color: '#FFC107' },
-    { name: '碳排强度', state: 0, color: '#F44336' },
+    { name: '耗强度', state: 0, color: '#15acaa' },
+    { name: '电耗强度', state: 0, color: '#FFC107' },
+    { name: '气耗强度', state: 0, color: '#F44336' },
 ])
 
 watch(() => props.resultData, (newVal) => {

+ 16 - 14
src/views/system/entranceguard/passable.vue

@@ -92,7 +92,7 @@ const initAccess = (valueNum) => {
                 zlevel: 100,
                 name: '外部',
                 type: 'gauge',
-                radius: '100%',
+                radius: '95%',
                 center: ['50%', '50%'],
                 itemStyle: {
                     color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
@@ -209,7 +209,7 @@ const initAccess = (valueNum) => {
                     show: true,
                     width: 2,
                 },
-                radius: '78%',
+                radius: '70%',
                 axisTick: {
                     //一个一个的小刻度
                     show: false,
@@ -227,10 +227,11 @@ const initAccess = (valueNum) => {
                 axisLine: {
                     show: false,
                 },
+                // 指针
                 pointer: {
                     icon: 'triangle',
-                    width: '60%', //指针的宽度
-                    length: '70%', //指针长度,按照半圆半径的百分比
+                    width: '90%', //指针的宽度
+                    length: '66%', //指针长度,按照半圆半径的百分比
                 },
                 data: [
                     {
@@ -261,7 +262,8 @@ const initAccess = (valueNum) => {
                     show: true,
                     width: 2,
                 },
-                radius: '55%',
+                // 指针圆
+                radius: '36%',
                 axisTick: {
                     //一个一个的小刻度
                     show: false,
@@ -419,7 +421,7 @@ window.addEventListener('resize', () => {
 }
 
 .camera {
-    width: 260px;
+    width: 240px;
     height: 100%;
     display: flex;
     position: relative;
@@ -449,30 +451,30 @@ window.addEventListener('resize', () => {
                 rgba(51, 149, 233, 0) 100px);
 
         .item-center-line {
-            width: 260px;
-            height: 260px;
+            width: 240px;
+            height: 240px;
             background-color: transparent;
             border-top: 3px solid rgb(22, 110, 191);
             border-bottom: 3px solid rgb(22, 110, 191);
             border-radius: 50%;
             box-sizing: border-box;
             position: absolute;
-            top: calc(50% - 130px);
-            left: calc(50% - 130px);
+            top: calc(50% - 120px);
+            left: calc(50% - 120px);
             animation: rotate 8s infinite linear;
         }
 
         .item-center-pie {
-            width: 230px;
-            height: 230px;
+            width: 210px;
+            height: 210px;
             background-color: transparent;
             border-top: 2px solid rgb(22, 110, 191);
             border-bottom: 2px solid rgb(22, 110, 191);
             border-radius: 50%;
             box-sizing: border-box;
             position: absolute;
-            top: calc(50% - 115px);
-            left: calc(50% - 115px);
+            top: calc(50% - 105px);
+            left: calc(50% - 105px);
             animation: rotate1 8s infinite linear;
         }
     }

+ 74 - 0
src/views/system/fireAlarm/index.vue

@@ -0,0 +1,74 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <deviceList :resultData="leftData.IlluminatingDevice" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>火灾报警</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <running :resultData="fireDisasterData" style="flex: 1;" />
+                    <switchAll :resultData="leftData.IlluminatingDevice" style="flex: 1;" />
+                    <eventList :resultData="leftData.EventList" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Air">
+import { getIlluminating } from "@/api/system/illuminating"
+import { getEnergy } from "@/api/system/energy"
+import { getAccess } from "@/api/system/passageway"
+import layout from "@/components/layout_/index.vue";
+import deviceList from './modules/deviceList.vue'
+import running from './modules/running.vue'
+import switchAll from './modules/switchAll.vue'
+import eventList from './modules/eventList.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+const MonitorList = ref([])
+const fireDisasterData = ref([])
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getIlluminatingData, 10000);
+    getIlluminatingData()
+    getAccessData()
+    getEnergyData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getIlluminatingData() {
+    getIlluminating().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+function getAccessData() {
+    getAccess().then((res) => {
+        if (res.code == 200) {
+            MonitorList.value = res.data.DeviceList
+        }
+    })
+}
+
+function getEnergyData() {
+    getEnergy().then((res) => {
+        if (res.code == 200) {
+            fireDisasterData.value = res.data.OnedayPowerConsumption
+        }
+    })
+}
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 236 - 0
src/views/system/fireAlarm/modules/deviceList.vue

@@ -0,0 +1,236 @@
+<template>
+    <div class="_deviceList">
+        <HeadlineTag value="设备列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="point_box" style="margin-top: 10px;">
+            <el-input v-model="value" placeholder="按巡查点名称搜索" />
+        </div>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
+                        <el-icon color="#168cdb">
+                            <el-icon>
+                                <Opportunity />
+                            </el-icon>
+                        </el-icon>
+                        {{ item.DeviceName }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated :class="item.SwitchStatus == 0 ? 'blue_title' : 'red_title'"
+                        style="flex: .2;">
+                        {{ item.SwitchStatus == 0 ? '开启' : '关闭' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated
+                        :class="item.OnlinePresence == 0 ? 'blue_title' : 'red_title'" style="flex: .2">
+                        {{ item.OnlinePresence == 0 ? '在线' : '离线' }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import { Opportunity, Aim } from '@element-plus/icons-vue'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+
+const value = ref('')
+
+const headerList = ref(['名称', '状态', '时间'])
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss">
+._deviceList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            padding: 10px;
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>
+
+<style lang="scss" scoped>
+.point_box {
+    margin: 10px 30px;
+}
+
+.point_box :deep(.el-input__wrapper) {
+    background-color: transparent !important;
+    box-shadow: 0 0 0 1px rgb(58, 86, 117) inset !important;
+}
+
+.point_box :deep(.el-input__wrapper.is-focus) {
+    box-shadow: 0 0 0 1px #409EFF inset !important;
+}
+
+.point_box :deep(.el-input__inner) {
+    color: #ffffff !important;
+}
+</style>

+ 228 - 0
src/views/system/fireAlarm/modules/eventList.vue

@@ -0,0 +1,228 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag type="right" value="维护计划列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.DeviceStatus === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <el-text class="w-150px mb-2" truncated style="color: white;">
+                        {{ item.EventDate }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+        }
+
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 211 - 0
src/views/system/fireAlarm/modules/running.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag type="right" value="历史火灾报警趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时气能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#49e645',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(58, 226, 56, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(58, 226, 56, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 234 - 0
src/views/system/fireAlarm/modules/switchAll.vue

@@ -0,0 +1,234 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag type="right" value="火灾报警列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
+                        <el-icon color="#168cdb">
+                            <el-icon>
+                                <Opportunity />
+                            </el-icon>
+                        </el-icon>
+                        {{ item.DeviceName }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated style="flex: .2;color: #fff;">
+                        {{ item.SwitchStatus == 0 ? '烟雾探测器' : '温感探测器' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated
+                        :class="item.OnlinePresence == 0 ? 'blue_title' : 'red_title'" style="flex: .2">
+                        {{ item.OnlinePresence == 0 ? '在线' : '故障' }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+        }
+
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 53 - 0
src/views/system/floodlighting/index.vue

@@ -0,0 +1,53 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <deviceList :resultData="leftData.IlluminatingDevice" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>泛光照明</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <running :resultData="leftData" style="flex: 1;" />
+                    <switchAll :resultData="leftData.RunAnalyse" style="flex: 1;" />
+                    <eventList :resultData="leftData.EventList" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Role">
+import { getIlluminating } from "@/api/system/illuminating"
+import layout from "@/components/layout_/index.vue";
+import deviceList from './modules/deviceList.vue'
+import running from './modules/running.vue'
+import eventList from './modules/eventList.vue'
+import switchAll from './modules/switchAll.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getIlluminatingData, 10000);
+    getIlluminatingData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getIlluminatingData() {
+    getIlluminating().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 236 - 0
src/views/system/floodlighting/modules/deviceList.vue

@@ -0,0 +1,236 @@
+<template>
+    <div class="_deviceList">
+        <HeadlineTag value="设备列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="point_box" style="margin-top: 10px;">
+            <el-input v-model="value" placeholder="按巡查点名称搜索" />
+        </div>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
+                        <el-icon color="#168cdb">
+                            <el-icon>
+                                <Opportunity />
+                            </el-icon>
+                        </el-icon>
+                        {{ item.DeviceName }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated :class="item.SwitchStatus == 0 ? 'blue_title' : 'red_title'"
+                        style="flex: .2;">
+                        {{ item.SwitchStatus == 0 ? '开启' : '关闭' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated
+                        :class="item.OnlinePresence == 0 ? 'blue_title' : 'red_title'" style="flex: .2">
+                        {{ item.OnlinePresence == 0 ? '在线' : '离线' }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import { Opportunity, Aim } from '@element-plus/icons-vue'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+
+const value = ref('')
+
+const headerList = ref(['名称', '状态', '时间'])
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss">
+._deviceList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            padding: 10px;
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>
+
+<style lang="scss" scoped>
+.point_box {
+    margin: 10px 30px;
+}
+
+.point_box :deep(.el-input__wrapper) {
+    background-color: transparent !important;
+    box-shadow: 0 0 0 1px rgb(58, 86, 117) inset !important;
+}
+
+.point_box :deep(.el-input__wrapper.is-focus) {
+    box-shadow: 0 0 0 1px #409EFF inset !important;
+}
+
+.point_box :deep(.el-input__inner) {
+    color: #ffffff !important;
+}
+</style>

+ 245 - 0
src/views/system/floodlighting/modules/eventList.vue

@@ -0,0 +1,245 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag type="right" value="事件列表" style="flex-shrink: 0;"></HeadlineTag>
+        <!-- 绑定鼠标移入移出事件 -->
+        <!-- <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
+            <div :style="{ transform: `translateY(${scrollY}px)` }">
+                <div class="_eventList_mains_item" v-for="(item, index) in eventList.concat(eventList)" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.DeviceStatus === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <el-text class="w-150px mb-2" truncated style="color: white;">
+                        {{ item.EventDate }}
+                    </el-text>
+                </div>
+            </div>
+        </div> -->
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.DeviceStatus === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <el-text class="w-150px mb-2" truncated style="color: white;">
+                        {{ item.EventDate }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+        }
+
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 537 - 0
src/views/system/floodlighting/modules/running.vue

@@ -0,0 +1,537 @@
+<template>
+    <div class="_running">
+        <HeadlineTag type="right" value="运行统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="flex_person">
+                <div ref="chartAccessLeft" style="width: 100%;height: 100%;"></div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import * as echarts from 'echarts'
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartAccessLeft = ref(null);
+let chartLeft = null;
+let chartRight = null;
+const initAccess = () => {
+    let data = [
+        {
+            name: '设备总数',
+            value: 0,
+        },
+        {
+            name: '开启数量',
+            value: 0,
+        },
+        {
+            name: '关闭数量',
+            value: 0,
+        },
+        {
+            name: '故障数量',
+            value: 0,
+        },
+    ];
+    let arrName = getArrayValue(data, 'name');
+    let arrValue = getArrayValue(data, 'value');
+    let sumValue = eval(arrValue.join('+'));
+    let objData = array2obj(data, 'name');
+    let optionData = getData(data);
+    function getArrayValue(array, key) {
+        var key = key || 'value';
+        var res = [];
+        if (array) {
+            array.forEach(function (t) {
+                res.push(t[key]);
+            });
+        }
+        return res;
+    }
+
+    function array2obj(array, key) {
+        var resObj = {};
+        for (var i = 0; i < array.length; i++) {
+            resObj[array[i][key]] = array[i];
+        }
+        return resObj;
+    }
+
+    function getData(data) {
+        var res = {
+            series: [],
+            yAxis: [],
+        };
+        for (let i = 0; i < data.length; i++) {
+            res.series.push({
+                // name: '职员1',
+                type: 'pie',
+                clockwise: true, //顺时加载
+                emphasis: {
+                    scale: false
+                },
+                //鼠标移入变大
+                radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                center: ['30%', '55%'],
+                label: {
+                    show: false,
+                },
+                itemStyle: {
+                    label: {
+                        show: false,
+                    },
+                    labelLine: {
+                        show: false,
+                    },
+                    borderWidth: 5,
+                },
+                data: [
+                    {
+                        value: data[i].value,
+                        name: data[i].name,
+                    },
+                    {
+                        value: sumValue - data[i].value,
+                        name: '',
+                        itemStyle: {
+                            color: 'rgba(0,0,0,0)',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                ],
+            });
+            res.series.push({
+                name: '',
+                type: 'pie',
+                silent: true,
+                z: 1,
+                clockwise: true, //顺时加载
+                emphasis: {
+                    scale: false
+                },
+                //鼠标移入变大
+                radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                center: ['30%', '55%'],
+                label: {
+                    show: false,
+                },
+                itemStyle: {
+                    label: {
+                        show: false,
+                    },
+                    labelLine: {
+                        show: false,
+                    },
+                    borderWidth: 5,
+                },
+                data: [
+                    {
+                        value: 7.5,
+                        itemStyle: {
+                            color: '#E3F0FF',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                    {
+                        value: 2.5,
+                        name: '',
+                        itemStyle: {
+                            color: 'rgba(0,0,0,0)',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                ],
+            });
+            res.yAxis.push(((data[i].value / sumValue) * 100).toFixed(2) + '%');
+        }
+        return res;
+    }
+
+    chartLeft = echarts.init(chartAccessLeft.value);
+    let option = {
+        // backgroundColor: '#fff',
+        legend: {//图例设置
+            show: true,
+            top: '35%',
+            left: '60%',
+            data: arrName,
+            itemWidth: 15,
+            itemHeight: 15,
+            width: 70,
+            padding: [0, 5],
+            itemGap: 20,
+            formatter: function (name) {
+                return '{title|' + name + '}-{value|' + objData[name].value + '}';
+            },
+            textStyle: {
+                rich: {
+                    title: {
+                        fontSize: 12,
+                        lineHeight: 10,
+                        color: '#fff',
+                    },
+                    value: {
+                        fontSize: 12,
+                        lineHeight: 10,
+                        color: '#fff',
+                    },
+                },
+            },
+        },
+        tooltip: {
+            show: true,
+            trigger: 'item',
+            formatter: '{a}<br>{b}:{c}({d}%)',
+            textStyle: {
+                color: '#fafafa',
+            },
+            borderColor: 'transparent',
+            backgroundColor: 'rgba(0, 0, 0, 0.5)',
+            extraCssText: 'backdrop-filter: blur(6px);',
+        },
+        color: ['#409eff', '#15acaa', '#FFC107', 'rgb(244, 67, 54)'],
+        grid: {
+            top: '20%',
+            bottom: '48%',
+            left: '20%',
+            containLabel: false,
+        },
+        yAxis: [
+            {
+                type: 'category',
+                inverse: true,
+                axisLine: {
+                    show: false,
+                },
+                axisTick: {
+                    show: false,
+                },
+                axisLabel: {
+                    interval: 0,
+                    inside: true,
+                    textStyle: {
+                        color: '#fff',
+                        fontSize: 10,
+                    },
+                    show: true,
+                },
+                data: optionData.yAxis,
+            },
+        ],
+        xAxis: [
+            {
+                show: true,
+            },
+        ],
+        series: optionData.series,
+    };
+    chartLeft.setOption(option)
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (chartLeft) {
+        let data = [
+            {
+                name: '设备总数',
+                value: newVal.DeviceCount,
+            },
+            {
+                name: '开启数量',
+                value: newVal.OpenCount,
+            },
+            {
+                name: '关闭数量',
+                value: newVal.OffCount,
+            },
+            {
+                name: '故障数量',
+                value: newVal.FaultCount,
+            },
+        ];
+        let arrName = getArrayValue(data, 'name');
+        let arrValue = getArrayValue(data, 'value');
+        let sumValue = eval(arrValue.join('+'));
+        let objData = array2obj(data, 'name');
+        let optionData = getData(data);
+        function getArrayValue(array, key) {
+            var key = key || 'value';
+            var res = [];
+            if (array) {
+                array.forEach(function (t) {
+                    res.push(t[key]);
+                });
+            }
+            return res;
+        }
+
+        function array2obj(array, key) {
+            var resObj = {};
+            for (var i = 0; i < array.length; i++) {
+                resObj[array[i][key]] = array[i];
+            }
+            return resObj;
+        }
+
+        function getData(data) {
+            var res = {
+                series: [],
+                yAxis: [],
+            };
+            for (let i = 0; i < data.length; i++) {
+                res.series.push({
+                    // name: '职员1',
+                    type: 'pie',
+                    clockwise: true, //顺时加载
+                    emphasis: {
+                        scale: false
+                    },
+                    //鼠标移入变大
+                    radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                    center: ['30%', '55%'],
+                    label: {
+                        show: false,
+                    },
+                    itemStyle: {
+                        label: {
+                            show: false,
+                        },
+                        labelLine: {
+                            show: false,
+                        },
+                        borderWidth: 5,
+                    },
+                    data: [
+                        {
+                            value: data[i].value,
+                            name: data[i].name,
+                        },
+                        {
+                            value: sumValue - data[i].value,
+                            name: '',
+                            itemStyle: {
+                                color: 'rgba(0,0,0,0)',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                    ],
+                });
+                res.series.push({
+                    name: '',
+                    type: 'pie',
+                    silent: true,
+                    z: 1,
+                    clockwise: true, //顺时加载
+                    emphasis: {
+                        scale: false
+                    },
+                    //鼠标移入变大
+                    radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                    center: ['30%', '55%'],
+                    label: {
+                        show: false,
+                    },
+                    itemStyle: {
+                        label: {
+                            show: false,
+                        },
+                        labelLine: {
+                            show: false,
+                        },
+                        borderWidth: 5,
+                    },
+                    data: [
+                        {
+                            value: 7.5,
+                            itemStyle: {
+                                color: '#E3F0FF',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                        {
+                            value: 2.5,
+                            name: '',
+                            itemStyle: {
+                                color: 'rgba(0,0,0,0)',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                    ],
+                });
+                res.yAxis.push(((data[i].value / sumValue) * 100).toFixed(2) + '%');
+            }
+            return res;
+        }
+        chartLeft.setOption({
+            legend: {
+                data: arrName,
+                formatter: function (name) {
+                    return '{title|' + name + '}-{value|' + objData[name].value + '}';
+                },
+            },
+            xAxis: [
+                {
+                    show: true,
+                },
+            ],
+            yAxis: [{
+                type: 'category',
+                data: optionData.yAxis,
+            }],
+            series: optionData.series,
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+// 生命周期
+onMounted(() => {
+    initAccess()
+});
+// 窗口自适应
+window.addEventListener('resize', () => {
+    chartLeft?.resize();
+    chartRight?.resize();
+});
+</script>
+<style lang="scss" scoped>
+._running {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        flex: 1;
+        display: flex;
+
+        &_left {
+            width: 150px;
+            height: 150px;
+            position: relative;
+            flex-shrink: 0;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
+        }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+    }
+}
+
+.flex_person {
+    flex: 1;
+    width: 100%;
+    height: 100%;
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
+    }
+}
+</style>

+ 281 - 0
src/views/system/floodlighting/modules/switchAll.vue

@@ -0,0 +1,281 @@
+<template>
+    <div class="_switchAll">
+        <HeadlineTag type="right" value="运行分析(周)"></HeadlineTag>
+        <div class="_switchAll_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+ const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        const option = {
+            title: null,
+            tooltip: {
+                trigger: 'axis',
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            legend: false,
+            // 调整 grid 配置以在 Y 轴方向拉伸图表
+            grid: {
+                top: '10%', // 减小顶部边距,让图表向上扩展
+                bottom: '10%', // 减小底部边距,让图表向下扩展
+                right: '0%',
+            },
+            xAxis: {
+                type: 'category',
+                data: [],
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true, // 不显示刻度标签
+                    color: '#fff'
+                },
+                // 配置 Y 轴网格刻线
+                splitLine: {
+                    show: false, // 显示网格刻线
+                    lineStyle: {
+                        color: '#44585e' // 设置网格刻线颜色 
+                    }
+                },
+                axisTick: {
+                    show: false // 不显示刻度线
+                },
+            },
+            series: [
+                {
+                    name: '开启数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(49, 143, 247)',
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(49, 143, 247, 0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(49, 143, 247, 0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                },
+                {
+                    name: '关闭数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(9, 60),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(255,193,7)',
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(255,193,7, 0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(255,193,7,0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                },
+                {
+                    name: '故障数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(9, 60),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(244,67,54)'
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(244,67,54,0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(244,67,54,0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        let arrData = Object.keys(newVal)
+        let arr1 = []
+        let arr2 = []
+        let arr3 = []
+        arrData.forEach((item) => {
+            let data = Object.entries(newVal[item])
+            arr1.push(data[0][1])
+            arr2.push(data[1][1])
+            arr3.push(data[2][1])
+        })
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                name: '开启数量',
+                data: arr1,
+            }, {
+                name: '关闭数量',
+                data: arr2,
+            }, {
+                name: '故障数量',
+                data: arr3,
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._switchAll {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 0 - 25
src/views/system/lighting/modules/deviceList.vue

@@ -26,31 +26,6 @@
                 </div>
             </div>
         </div>
-        <!-- <div class="_deviceList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
-            <div :style="{ transform: `translateY(${scrollY}px)` }">
-                <div class="_deviceList_mains_item" v-for="(item, index) in eventList" :key="index">
-                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
-                        <el-icon color="#168cdb">
-                            <el-icon>
-                                <Opportunity />
-                            </el-icon>
-                        </el-icon>
-                        {{ item.DeviceName }}
-                    </el-text>
-                    <el-text class="w-150px mb-2" truncated style="color: white;flex: .2;">
-                        {{ item.state == 0 ? '开启' : '关闭' }}
-                    </el-text>
-                    <el-text class="w-150px mb-2" truncated style="color: white;flex: .2">
-                        {{ item.OnlinePresence == 0 ? '在线' : '离线' }}
-                    </el-text>
-                    <el-icon color="#168cdb" style="flex: .2;">
-                        <el-icon>
-                            <Aim />
-                        </el-icon>
-                    </el-icon>
-                </div>
-            </div>
-        </div> -->
     </div>
 </template>
 

+ 32 - 34
src/views/system/message/index.vue

@@ -1,34 +1,33 @@
 <template>
     <div class="_energy">
-     <layout>
-         <template #left>
-            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
-                <running :resultData="leftData" style="flex: 1;"/>
-                <carbonEmission :resultData="leftData.Information" style="flex:1;"/>
-             </div>
-         </template>
-         <template #content>
-             <!-- <p>信息</p> -->
-         </template>
-         <template #right>
-            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
-                <consume  :resultData="leftData" style="flex:1;"/>
-                <tiring :resultData="leftData.Readings" style="flex: 1;"/>
-                <sameDay :resultData="leftData.Large" style="flex: 1;"/>
-             </div>
-         </template>
-     </layout>
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <running :resultData="leftData" style="height: 35%;" />
+                    <carbonEmission :resultData="leftData.Information" style="flex:1;" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>信息</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <tiring :resultData="leftData" style="height: 35%;" />
+                    <sameDay :resultData="leftData.Large" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
     </div>
- </template>
- 
- <script setup name="Role">
+</template>
+
+<script setup name="Role">
 import { getInformation } from "@/api/system/information"
- import layout from "@/components/layout_/index.vue";
- import running from './modules/running.vue'
- import consume from './modules/consume.vue'
- import carbonEmission from "./modules/carbonEmission.vue";
- import sameDay from './modules/sameDay.vue'
- import tiring from './modules/tiring.vue'
+import layout from "@/components/layout_/index.vue";
+import running from './modules/running.vue'
+import consume from './modules/consume.vue'
+import carbonEmission from "./modules/carbonEmission.vue";
+import sameDay from './modules/sameDay.vue'
+import tiring from './modules/tiring.vue'
 
 const intervalId = ref(null)
 const leftData = ref({})
@@ -47,10 +46,9 @@ function getInformationData() {
         }
     })
 }
- </script> 
- <style lang="scss">
- ._energy{
-     height: 100%;
- }
- </style>
- 
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 208 - 166
src/views/system/message/modules/carbonEmission.vue

@@ -1,213 +1,255 @@
 <template>
-    <div class="_consume">
-        <HeadlineTag value="今日信息类型占比"></HeadlineTag>
-        <div class="_consume_mains">
-            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+    <div class="_eventList">
+        <HeadlineTag value="信息发布记录" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item">
+                        <div class="_eventList_mains_item_text_flag">
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: #fff;">
+                            {{ index + 1 }}F#{{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <div class="_xian" :class="item.UseState == 0 ? '_warning' : '_success'">{{ item.UseState == 0 ?
+                        '发布失败' : '发布成功' }}</div>
+                    <div class="view_item" style="text-align: right;">{{ item.Date }}</div>
+                </div>
+            </div>
         </div>
     </div>
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
-import * as echarts from 'echarts';
-import HeadlineTag from '@/components/HeadlineTag';
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
         type: Array,
         default: []
     }
 })
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
 
-const chartRef = ref(null);
-let chart = null;
-const generateRandomData = (length, max) => {
-    const randomData = [];
-    for (let i = 0; i < length; i++) {
-        randomData.push(Math.floor(Math.random() * max));
-    }
-    return randomData;
-};
-const handleResize = () => {
-    if (chart) {
-        chart.resize();
-    }
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
 };
-onMounted(() => {
-    if (chartRef.value) {
-        chart = echarts.init(chartRef.value);
-        // 信息类型
-        // const infoTypes = ['紧急通知', '政策法规', '活动预告', '其他'];
-        // const randomData = generateRandomData(4, 100);
-        // const data = infoTypes.map((type, index) => ({
-        //     name: type,
-        //     value: randomData[index]
-        // }));
-        const option = {
-            // 添加动画效果
-            animation: true,
-            animationDuration: 1000,
-            animationEasing: 'cubicOut',
-            tooltip: {
-                trigger: 'item',
-                formatter: '{a} <br/>{b} : {c} ({d}%)',
-                textStyle: {
-                    color: '#fafafa',
-                },
-                borderColor: 'transparent',
-                backgroundColor: 'rgba(0, 0, 0, 0.5)',
-                extraCssText: 'backdrop-filter: blur(6px);',
-            },
-            // 添加图例配置
-            legend: {
-                orient: 'horizontal', // 图例水平排列
-                bottom: '0%', // 增大距离底部的距离,可根据需求调整
-                left: 'center', // 水平居中
-                // data: infoTypes,
-                data: [],
-                textStyle: {
-                    color: '#fff'
-                }
-            },
-            series: [
-                {
-                    name: '信息类型',
-                    type: 'pie',
-                    // data: data,
-                    data: [],
-                    radius: ['40%', '70%'],
-                    roseType: 'radius',
-                    stillShowZeroSum: false,
-                    itemStyle: {
-                        color: (params) => {
-                            const colorList = ['#4a90e2', '#50e3c2', '#f5a623', '#e35050'];
-                            return colorList[params.dataIndex];
-                        },
-                        // 设置圆角
-                        borderRadius: 10
-                    },
-                    emphasis: {
-                        itemStyle: {
-                            // 鼠标悬停时加大圆角
-                            borderRadius: 12
-                        }
-                    },
-                    label: {
-                        show: true,
-                        formatter: '{b}: {d}%',
-                        textStyle: {
-                            color: '#fff',
-                            fontSize: 12
-                        },
-                        distance: 20
-                    },
-                    labelLine: {
-                        show: true,
-                        length: 10,
-                        length2: 20,
-                        lineStyle: {
-                            color: '#fff'
-                        }
-                    }
-                }
-            ]
-        };
-        chart.setOption(option);
-    }
-    window.addEventListener('resize', handleResize);
-});
+
 watch(() => props.resultData, (newVal) => {
-    if (chart) {
-        let data = []
-        let legendData = []
-        let arrData = newVal
-        arrData.forEach((item) => {
-            // console.log(item, 776)
-            let arrObj = {
-                name: item.InformationType,
-                value: item.UseState,
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
             }
-            data.push(arrObj)
-            legendData.push(item.InformationType)
-        })
-        chart.setOption({
-            legend: {
-                data: legendData,
-            },
-            series: [{
-                name: '信息类型',
-                data: data,
-            }],
-        })
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-onUnmounted(() => {
-    window.removeEventListener('resize', handleResize);
-    if (chart) {
-        chart.dispose();
-    }
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
-<style lang="scss">
-._divider {
-    height: 1px;
-    border: 1px dashed #168cdb;
-    flex: 1;
-    margin: 0 10px;
+<style lang="scss" scoped>
+._success {
+    color: #15acaa !important;
+}
+
+._warning {
+    color: #FFC107 !important;
 }
 
-._consume {
+._eventList {
+    overflow: hidden;
     display: flex;
     flex-direction: column;
 
     &_mains {
         margin: 10px 30px;
-        flex: 1;
-        display: flex;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
 
         &_item {
             display: flex;
             justify-content: space-between;
             align-items: center;
-            padding: 10px;
-
-            &_name {
-                width: 30px;
-                height: 30px;
-                background: url("@/assets/images/content_circle_num.png");
-                background-size: 100% 100%;
-                background-position: center;
-                background-repeat: no-repeat;
-                // animation: scanning 4s linear infinite;
-                border: 1px solid red;
-            }
+            padding: 10px 0;
 
-            &_flag {
+            &_text {
+                width: 50%;
                 display: flex;
+                justify-content: flex-start;
                 align-items: center;
-                gap: 3px;
-
-                &_item {
-                    width: 30px;
-                    height: 30px;
-                    border: 3px dashed #168cdb;
-                    box-sizing: border-box;
-                    background: #0e6ead;
-                    color: #fff;
+                max-width: 50%;
+                text-emphasis: none;
+                /* 新增样式让文本超出显示省略号 */
+                white-space: nowrap;
+                overflow: hidden;
+                text-overflow: ellipsis;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
                     border-radius: 50%;
+                    margin-left: 10px;
                     display: flex;
                     align-items: center;
                     justify-content: center;
-                    font-size: 14px;
+                    margin-right: 10px;
+                    background: #168cdb;
                 }
-            }
-        }
 
-        &_item:hover {
-            cursor: pointer;
-            background-image: linear-gradient(to right, #168cdb, transparent);
+                &_p {
+                    margin-left: 10px;
+                }
+            }
         }
     }
 }
+
+._xian {
+    width: fit-content;
+    width: -webkit-fit-content;
+    width: -moz-fit-content;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 12px;
+    width: 100px;
+}
+
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    color: #ffffff;
+}
+
+// .view_item:first-child {
+//     flex: 2;
+// }
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 0px !important;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
 </style>

+ 189 - 47
src/views/system/message/modules/consume.vue

@@ -1,34 +1,15 @@
 <template>
     <div class="_runnings">
-        <HeadlineTag type="right" value="信息类型占比"></HeadlineTag>
+        <HeadlineTag type="right" value="业务完成进度"></HeadlineTag>
         <div class="_runnings_mains" :style="{ '--heightSume': heightcon + 'px' }">
-            <div class="_runnings_mains_left" id="sumeWidth">
-                <div class="_runnings_mains_left_tuan tuan1"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.UrgentNotice}}</div>
-                    <div class="_runnings_mains_left_conter_text">紧急通知</div>
-                </div>
-            </div>
-            <div class="_runnings_mains_left">
-                <div class="_runnings_mains_left_tuan tuan2"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.Policy}}</div>
-                    <div class="_runnings_mains_left_conter_text">政策法规</div>
-                </div>
-            </div>
-            <div class="_runnings_mains_left">
-                <div class="_runnings_mains_left_tuan tuan3"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.Activity}}</div>
-                    <div class="_runnings_mains_left_conter_text">活动预告</div>
-                </div>
-            </div>
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
         </div>
     </div>
 </template>
 
 <script setup>
 import { ref } from "vue";
+import * as echarts from 'echarts';
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
@@ -36,35 +17,196 @@ const props = defineProps({
         default: {}
     }
 })
-const runningList = ref([
-    { name: '入侵检测', state: '154', color: '#409eff', tip: '划定区域人员闯入' },
-    { name: '异常行为', state: '15', color: '#15acaa', tip: '徘徊、倒地、聚集' },
-    { name: '丢失告警', state: '134', color: '#FFC107', tip: '物品遗留/丢失告警' },
-])
-const heightcon = ref(0)
-// 生命周期
+
+const chartRef = ref(null);
+let chart = null;
+const datas = [
+    { name: "工学院", value: "32", value1: "258", value2: "82" },
+    { name: "艺术学院", value: "37", value1: "265", value2: "98" },
+    { name: "教育学院", value: "94", value1: "582", value2: "550" },
+    { name: "法学院", value: "73", value1: "324", value2: "235" },
+    { name: "文学院", value: "37", value1: "321", value2: "120" },
+    { name: "统计学院", value: "74", value1: "350", value2: "260" },
+    { name: "信息学院", value: "69", value1: "429", value2: "294" },
+    { name: "经济学院", value: "65", value1: "545", value2: "354" }
+];
+const xData = datas.map(item => item.value1);
+const yData = datas.map(item => item.name);
+const zData = datas.map(item => item.value2);
+const pData = datas.map(item => item.value);
+//const max = Math.ceil(Math.max(...yData) * 1.2);
+const max = 100;
+const maxData = [max, max, max, max, max, max, max, max, max, max];
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
 onMounted(() => {
-    var element = document.getElementById("sumeWidth");
-    var width = element.offsetWidth;
-    heightcon.value = width
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        const option = {
+            grid: {
+                left: "10%",
+                right: "30%",
+                bottom: "3%",
+                top: "3%",
+                containLabel: false
+            },
+            xAxis: [
+                {
+                    show: false,
+                },
+                {
+                    show: false,
+                    splitLine: {
+                        show: false
+                    }
+                }
+            ],
+            yAxis: [
+                {
+                    show: true,
+                    data: yData,
+                    offset: 40,
+                    position: "right",
+                    axisLabel: {
+                        lineHeight: 0,
+                        verticalAlign: "bottom",
+                        fontSize: 14,
+                        color: "#ffffff",
+                        formatter: "{value}"
+                    },
+                    axisLine: {
+                        show: false
+                    },
+                    splitLine: {
+                        show: false
+                    },
+                    axisTick: {
+                        show: false
+                    }
+                },
+                {
+                    show: true,
+                    data: zData,
+                    offset: 5,
+                    position: "right",
+                    axisLabel: {
+                        lineHeight: 0,
+                        verticalAlign: "bottom",
+                        fontSize: 14,
+                        color: "#8CEA00",
+                        formatter: "{value}"
+                    },
+                    axisLine: {
+                        show: false
+                    },
+                    splitLine: {
+                        show: false
+                    },
+                    axisTick: {
+                        show: false
+                    }
+                }
+            ],
+            series: [{
+                name: "进度",
+                show: true,
+                type: "bar",
+                barGap: "-100%",
+                xAxisIndex: 1,
+                barWidth: 20,
+                itemStyle: {
+                    borderRadius: 4,
+                    color: {
+                        type: 'linear',
+                        x: 0,
+                        y: 0,
+                        x2: 1,
+                        y2: 0,
+                        colorStops: [
+                            {
+                                offset: 0,
+                                color: '#148BCE', // 0% 处的颜色
+                            },
+                            {
+                                offset: 1,
+                                color: '#73E3FF', // 0% 处的颜色
+                            },
+                        ],
+                        global: false, // 缺省为 false
+                    }
+                },
+                label: {
+                    show: true,
+                    position: 'insideRight',
+                    formatter: '{c}%',
+                    offset: [-10, 2],
+                    color: '#fff'
+                },
+                labelLine: {
+                    show: false
+                },
+                z: 2,
+                data: pData,
+                animationDelay: 1000,
+                animationDuration: 1000
+            }, {
+                name: "百分比",
+                z: 1,
+                show: true,
+                type: "bar",
+                xAxisIndex: 1,
+                barGap: "-100%",
+                barWidth: 20,
+                itemStyle: {
+                    borderRadius: 4,
+                    color: "rgba(13, 55, 78, 1)"
+                },
+                label: {
+                    show: true,
+                    verticalAlign: "middle",
+                    position: "left",
+                    fontSize: 14,
+                    color: "#fff",
+                    formatter: function (data) {
+                        return xData[data.dataIndex];
+                    }
+                },
+                data: maxData
+            }
+            ]
+        };
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        let dataArr = []
+        let arr = Object.entries(newVal)
+        arr.forEach(item => {
+            dataArr.push(item[1])
+        })
+        // chart.setOption({
+        //     series: [{
+        //         name: '阅读量',
+        //         data: dataArr,
+        //     }],
+        // })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
 });
 </script>
 <style lang="scss" scoped>
-.tuan1 {
-    background: url("@/assets/images/video_bg_1.png");
-    animation: scanning 10s linear infinite;
-}
-
-.tuan2 {
-    background: url("@/assets/images/video_bg_2.png");
-    animation: scanning 10s linear infinite;
-}
-
-.tuan3 {
-    background: url("@/assets/images/video_bg_3.png");
-    animation: scanning 10s linear infinite;
-}
-
 ._runnings {
     display: flex;
     align-items: center;

+ 2 - 2
src/views/system/message/modules/running.vue

@@ -1,11 +1,11 @@
 <template>
     <div class="_running">
-        <HeadlineTag value="实时设备统计"></HeadlineTag>
+        <HeadlineTag value="信息发布设备统计"></HeadlineTag>
         <div class="_running_mains">
             <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
                 <div class="_running_mains_left_tuan"></div>
                 <div class="_running_mains_left_conter">
-                    <div class="_running_mains_left_conter_num">{{getTotal()}}</div>
+                    <div class="_running_mains_left_conter_num">{{getTotal() || 0}}</div>
                     <div class="_running_mains_left_conter_text">大屏总量</div>
                 </div>
             </div>

+ 1 - 1
src/views/system/message/modules/sameDay.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="_eventList">
-        <HeadlineTag type="right" value="大屏控制" style="flex-shrink: 0;"></HeadlineTag>
+        <HeadlineTag type="right" value="信息发布设备控制" style="flex-shrink: 0;"></HeadlineTag>
         <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
             <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
                 <div class="item" v-for="(item, index) in data" :key="index">

+ 91 - 183
src/views/system/message/modules/tiring.vue

@@ -1,208 +1,116 @@
 <template>
-    <div class="_consume">
-        <HeadlineTag type="right" value="阅读量"></HeadlineTag>
-        <div class="_consume_mains">
-            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+
+    <div class="flex_home">
+        <HeadlineTag type="right" value="停车系统"></HeadlineTag>
+        <div class="box_arch">
+            <div class="box_vehicle">
+                <div class="tect_arch_tc">
+                    <svg class="arch_icon_tc" aria-hidden="true">
+                        <defs>
+                            <linearGradient id="myleisure" x1="0%" y1="100%" x2="0%" y2="0%">
+                                <stop offset="0%" stop-color="rgb(21, 213, 21)" />
+                                <stop offset="100%" stop-color="rgb(255,255,255)" />
+                            </linearGradient>
+                        </defs>
+                        <use xlink:href="#icon-leisure" fill="url('#myleisure')" />
+                    </svg>
+                </div>
+                <div style="display: flex;align-items: center;">
+                    <div class="color-green-line"></div>
+                    <span>在线率</span>
+                    <div class="sum_cle" style="color: rgb(21, 213, 21);">{{ resultData.online || 0 }}%</div>
+                </div>
+            </div>
+            <div class="box_vehicle">
+                <div class="tect_arch_tc">
+                    <svg class="arch_icon_tc" aria-hidden="true">
+                        <defs>
+                            <linearGradient id="myforbid" x1="0%" y1="100%" x2="0%" y2="0%">
+                                <stop offset="0%" stop-color="rgb(215, 113, 23)" />
+                                <stop offset="100%" stop-color="rgb(255,255,255)" />
+                            </linearGradient>
+                        </defs>
+                        <use xlink:href="#icon-forbid" fill="url('#myforbid')" />
+                    </svg>
+                </div>
+                <div style="display: flex;align-items: center;">
+                    <div class="color-orange-line"></div>
+                    <span>离线率</span>
+                    <div class="sum_cle" style="color: rgb(215, 113, 23);">{{ resultData.unline || 0 }}%
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
-import * as echarts from 'echarts';
-import HeadlineTag from '@/components/HeadlineTag';
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
         type: Object,
         default: {}
     }
 })
-
-const chartRef = ref(null);
-let chart = null;
-// 模拟不同年龄段的阅读量数据
-const ageGroupReadData = [30, 50, 70, 40, 20];
-const ageGroups = ['30岁以下', '30 - 40岁', '40 - 50岁', '50 - 60岁', '60岁以上'];
-const handleResize = () => {
-    if (chart) {
-        chart.resize();
-    }
-};
-onMounted(() => {
-    if (chartRef.value) {
-        chart = echarts.init(chartRef.value);
-        const option = {
-            xAxis: {
-                type: 'category',
-                data: ageGroups,
-                axisLabel: {
-                    color: '#fff'
-                },
-            },
-            tooltip: {
-                trigger: 'axis',
-                axisPointer: {
-                    type: 'cross',
-                    crossStyle: {
-                        color: '#999'
-                    }
-                },
-                textStyle: {
-                    color: '#fafafa',
-                },
-                borderColor: 'transparent',
-                backgroundColor: 'rgba(0, 0, 0, 0.5)',
-                extraCssText: 'backdrop-filter: blur(6px);',
-            },
-            grid: {
-                top: '10%',
-                bottom: '10%',
-                right: '0%',
-            },
-            yAxis: {
-                type: 'value',
-                axisLabel: {
-                    show: true,
-                    color: '#fff'
-                },
-                splitLine: {
-                    show: false,
-                    lineStyle: {
-                        color: '#44585e'
-                    }
-                },
-                axisTick: {
-                    show: false
-                },
-            },
-            series: [
-                {
-                    name: '阅读量',
-                    type: 'bar',
-                    // data: ageGroupReadData,
-                    data: [],
-                    // 设置柱子宽度
-                    barWidth: '40%',
-                    itemStyle: {
-                        // 设置柱子圆角,仅上下圆角
-                        borderRadius: [10, 10, 10, 10],
-                        // 设置渐变颜色
-                        color: {
-                            type: 'linear',
-                            x: 0,
-                            y: 0,
-                            x2: 0,
-                            y2: 1,
-                            colorStops: [
-                                {
-                                    offset: 0,
-                                    color: '#446bf5' // 渐变起始颜色
-                                },
-                                {
-                                    offset: 1,
-                                    color: '#2ca3e2' // 渐变结束颜色
-                                }
-                            ],
-                            global: false
-                        }
-                    },
-                    // 添加柱状图背景样式
-                    barBackgroundStyle: {
-                        color: 'rgba(255, 255, 255, 0.1)', // 背景颜色,这里设置为半透明白色
-                        borderRadius: [10, 10, 0, 0] // 背景圆角和柱子保持一致
-                    }
-                }
-            ]
-        };
-
-        chart.setOption(option);
-    }
-    window.addEventListener('resize', handleResize);
-});
-
-watch(() => props.resultData, (newVal) => {
-    if (chart) {
-        let dataArr = []
-        let arr = Object.entries(newVal)
-        arr.forEach(item=>{
-            dataArr.push(item[1])
-        })
-        chart.setOption({
-            series: [{
-                name: '阅读量',
-                data: dataArr,
-            }],
-        })
-    }
-}, { deep: true, immediate: true } // 开启深度监听
-)
-onUnmounted(() => {
-    window.removeEventListener('resize', handleResize);
-    if (chart) {
-        chart.dispose();
-    }
-});
 </script>
-
 <style lang="scss">
-._divider {
-    height: 1px;
-    border: 1px dashed #168cdb;
-    flex: 1;
-    margin: 0 10px;
+.flex_home {
+    color: #fff;
 }
 
-._consume {
+.box_arch {
     display: flex;
-    flex-direction: column;
+    align-items: center;
+    height: calc(100% - 40px);
+}
 
-    &_mains {
-        margin: 10px 30px;
-        flex: 1;
-        display: flex;
+.tect_arch_tc {
+    width: 40%;
+    max-height: 100px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.arch_icon_tc {
+    width: 70%;
+    height: auto;
+    margin-bottom: 20px;
+}
 
-        &_item {
-            display: flex;
-            justify-content: space-between;
-            align-items: center;
-            padding: 10px;
+.arch_icon {
+    width: 70%;
+    height: 70%;
+    margin-bottom: 20px;
+}
 
-            &_name {
-                width: 30px;
-                height: 30px;
-                background: url("@/assets/images/content_circle_num.png");
-                background-size: 100% 100%;
-                background-position: center;
-                background-repeat: no-repeat;
-                // animation: scanning 4s linear infinite;
-                border: 1px solid red;
-            }
+.box_vehicle {
+    flex: 1;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-direction: column;
+}
 
-            &_flag {
-                display: flex;
-                align-items: center;
-                gap: 3px;
+.color-green-line {
+    width: 6px;
+    height: 6px;
+    margin-right: 10px;
+    background-color: rgb(21, 213, 21);
+}
 
-                &_item {
-                    width: 30px;
-                    height: 30px;
-                    border: 3px dashed #168cdb;
-                    box-sizing: border-box;
-                    background: #0e6ead;
-                    color: #fff;
-                    border-radius: 50%;
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                    font-size: 14px;
-                }
-            }
-        }
+.color-orange-line {
+    width: 6px;
+    height: 6px;
+    margin-right: 10px;
+    background-color: rgb(215, 113, 23);
+}
 
-        &_item:hover {
-            cursor: pointer;
-            background-image: linear-gradient(to right, #168cdb, transparent);
-        }
-    }
+.sum_cle {
+    font-size: 28px;
+    font-weight: bold;
+    padding-left: 10px;
 }
 </style>

+ 4 - 4
src/views/system/passageway/Personnel.vue

@@ -171,14 +171,14 @@ watch(() => props.resultData, (newVal) => {
     if (newVal.Out) {
         let valueOut = {
             value: newVal.Out,
-            text: "人员(出)"
+            text: "出入口(出)"
         }
         initAccess(valueOut, 'out')
     }
     if (newVal.Into) {
         let valueInto = {
             value: newVal.Into,
-            text: "人员(入)"
+            text: "出入口(入)"
         }
         initAccess(valueInto, 'into')
     }
@@ -190,8 +190,8 @@ onMounted(() => {
         value: [],
         text: ""
     }
-    initAccess(value,'out')
-    initAccess(value,'into')
+    initAccess(value, 'out')
+    initAccess(value, 'into')
 });
 // 窗口自适应
 window.addEventListener('resize', () => {

+ 20 - 11
src/views/system/passageway/access.vue

@@ -4,16 +4,20 @@
             <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
                 <div class="item" v-for="(item, index) in data" :key="index">
                     <div class="_eventList_mains_item_text view_item">
-                        <div :class="item.State === 0 ? '_warning' : '_success'"
+                        <div :class="item.State === 0 ? '_success' : '_warning'"
                             class="_eventList_mains_item_text_flag">
+                            {{ item.State === 0 ? '进' : '出' }}
                         </div>
                         <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
-                            {{ item.Name }}
+                            {{ item.Car }}
                         </el-text>
                     </div>
-                    <el-text class="w-150px mb-2 view_item" style="justify-content: center;"
+                    <el-text class="w-150px mb-2" style="justify-content: center;width: 50px;"
                         :class="item.State == 0 ? 'blue_title' : 'green_title'">
-                        {{ item.State == 0 ? '出' : '入' }}
+                        {{ item.State == 0 ? '进入' : '离开' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2 view_item" truncated style="color: white;">
+                        {{ item.Name }}
                     </el-text>
                     <el-text class="w-150px mb-2 view_item" truncated style="color: white;justify-content: flex-end;">
                         {{ item.Date }}
@@ -130,14 +134,19 @@ const onMouseleave = () => {
 
             &_text {
                 display: flex;
-                justify-content: space-between;
                 align-items: center;
+                justify-content: flex-start;
 
                 &_flag {
-                    width: 10px;
-                    height: 10px;
+                    width: 30px;
+                    height: 30px;
                     border-radius: 50%;
-                    margin-left: 10px;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #fff;
+                    font-size: 14px;
+                    flex: none;
                 }
 
                 &_p {
@@ -164,9 +173,9 @@ const onMouseleave = () => {
 
 .view_item {
     flex: 1;
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
     white-space: nowrap;
     text-overflow: ellipsis;
     overflow: hidden;

+ 1 - 1
src/views/system/passageway/index.vue

@@ -76,7 +76,7 @@
             </div>
           </div>
           <div class="flex_spection">
-            <HeadlineTag type="right" value="出入人员统计"></HeadlineTag>
+            <HeadlineTag type="right" value="出入口进出统计"></HeadlineTag>
             <div class="box_arch">
               <Personnel :resultData="leftData"></Personnel>
             </div>

+ 53 - 0
src/views/system/sceneLighting/index.vue

@@ -0,0 +1,53 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <deviceList :resultData="leftData.IlluminatingDevice" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>景观照明</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <running :resultData="leftData" style="flex: 1;" />
+                    <switchAll :resultData="leftData.RunAnalyse" style="flex: 1;" />
+                    <eventList :resultData="leftData.EventList" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Role">
+import { getIlluminating } from "@/api/system/illuminating"
+import layout from "@/components/layout_/index.vue";
+import deviceList from './modules/deviceList.vue'
+import running from './modules/running.vue'
+import eventList from './modules/eventList.vue'
+import switchAll from './modules/switchAll.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getIlluminatingData, 10000);
+    getIlluminatingData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getIlluminatingData() {
+    getIlluminating().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 236 - 0
src/views/system/sceneLighting/modules/deviceList.vue

@@ -0,0 +1,236 @@
+<template>
+    <div class="_deviceList">
+        <HeadlineTag value="设备列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="point_box" style="margin-top: 10px;">
+            <el-input v-model="value" placeholder="按巡查点名称搜索" />
+        </div>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <el-text class="w-150px mb-2 _table_row1" truncated style="color: white;flex: .4;">
+                        <el-icon color="#168cdb">
+                            <el-icon>
+                                <Opportunity />
+                            </el-icon>
+                        </el-icon>
+                        {{ item.DeviceName }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated :class="item.SwitchStatus == 0 ? 'blue_title' : 'red_title'"
+                        style="flex: .2;">
+                        {{ item.SwitchStatus == 0 ? '开启' : '关闭' }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" truncated
+                        :class="item.OnlinePresence == 0 ? 'blue_title' : 'red_title'" style="flex: .2">
+                        {{ item.OnlinePresence == 0 ? '在线' : '离线' }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import { Opportunity, Aim } from '@element-plus/icons-vue'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+
+const value = ref('')
+
+const headerList = ref(['名称', '状态', '时间'])
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss">
+._deviceList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            padding: 10px;
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>
+
+<style lang="scss" scoped>
+.point_box {
+    margin: 10px 30px;
+}
+
+.point_box :deep(.el-input__wrapper) {
+    background-color: transparent !important;
+    box-shadow: 0 0 0 1px rgb(58, 86, 117) inset !important;
+}
+
+.point_box :deep(.el-input__wrapper.is-focus) {
+    box-shadow: 0 0 0 1px #409EFF inset !important;
+}
+
+.point_box :deep(.el-input__inner) {
+    color: #ffffff !important;
+}
+</style>

+ 245 - 0
src/views/system/sceneLighting/modules/eventList.vue

@@ -0,0 +1,245 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag type="right" value="事件列表" style="flex-shrink: 0;"></HeadlineTag>
+        <!-- 绑定鼠标移入移出事件 -->
+        <!-- <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
+            <div :style="{ transform: `translateY(${scrollY}px)` }">
+                <div class="_eventList_mains_item" v-for="(item, index) in eventList.concat(eventList)" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.DeviceStatus === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <el-text class="w-150px mb-2" truncated style="color: white;">
+                        {{ item.EventDate }}
+                    </el-text>
+                </div>
+            </div>
+        </div> -->
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.DeviceStatus === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <el-text class="w-150px mb-2" truncated style="color: white;">
+                        {{ item.EventDate }}
+                    </el-text>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+        }
+
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 537 - 0
src/views/system/sceneLighting/modules/running.vue

@@ -0,0 +1,537 @@
+<template>
+    <div class="_running">
+        <HeadlineTag type="right" value="运行统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="flex_person">
+                <div ref="chartAccessLeft" style="width: 100%;height: 100%;"></div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import * as echarts from 'echarts'
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartAccessLeft = ref(null);
+let chartLeft = null;
+let chartRight = null;
+const initAccess = () => {
+    let data = [
+        {
+            name: '设备总数',
+            value: 0,
+        },
+        {
+            name: '开启数量',
+            value: 0,
+        },
+        {
+            name: '关闭数量',
+            value: 0,
+        },
+        {
+            name: '故障数量',
+            value: 0,
+        },
+    ];
+    let arrName = getArrayValue(data, 'name');
+    let arrValue = getArrayValue(data, 'value');
+    let sumValue = eval(arrValue.join('+'));
+    let objData = array2obj(data, 'name');
+    let optionData = getData(data);
+    function getArrayValue(array, key) {
+        var key = key || 'value';
+        var res = [];
+        if (array) {
+            array.forEach(function (t) {
+                res.push(t[key]);
+            });
+        }
+        return res;
+    }
+
+    function array2obj(array, key) {
+        var resObj = {};
+        for (var i = 0; i < array.length; i++) {
+            resObj[array[i][key]] = array[i];
+        }
+        return resObj;
+    }
+
+    function getData(data) {
+        var res = {
+            series: [],
+            yAxis: [],
+        };
+        for (let i = 0; i < data.length; i++) {
+            res.series.push({
+                // name: '职员1',
+                type: 'pie',
+                clockwise: true, //顺时加载
+                emphasis: {
+                    scale: false
+                },
+                //鼠标移入变大
+                radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                center: ['30%', '55%'],
+                label: {
+                    show: false,
+                },
+                itemStyle: {
+                    label: {
+                        show: false,
+                    },
+                    labelLine: {
+                        show: false,
+                    },
+                    borderWidth: 5,
+                },
+                data: [
+                    {
+                        value: data[i].value,
+                        name: data[i].name,
+                    },
+                    {
+                        value: sumValue - data[i].value,
+                        name: '',
+                        itemStyle: {
+                            color: 'rgba(0,0,0,0)',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                ],
+            });
+            res.series.push({
+                name: '',
+                type: 'pie',
+                silent: true,
+                z: 1,
+                clockwise: true, //顺时加载
+                emphasis: {
+                    scale: false
+                },
+                //鼠标移入变大
+                radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                center: ['30%', '55%'],
+                label: {
+                    show: false,
+                },
+                itemStyle: {
+                    label: {
+                        show: false,
+                    },
+                    labelLine: {
+                        show: false,
+                    },
+                    borderWidth: 5,
+                },
+                data: [
+                    {
+                        value: 7.5,
+                        itemStyle: {
+                            color: '#E3F0FF',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                    {
+                        value: 2.5,
+                        name: '',
+                        itemStyle: {
+                            color: 'rgba(0,0,0,0)',
+                            borderWidth: 0,
+                        },
+                        tooltip: {
+                            show: false,
+                        },
+                        emphasis: {
+                            scale: false
+                        },
+
+                    },
+                ],
+            });
+            res.yAxis.push(((data[i].value / sumValue) * 100).toFixed(2) + '%');
+        }
+        return res;
+    }
+
+    chartLeft = echarts.init(chartAccessLeft.value);
+    let option = {
+        // backgroundColor: '#fff',
+        legend: {//图例设置
+            show: true,
+            top: '35%',
+            left: '60%',
+            data: arrName,
+            itemWidth: 15,
+            itemHeight: 15,
+            width: 70,
+            padding: [0, 5],
+            itemGap: 20,
+            formatter: function (name) {
+                return '{title|' + name + '}-{value|' + objData[name].value + '}';
+            },
+            textStyle: {
+                rich: {
+                    title: {
+                        fontSize: 12,
+                        lineHeight: 10,
+                        color: '#fff',
+                    },
+                    value: {
+                        fontSize: 12,
+                        lineHeight: 10,
+                        color: '#fff',
+                    },
+                },
+            },
+        },
+        tooltip: {
+            show: true,
+            trigger: 'item',
+            formatter: '{a}<br>{b}:{c}({d}%)',
+            textStyle: {
+                color: '#fafafa',
+            },
+            borderColor: 'transparent',
+            backgroundColor: 'rgba(0, 0, 0, 0.5)',
+            extraCssText: 'backdrop-filter: blur(6px);',
+        },
+        color: ['#409eff', '#15acaa', '#FFC107', 'rgb(244, 67, 54)'],
+        grid: {
+            top: '20%',
+            bottom: '48%',
+            left: '20%',
+            containLabel: false,
+        },
+        yAxis: [
+            {
+                type: 'category',
+                inverse: true,
+                axisLine: {
+                    show: false,
+                },
+                axisTick: {
+                    show: false,
+                },
+                axisLabel: {
+                    interval: 0,
+                    inside: true,
+                    textStyle: {
+                        color: '#fff',
+                        fontSize: 10,
+                    },
+                    show: true,
+                },
+                data: optionData.yAxis,
+            },
+        ],
+        xAxis: [
+            {
+                show: true,
+            },
+        ],
+        series: optionData.series,
+    };
+    chartLeft.setOption(option)
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (chartLeft) {
+        let data = [
+            {
+                name: '设备总数',
+                value: newVal.DeviceCount,
+            },
+            {
+                name: '开启数量',
+                value: newVal.OpenCount,
+            },
+            {
+                name: '关闭数量',
+                value: newVal.OffCount,
+            },
+            {
+                name: '故障数量',
+                value: newVal.FaultCount,
+            },
+        ];
+        let arrName = getArrayValue(data, 'name');
+        let arrValue = getArrayValue(data, 'value');
+        let sumValue = eval(arrValue.join('+'));
+        let objData = array2obj(data, 'name');
+        let optionData = getData(data);
+        function getArrayValue(array, key) {
+            var key = key || 'value';
+            var res = [];
+            if (array) {
+                array.forEach(function (t) {
+                    res.push(t[key]);
+                });
+            }
+            return res;
+        }
+
+        function array2obj(array, key) {
+            var resObj = {};
+            for (var i = 0; i < array.length; i++) {
+                resObj[array[i][key]] = array[i];
+            }
+            return resObj;
+        }
+
+        function getData(data) {
+            var res = {
+                series: [],
+                yAxis: [],
+            };
+            for (let i = 0; i < data.length; i++) {
+                res.series.push({
+                    // name: '职员1',
+                    type: 'pie',
+                    clockwise: true, //顺时加载
+                    emphasis: {
+                        scale: false
+                    },
+                    //鼠标移入变大
+                    radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                    center: ['30%', '55%'],
+                    label: {
+                        show: false,
+                    },
+                    itemStyle: {
+                        label: {
+                            show: false,
+                        },
+                        labelLine: {
+                            show: false,
+                        },
+                        borderWidth: 5,
+                    },
+                    data: [
+                        {
+                            value: data[i].value,
+                            name: data[i].name,
+                        },
+                        {
+                            value: sumValue - data[i].value,
+                            name: '',
+                            itemStyle: {
+                                color: 'rgba(0,0,0,0)',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                    ],
+                });
+                res.series.push({
+                    name: '',
+                    type: 'pie',
+                    silent: true,
+                    z: 1,
+                    clockwise: true, //顺时加载
+                    emphasis: {
+                        scale: false
+                    },
+                    //鼠标移入变大
+                    radius: [65 - i * 15 + '%', 57 - i * 15 + '%'],
+                    center: ['30%', '55%'],
+                    label: {
+                        show: false,
+                    },
+                    itemStyle: {
+                        label: {
+                            show: false,
+                        },
+                        labelLine: {
+                            show: false,
+                        },
+                        borderWidth: 5,
+                    },
+                    data: [
+                        {
+                            value: 7.5,
+                            itemStyle: {
+                                color: '#E3F0FF',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                        {
+                            value: 2.5,
+                            name: '',
+                            itemStyle: {
+                                color: 'rgba(0,0,0,0)',
+                                borderWidth: 0,
+                            },
+                            tooltip: {
+                                show: false,
+                            },
+                            emphasis: {
+                                scale: false
+                            },
+
+                        },
+                    ],
+                });
+                res.yAxis.push(((data[i].value / sumValue) * 100).toFixed(2) + '%');
+            }
+            return res;
+        }
+        chartLeft.setOption({
+            legend: {
+                data: arrName,
+                formatter: function (name) {
+                    return '{title|' + name + '}-{value|' + objData[name].value + '}';
+                },
+            },
+            xAxis: [
+                {
+                    show: true,
+                },
+            ],
+            yAxis: [{
+                type: 'category',
+                data: optionData.yAxis,
+            }],
+            series: optionData.series,
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+// 生命周期
+onMounted(() => {
+    initAccess()
+});
+// 窗口自适应
+window.addEventListener('resize', () => {
+    chartLeft?.resize();
+    chartRight?.resize();
+});
+</script>
+<style lang="scss" scoped>
+._running {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        flex: 1;
+        display: flex;
+
+        &_left {
+            width: 150px;
+            height: 150px;
+            position: relative;
+            flex-shrink: 0;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
+        }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+    }
+}
+
+.flex_person {
+    flex: 1;
+    width: 100%;
+    height: 100%;
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
+    }
+}
+</style>

+ 281 - 0
src/views/system/sceneLighting/modules/switchAll.vue

@@ -0,0 +1,281 @@
+<template>
+    <div class="_switchAll">
+        <HeadlineTag type="right" value="运行分析(周)"></HeadlineTag>
+        <div class="_switchAll_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+ const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        const option = {
+            title: null,
+            tooltip: {
+                trigger: 'axis',
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            legend: false,
+            // 调整 grid 配置以在 Y 轴方向拉伸图表
+            grid: {
+                top: '10%', // 减小顶部边距,让图表向上扩展
+                bottom: '10%', // 减小底部边距,让图表向下扩展
+                right: '0%',
+            },
+            xAxis: {
+                type: 'category',
+                data: [],
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true, // 不显示刻度标签
+                    color: '#fff'
+                },
+                // 配置 Y 轴网格刻线
+                splitLine: {
+                    show: false, // 显示网格刻线
+                    lineStyle: {
+                        color: '#44585e' // 设置网格刻线颜色 
+                    }
+                },
+                axisTick: {
+                    show: false // 不显示刻度线
+                },
+            },
+            series: [
+                {
+                    name: '开启数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(49, 143, 247)',
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(49, 143, 247, 0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(49, 143, 247, 0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                },
+                {
+                    name: '关闭数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(9, 60),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(255,193,7)',
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(255,193,7, 0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(255,193,7,0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                },
+                {
+                    name: '故障数量',
+                    type: 'line',
+                    smooth: true,
+                    // data: generateRandomData(9, 60),
+                    data: [],
+                    showSymbol: false,
+                    lineStyle: {
+                        color: 'rgb(244,67,54)'
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(244,67,54,0.6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(244,67,54,0.2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    }
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        let arrData = Object.keys(newVal)
+        let arr1 = []
+        let arr2 = []
+        let arr3 = []
+        arrData.forEach((item) => {
+            let data = Object.entries(newVal[item])
+            arr1.push(data[0][1])
+            arr2.push(data[1][1])
+            arr3.push(data[2][1])
+        })
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                name: '开启数量',
+                data: arr1,
+            }, {
+                name: '关闭数量',
+                data: arr2,
+            }, {
+                name: '故障数量',
+                data: arr3,
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._switchAll {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 7 - 7
src/views/system/tenement/modules/carbonEmission.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="_consume">
-        <HeadlineTag value="门禁管理"></HeadlineTag>
+        <HeadlineTag value="业主投诉量趋势"></HeadlineTag>
         <div class="_consume_mains">
             <div ref="chartRef" style="width: 100%; height: 100%;"></div>
         </div>
@@ -84,7 +84,7 @@ onMounted(() => {
             series: [
                 // 柱状图表示运行正常
                 {
-                    name: '刷脸进入',
+                    name: '卫生问题',
                     type: 'bar',
                     // data: generateRandomData(7, 50),
                     data: [],
@@ -108,7 +108,7 @@ onMounted(() => {
                 },
                 // 曲线表示运行异常
                 {
-                    name: '刷卡进入',
+                    name: '安全问题',
                     type: 'line',
                     // data: generateRandomData(7, 50),
                     data: [],
@@ -139,7 +139,7 @@ onMounted(() => {
                     },
                 },
                 {
-                    name: '远程开门',
+                    name: '设施设备问题',
                     type: 'line',
                     // data: generateRandomData(7, 50),
                     data: [],
@@ -194,13 +194,13 @@ watch(() => props.resultData, (newVal) => {
                 data: Object.keys(newVal),
             },
             series: [{
-                name: '刷脸进入',
+                name: '卫生问题',
                 data: arr1,
             },{
-                name: '刷卡进入',
+                name: '安全问题',
                 data: arr2,
             },{
-                name: '远程开门',
+                name: '设施设备问题',
                 data: arr3,
             }],
         })

+ 1 - 1
src/views/system/tenement/modules/consume.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="_runnings">
-        <HeadlineTag value="统计数据"></HeadlineTag>
+        <HeadlineTag value="小区基本信息"></HeadlineTag>
         <div class="_runnings_mains" :style="{ '--heightSume': heightcon + 'px' }">
             <div class="_runnings_mains_left" id="sumeWidth">
                 <div class="_runnings_mains_left_tuan tuan1"></div>

+ 1 - 1
src/views/system/tenement/modules/eventList.vue

@@ -14,7 +14,7 @@
                         </el-text>
                     </div>
                     <div :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
-                        {{ item.State === 0 ? '进入' : '出入' }}
+                        {{ item.State === 0 ? '进入' : '离开' }}
                     </div>
                     <div class="_eventList_mains_item_btn">
                         {{ item.Date }}

+ 5 - 5
src/views/system/tenement/modules/sameDay.vue

@@ -1,6 +1,6 @@
 <template>
     <div class="_consume">
-        <HeadlineTag type="right" value="车库流动(周)"></HeadlineTag>
+        <HeadlineTag type="right" value="业主满意度趋势"></HeadlineTag>
         <div class="_consume_mains">
             <div ref="chartRef" style="width: 100%; height: 100%;"></div>
         </div>
@@ -84,7 +84,7 @@ onMounted(() => {
             },
             series: [
             {
-                    name: '车辆入库(辆)',
+                    name: '环境卫生满意度',
                     type: 'line',
                     // data: generateRandomData(7, 50),
                     data:[],
@@ -115,7 +115,7 @@ onMounted(() => {
                     },
                 },
                 {
-                    name: '车辆出库(辆)',
+                    name: '安全管理满意度',
                     type: 'line',
                     // data: generateRandomData(7, 50),
                     data:[],
@@ -168,10 +168,10 @@ watch(() => props.resultData, (newVal) => {
                 data: Object.keys(newVal),
             },
             series: [{
-                name: '车辆入库(辆)',
+                name: '环境卫生满意度',
                 data: arr1,
             },{
-                name: '车辆出库(辆)',
+                name: '安全管理满意度',
                 data: arr2,
             }],
         })

+ 24 - 12
src/views/system/tenement/modules/tiring.vue

@@ -1,23 +1,20 @@
 <template>
     <div class="_eventList">
-        <HeadlineTag type="right" value="车辆出入记录" style="flex-shrink: 0;"></HeadlineTag>
+        <HeadlineTag type="right" value="客户咨询与投诉数据" style="flex-shrink: 0;"></HeadlineTag>
         <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
             <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
                 <div class="item" v-for="(item, index) in data" :key="index">
-                    <div class="_eventList_mains_item_text">
-                        <div :class="item.State === 0 ? '_success' : '_warning'"
-                            class="_eventList_mains_item_text_flag">
-                            {{ item.State === 0 ? '进' : '出' }}
-                        </div>
-                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                    <div class="_eventList_mains_item_text tiring_name">
+                        <el-text class="w-150px mb-2" truncated style="color: white;">
                             {{ item.GarName }}
                         </el-text>
                     </div>
-                    <div :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
-                        {{ item.State === 0 ? '进入' : '离开' }}
+                    <div class="tiring_name"
+                        :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
+                        {{ item.State === 0 ? '咨询' : '投诉' }}
                     </div>
-                    <div style="width: 30px;height: 30px;">
-                        <el-image :src="item.GarImag" style="width: 30px;height: 30px;border-radius: 5px;" />
+                    <div class="view_item">
+                        {{ item.Context }}
                     </div>
                     <div class="_eventList_mains_item_btn">
                         {{ item.Date }}
@@ -115,6 +112,21 @@ const onMouseleave = () => {
     background: #FFC107;
 }
 
+.tiring_name {
+    width: 60px;
+}
+
+.view_item {
+    flex: 1;
+    // display: flex;
+    // align-items: center;
+    // justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    color: #ffffff;
+}
+
 ._eventList {
     overflow: hidden;
     display: flex;
@@ -142,7 +154,6 @@ const onMouseleave = () => {
                     width: 30px;
                     height: 30px;
                     border-radius: 50%;
-                    margin-left: 10px;
                     display: flex;
                     align-items: center;
                     justify-content: center;
@@ -169,6 +180,7 @@ const onMouseleave = () => {
         }
     }
 }
+
 .page {
     width: 100%;
     height: 100%;

+ 6 - 6
src/views/system/video/index.vue

@@ -3,9 +3,9 @@
         <layout>
             <template #left>
                 <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
-                    <running :resultData="leftData" style="flex: 1;" />
-                    <consume :resultData="leftData" style="flex: 1;" />
-                    <carbonEmission :resultData="leftData" style="flex: 1;" />
+                    <consume :resultData="leftData" style="height: 35%;" />
+                    <running :resultData="leftData.MonitorList" style="flex: 1;" />
+                    <!-- <carbonEmission :resultData="leftData" style="flex: 1;" /> -->
                 </div>
             </template>
             <template #content>
@@ -13,8 +13,8 @@
             </template>
             <template #right>
                 <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
-                    <sameDay :resultData="leftData" style="flex: 1;" />
-                    <eventList :resultData="leftData.MonitorList" style="flex: 2;" />
+                    <sameDay :resultData="leftData" style="height: 35%;" />
+                    <eventList :resultData="leftData.MonitorNms" style="flex: 1;" />
                 </div>
             </template>
         </layout>
@@ -24,9 +24,9 @@
 <script setup name="Role">
 import { getHikvision } from "@/api/system/monitor"
 import layout from "@/components/layout_/index.vue";
+import consume from './modules/consume.vue'
 import running from './modules/running.vue'
 import carbonEmission from './modules/carbonEmission.vue'
-import consume from './modules/consume.vue'
 import sameDay from './modules/sameDay.vue'
 import eventList from './modules/eventList.vue'
 

+ 45 - 49
src/views/system/video/modules/consume.vue

@@ -1,26 +1,29 @@
 <template>
-    <div class="_runnings">
-        <HeadlineTag value="行为检测(周)"></HeadlineTag>
-        <div class="_runnings_mains" :style="{ '--heightSume': heightcon + 'px' }">
-            <div class="_runnings_mains_left" id="sumeWidth">
-                <div class="_runnings_mains_left_tuan tuan1"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.IntrusionDetection}}</div>
-                    <div class="_runnings_mains_left_conter_text">入侵检测</div>
+    <div class="_running">
+        <HeadlineTag value="设备统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">{{ resultData.MonitorCount }}</div>
+                    <div class="_running_mains_left_conter_text">设备总量</div>
                 </div>
             </div>
-            <div class="_runnings_mains_left">
-                <div class="_runnings_mains_left_tuan tuan2"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.AbnormalBehavior}}</div>
-                    <div class="_runnings_mains_left_conter_text">异常行为</div>
-                </div>
-            </div>
-            <div class="_runnings_mains_left">
-                <div class="_runnings_mains_left_tuan tuan3"></div>
-                <div class="_runnings_mains_left_conter">
-                    <div class="_runnings_mains_left_conter_num">{{resultData.LostAlarms}}</div>
-                    <div class="_runnings_mains_left_conter_text">丢失告警</div>
+            <div class="_running_mains_right">
+                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
+                    <div class="_running_mains_right_item_tuan">
+                        <span class="_running_mains_right_item_tuan_flag"
+                            :style="{ backgroundColor: item.color }"></span>
+                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_running_mains_right_item__txt">
+                        <span>{{ item.state }}</span>
+                        <span :style="{ color: item.color, 'font-size': '12px', 'margin-left': '5px' }">
+                            {{ item.unit }}
+                        </span>
+                    </div>
                 </div>
             </div>
         </div>
@@ -38,58 +41,51 @@ const props = defineProps({
 })
 
 const runningList = ref([
-    { name: '入侵检测', state: '154', color: '#409eff', tip: '划定区域人员闯入' },
-    { name: '异常行为', state: '15', color: '#15acaa', tip: '徘徊、倒地、聚集' },
-    { name: '丢失告警', state: '134', color: '#FFC107', tip: '物品遗留/丢失告警' },
+    { name: '设备在线', state: 0, color: '#409eff', unit: '台' },
+    { name: '设备离线', state: 0, color: '#15acaa', unit: '台' },
+    { name: '存储容量', state: 0, color: '#FFC107', unit: 'TB' },
 ])
-const heightcon = ref(0)
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        runningList.value[0].state = newVal.DeviceOnline
+        runningList.value[1].state = newVal.DeviceOffline
+        runningList.value[2].state = newVal.StorageCapacity
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+const heightVideo = ref(0)
 // 生命周期
 onMounted(() => {
-    var element = document.getElementById("sumeWidth");
+    var element = document.getElementById("videoWidth");
     var width = element.offsetWidth;
-    heightcon.value = width
+    heightVideo.value = width
 });
 </script>
 <style lang="scss" scoped>
-.tuan1 {
-    background: url("@/assets/images/video_bg_1.png");
-    animation: scanning 5s linear infinite;
-}
-
-.tuan2 {
-    background: url("@/assets/images/video_bg_2.png");
-    animation: scanning 5s linear infinite;
-}
-
-.tuan3 {
-    background: url("@/assets/images/video_bg_3.png");
-    animation: scanning 5s linear infinite;
-}
-
-._runnings {
+._running {
     display: flex;
-    align-items: center;
     flex-direction: column;
 
     &_mains {
         flex: 1;
-        width: 100%;
+        margin: 30px;
         display: flex;
         align-items: center;
-        justify-content: space-around;
 
         &_left {
-            flex-shrink: 0;
-            width: 32%;
-            height: var(--heightSume);
+            width: 50%;
+            height: var(--heightRun);
             position: relative;
 
             &_tuan {
                 width: 100%;
                 height: 100%;
-                background-size: 80% 80%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
                 background-position: center;
                 background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
             }
 
             &_conter {

+ 34 - 34
src/views/system/video/modules/eventList.vue

@@ -1,45 +1,18 @@
 <template>
     <div class="_eventList">
-        <HeadlineTag type="right" value="监控列表" style="flex-shrink: 0;"></HeadlineTag>
-        <!-- <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
-            <div :style="{ transform: `translateY(${scrollY}px)` }">
-                <div class="_eventList_mains_item" v-for="(item, index) in eventList.concat(eventList)" :key="index">
-                    <div class="_eventList_mains_item_text">
-                        <div
-                        :class="item.Id === 'on' ? '_success' : '_warning'"
-                        class="_eventList_mains_item_text_flag"></div>
-                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
-                            {{ item.Name }}
-                        </el-text>
-                    </div>
-                    <div class="_eventList_mains_item_btn">
-                        <el-icon color="#44b0f9" size="18">
-                            <Download />
-                        </el-icon>
-                        <el-icon color="#44b0f9" size="18">
-                            <Bell />
-                        </el-icon>
-                    </div>
-                </div>
-            </div>
-        </div> -->
+        <HeadlineTag type="right" value="视频图像诊断列表" style="flex-shrink: 0;"></HeadlineTag>
         <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
             <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
                 <div class="item" v-for="(item, index) in data" :key="index">
-                    <div class="_eventList_mains_item_text">
+                    <div class="_eventList_mains_item_text view_item" style="flex: 2;">
                         <div :class="item.Id === 'on' ? '_success' : '_warning'"
                             class="_eventList_mains_item_text_flag"></div>
                         <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
                             {{ item.Name }}
                         </el-text>
                     </div>
-                    <div class="_eventList_mains_item_btn">
-                        <el-icon color="#44b0f9" size="18">
-                            <Download />
-                        </el-icon>
-                        <el-icon color="#44b0f9" size="18">
-                            <Bell />
-                        </el-icon>
+                    <div class="view_item">
+                        {{ getdiagnose(item.InspectResult) }}
                     </div>
                 </div>
             </div>
@@ -75,7 +48,25 @@ const getData = () => {
         }, 100);
     });
 };
-
+function getdiagnose(params) {
+    let title = ''
+    if (params == 0) {
+        title = '视频异常'
+    } else if (params == 1) {
+        title = '视频正常'
+    } else if (params == 2) {
+        title = '登录失败'
+    } else if (params == 3) {
+        title = '取流异常'
+    } else if (params == 4) {
+        title = '解码失败'
+    } else if (params == 5) {
+        title = '码流延时'
+    } else if (params == 6) {
+        title = '诊断失败'
+    }
+    return title
+}
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
         // data.value = await getData();
@@ -133,6 +124,16 @@ const onMouseleave = () => {
     background: #FFC107;
 }
 
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    color: #ffffff;
+}
+
 ._eventList {
     overflow: hidden;
     display: flex;
@@ -146,13 +147,12 @@ const onMouseleave = () => {
 
         &_item {
             display: flex;
-            justify-content: space-between;
             align-items: center;
             padding: 10px 0;
 
             &_text {
                 display: flex;
-                justify-content: space-between;
+                // justify-content: space-between;
                 align-items: center;
 
                 &_flag {

+ 191 - 107
src/views/system/video/modules/running.vue

@@ -1,29 +1,20 @@
 <template>
-    <div class="_running">
-        <HeadlineTag value="实时设备统计"></HeadlineTag>
-        <div class="_running_mains">
-            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
-                <div class="_running_mains_left_tuan"></div>
-                <div class="_running_mains_left_conter">
-                    <div class="_running_mains_left_conter_num">{{ resultData.MonitorCount }}</div>
-                    <div class="_running_mains_left_conter_text">摄像头总量</div>
-                </div>
-            </div>
-            <div class="_running_mains_right">
-                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
-                    <div class="_running_mains_right_item_tuan">
-                        <span class="_running_mains_right_item_tuan_flag"
-                            :style="{ backgroundColor: item.color }"></span>
-                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
-                            {{ item.name }}
+    <div class="_eventList">
+        <HeadlineTag value="监控列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.Name }}
                         </el-text>
                     </div>
-                    <div class="_running_mains_right_item__txt">
-                        <span>{{ item.state }}</span>
-                        <span :style="{ color: item.color, 'font-size': '12px', 'margin-left': '5px' }">
-                            {{ item.unit }}
-                        </span>
-                    </div>
+                    <div style="justify-content: flex-start;width: 60px;"
+                        :class="item.State == 0 ? 'blue_title' : 'red_title'">
+                        {{ item.State == 0 ? '在线' : '离线' }}</div>
+                    <div class="view_item">{{ item.Location }}</div>
                 </div>
             </div>
         </div>
@@ -31,125 +22,218 @@
 </template>
 
 <script setup>
-import { ref } from "vue";
+import { ref, onMounted, onUnmounted } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
-        type: Object,
-        default: {}
+        type: Array,
+        default: []
     }
 })
-
-const runningList = ref([
-    { name: '设备在线', state: 0, color: '#409eff', unit: '台' },
-    { name: '设备离线', state: 0, color: '#15acaa', unit: '台' },
-    { name: '存储容量', state: 0, color: '#FFC107', unit: 'TB' },
-    { name: '存储剩余', state: 0, color: '#F44336', unit: 'TB' },
-])
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
 
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        runningList.value[0].state = newVal.DeviceOnline
-        runningList.value[1].state = newVal.DeviceOffline
-        runningList.value[2].state = newVal.StorageCapacity
-        runningList.value[3].state = newVal.StoreSurplus
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-const heightVideo = ref(0)
-// 生命周期
 onMounted(() => {
-    var element = document.getElementById("videoWidth");
-    var width = element.offsetWidth;
-    heightVideo.value = width
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
+
 <style lang="scss" scoped>
-._running {
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
     display: flex;
     flex-direction: column;
 
     &_mains {
-        flex: 1;
-        margin: 30px;
-        display: flex;
-        align-items: center;
-
-        &_left {
-            width: 50%;
-            height: var(--heightRun);
-            position: relative;
-
-            &_tuan {
-                width: 100%;
-                height: 100%;
-                background: url("@/assets/images/content_circle.png");
-                background-size: 100% 100%;
-                background-position: center;
-                background-repeat: no-repeat;
-                animation: scanning 4s linear infinite;
-            }
-
-            &_conter {
-                position: absolute;
-                left: 0;
-                top: 0;
-                flex-shrink: 0;
-                width: 100%;
-                height: 100%;
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
                 display: flex;
-                flex-direction: column;
+                justify-content: space-between;
                 align-items: center;
-                justify-content: center;
-                color: #fff;
 
-                &_num {
-                    font-size: 18px;
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
                 }
 
-                &_text {
-                    font-size: 12px;
+                &_p {
+                    margin-left: 10px;
                 }
             }
-        }
-
-        &_right {
-            margin-left: 10px;
-            flex: 1;
-            color: #fff;
 
-            &_item {
+            &_btn {
+                color: red;
                 display: flex;
-                align-items: center;
-                gap: 40px;
-                padding: 5px 0;
-
-                &_tuan {
-                    display: flex;
-                    align-items: center;
-
-                    &_flag {
-                        display: block;
-                        width: 7px;
-                        height: 7px;
-                        border-radius: 50%;
-                        margin-right: 10px;
-                    }
-                }
+                font-size: 14px;
+                gap: 20px;
 
-                &__txt {
-                    font-size: 24px;
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
                 }
             }
         }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
 
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
 
-    }
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
 }
 
-@keyframes scanning {
-    to {
-        transform: rotate(1turn);
-    }
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
 }
 </style>

+ 121 - 31
src/views/system/video/modules/sameDay.vue

@@ -1,34 +1,35 @@
 <template>
-    <div class="_runnin">
-        <HeadlineTag type="right" value="性能统计"></HeadlineTag>
-        <div class="_runnin_mains">
-            <!-- 修正引用名称,与脚本里保持一致 -->
-            <div class="_runnin_mains_item" style="width: 33%;height: 100%;">
-                <div style="width: 100%;">
-                    <waterdrop width="100%" fontSize="32px" circleBackGround="rgb(8, 36, 62)" birderColor="transparent"
-                        color="rgb(42, 139, 247)" :heightTop="'-' + resultData.CPU + '%'"
-                        :dropTitle="resultData.CPU + '%'">
-                    </waterdrop>
+    <div class="_running">
+        <HeadlineTag type="right" value="视频图像诊断"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">{{ resultData.MonitorCount }}</div>
+                    <div class="_running_mains_left_conter_text">设备总量</div>
                 </div>
-                <span style="margin-top: 10px;font-weight: bold;">CPU</span>
             </div>
-            <div class="_runnin_mains_item" style="width: 33%;height: 100%;">
-                <div style="width: 100%;">
-                    <waterdrop width="100%" fontSize="32px" circleBackGround="rgb(8, 36, 62)"
-                        birderColor="rgb(8, 36, 62)" color="rgb(251, 97, 38)" :heightTop="'-' + resultData.RAM + '%'"
-                        :dropTitle="resultData.RAM + '%'">
-                    </waterdrop>
+            <div class="_running_mains_right">
+                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
+                    <div class="_running_mains_right_item_tuan">
+                        <span class="_running_mains_right_item_tuan_flag"
+                            :style="{ backgroundColor: item.color }"></span>
+                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_running_mains_right_item__txt">
+                        <span>{{ item.state }}</span>
+                    </div>
                 </div>
-                <span style="margin-top: 10px;font-weight: bold;">RAM</span>
             </div>
         </div>
     </div>
 </template>
 
 <script setup>
-import 'echarts-liquidfill';
+import { ref } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
-import waterdrop from '@/components/waterdrop'
 const props = defineProps({
     resultData: {
         type: Object,
@@ -36,10 +37,32 @@ const props = defineProps({
     }
 })
 
-</script>
+const runningList = ref([
+    { name: '图像正常', state: 0, color: '#409eff' },
+    { name: '图像异常', state: 0, color: '#15acaa' },
+    { name: '诊断失败', state: 0, color: '#e84042' },
+    { name: '未检测', state: 0, color: '#FFC107' },
+])
 
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        runningList.value[0].state = newVal.ImageIsNormal
+        runningList.value[1].state = newVal.ImageAbnormalities
+        runningList.value[2].state = newVal.DiagnosisFailed
+        runningList.value[3].state = newVal.NotDetected
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+const heightVideo = ref(0)
+// 生命周期
+onMounted(() => {
+    var element = document.getElementById("videoWidth");
+    var width = element.offsetWidth;
+    heightVideo.value = width
+});
+</script>
 <style lang="scss" scoped>
-._runnin {
+._running {
     display: flex;
     flex-direction: column;
 
@@ -48,16 +71,83 @@ const props = defineProps({
         margin: 30px;
         display: flex;
         align-items: center;
-        justify-content: space-around;
-        gap: 30px;
-
-        &_item {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            flex-direction: column;
-            color: #FFF;
+
+        &_left {
+            width: 50%;
+            height: var(--heightRun);
+            position: relative;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
         }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+                    width: 85px;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+
+
+    }
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
     }
 }
 </style>

+ 65 - 0
src/views/system/wastewater/index.vue

@@ -0,0 +1,65 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <blowingIn :resultData="leftData.OnedayPowerConsumption" style="height: 35%;" />
+                    <malfunction :resultData="MonitorList" style="flex: 1;" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>给排水系统</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <consume :resultData="leftData" style="height: 35%;" />
+                    <running :resultData="MonitorList" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Air">
+import { getEnergy } from "@/api/system/energy"
+import { getAccess } from "@/api/system/passageway"
+import layout from "@/components/layout_/index.vue";
+import blowingIn from './modules/blowingIn.vue'
+import malfunction from './modules/malfunction.vue'
+import consume from './modules/consume.vue'
+import running from './modules/running.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+const MonitorList = ref([])
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getEnergyData, 10000);
+    getEnergyData()
+    getAccessData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getEnergyData() {
+    getEnergy().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+
+function getAccessData() {
+    getAccess().then((res) => {
+        if (res.code == 200) {
+            MonitorList.value = res.data.DeviceList
+        }
+    })
+}
+
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 211 - 0
src/views/system/wastewater/modules/blowingIn.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag value="集水坑高低液位趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时气能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#49e645',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(58, 226, 56, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(58, 226, 56, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 151 - 0
src/views/system/wastewater/modules/consume.vue

@@ -0,0 +1,151 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="设备统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">{{ resultData.EnergyCount }}</div>
+                    <div class="_running_mains_left_conter_text">水泵总量</div>
+                </div>
+            </div>
+            <div class="_running_mains_right">
+                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
+                    <div class="_running_mains_right_item_tuan">
+                        <span class="_running_mains_right_item_tuan_flag"
+                            :style="{ backgroundColor: item.color }"></span>
+                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_running_mains_right_item__txt">
+                        <span>{{ item.state }}</span>
+                        <span :style="{ color: item.color, 'font-size': '12px', 'margin-left': '5px' }">
+                            {{ item.unit }}
+                        </span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const runningList = ref([
+    { name: '设备在线', state: 0, color: '#409eff', unit: '台' },
+    { name: '设备离线', state: 0, color: '#15acaa', unit: '台' },
+])
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        runningList.value[0].state = newVal.CarbonEmissions
+        runningList.value[1].state = newVal.EnergyIntensity
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+const heightVideo = ref(0)
+// 生命周期
+onMounted(() => {
+    var element = document.getElementById("videoWidth");
+    var width = element.offsetWidth;
+    heightVideo.value = width
+});
+</script>
+<style lang="scss" scoped>
+._running {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+
+        &_left {
+            width: 50%;
+            height: var(--heightRun);
+            position: relative;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
+        }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+
+
+    }
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
+    }
+}
+</style>

+ 239 - 0
src/views/system/wastewater/modules/malfunction.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag value="水泵故障状态列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.Name }}
+                        </el-text>
+                    </div>
+                    <div style="justify-content: flex-start;width: 60px;"
+                        :class="item.State == 0 ? 'blue_title' : 'red_title'">
+                        {{ item.State == 0 ? '待使用' : '待维修' }}</div>
+                    <div class="view_item">{{ item.Date }}</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+
+            &_btn {
+                color: red;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 239 - 0
src/views/system/wastewater/modules/running.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag value="水泵运行状态列表" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.Name }}
+                        </el-text>
+                    </div>
+                    <div style="justify-content: flex-start;width: 60px;"
+                        :class="item.State == 0 ? 'blue_title' : 'red_title'">
+                        {{ item.State == 0 ? '正常' : '故障' }}</div>
+                    <div class="view_item">{{ item.Date }}</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+
+            &_btn {
+                color: red;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 67 - 0
src/views/system/wirelessIntercom/index.vue

@@ -0,0 +1,67 @@
+<template>
+    <div class="_energy">
+        <layout>
+            <template #left>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <blowingIn :resultData="leftData.OnedayPowerConsumption" style="height: 35%;" />
+                    <malfunction :resultData="DeviceRanking" style="flex: 1;" />
+                </div>
+            </template>
+            <template #content>
+                <!-- <p>无线对讲系统</p> -->
+            </template>
+            <template #right>
+                <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                    <consume :resultData="leftData" style="height: 35%;" />
+                    <running :resultData="MonitorList" style="flex: 1;" />
+                </div>
+            </template>
+        </layout>
+    </div>
+</template>
+
+<script setup name="Air">
+import { getEnergy } from "@/api/system/energy"
+import { getAccess } from "@/api/system/passageway"
+import layout from "@/components/layout_/index.vue";
+import consume from './modules/consume.vue'
+import running from './modules/running.vue'
+import blowingIn from './modules/blowingIn.vue'
+import malfunction from './modules/malfunction.vue'
+
+const intervalId = ref(null)
+const leftData = ref({})
+const MonitorList = ref([])
+const DeviceRanking = ref([])
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getEnergyData, 10000);
+    getEnergyData()
+    getAccessData()
+});
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
+function getEnergyData() {
+    getEnergy().then((res) => {
+        if (res.code == 200) {
+            leftData.value = res.data
+        }
+    })
+}
+
+function getAccessData() {
+    getAccess().then((res) => {
+        if (res.code == 200) {
+            MonitorList.value = res.data.DeviceList
+            DeviceRanking.value = res.data.DeviceRanking
+        }
+    })
+}
+
+</script>
+<style lang="scss">
+._energy {
+    height: 100%;
+}
+</style>

+ 211 - 0
src/views/system/wirelessIntercom/modules/blowingIn.vue

@@ -0,0 +1,211 @@
+<template>
+    <div class="_consume">
+        <HeadlineTag value="对讲使用情况趋势"></HeadlineTag>
+        <div class="_consume_mains">
+            <div ref="chartRef" style="width: 100%; height: 100%;"></div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from 'vue';
+import * as echarts from 'echarts';
+import HeadlineTag from '@/components/HeadlineTag';
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const chartRef = ref(null);
+let chart = null;
+const generateRandomData = (length, max) => {
+    const randomData = [];
+    for (let i = 0; i < length; i++) {
+        randomData.push(Math.floor(Math.random() * max));
+    }
+    return randomData;
+};
+const handleResize = () => {
+    if (chart) {
+        chart.resize();
+    }
+};
+onMounted(() => {
+    if (chartRef.value) {
+        chart = echarts.init(chartRef.value);
+        // 修改为 24 小时
+        // const hours = Array.from({ length: 24 }, (_, i) => `${i}时`);
+        const hours = []
+        const option = {
+            xAxis: {
+                type: 'category',
+                // 使用 24 小时数据
+                data: hours,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                },
+                textStyle: {
+                    color: '#fafafa',
+                },
+                borderColor: 'transparent',
+                backgroundColor: 'rgba(0, 0, 0, 0.5)',
+                extraCssText: 'backdrop-filter: blur(6px);',
+            },
+            grid: {
+                top: '10%',
+                bottom: '10%',
+                right: '0%',
+            },
+            yAxis: {
+                type: 'value',
+                axisLabel: {
+                    show: true,
+                    color: '#fff'
+                },
+                splitLine: {
+                    show: false,
+                    lineStyle: {
+                        color: '#44585e'
+                    }
+                },
+                axisTick: {
+                    show: false
+                },
+            },
+            series: [
+                {
+                    name: '24小时气能耗',
+                    // data: generateRandomData(7, 50),
+                    data: [],
+                    type: 'line',
+                    showSymbol: false,
+                    smooth: true,
+                    color: '#49e645',
+                    lineStyle: {
+                        width: 2,
+                    },
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(58, 226, 56, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(58, 226, 56, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                    symbol: 'circle',
+                    symbolSize: 6,
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+});
+
+watch(() => props.resultData, (newVal) => {
+    if (chart) {
+        chart.setOption({
+            xAxis: {
+                data: Object.keys(newVal),
+            },
+            series: [{
+                type: 'line',
+                data: Object.entries(newVal),
+            }],
+        })
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onUnmounted(() => {
+    window.removeEventListener('resize', handleResize);
+    if (chart) {
+        chart.dispose();
+    }
+});
+</script>
+
+<style lang="scss">
+._divider {
+    height: 1px;
+    border: 1px dashed #168cdb;
+    flex: 1;
+    margin: 0 10px;
+}
+
+._consume {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        flex: 1;
+        display: flex;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px;
+
+            &_name {
+                width: 30px;
+                height: 30px;
+                background: url("@/assets/images/content_circle_num.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                // animation: scanning 4s linear infinite;
+                border: 1px solid red;
+            }
+
+            &_flag {
+                display: flex;
+                align-items: center;
+                gap: 3px;
+
+                &_item {
+                    width: 30px;
+                    height: 30px;
+                    border: 3px dashed #168cdb;
+                    box-sizing: border-box;
+                    background: #0e6ead;
+                    color: #fff;
+                    border-radius: 50%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    font-size: 14px;
+                }
+            }
+        }
+
+        &_item:hover {
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent);
+        }
+    }
+}
+</style>

+ 151 - 0
src/views/system/wirelessIntercom/modules/consume.vue

@@ -0,0 +1,151 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="设备统计"></HeadlineTag>
+        <div class="_running_mains">
+            <div class="_running_mains_left" id="videoWidth" :style="{ '--heightRun': heightVideo + 'px' }">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">{{ resultData.EnergyCount }}</div>
+                    <div class="_running_mains_left_conter_text">设备总量</div>
+                </div>
+            </div>
+            <div class="_running_mains_right">
+                <div class="_running_mains_right_item" v-for="item, index in runningList" :key="index">
+                    <div class="_running_mains_right_item_tuan">
+                        <span class="_running_mains_right_item_tuan_flag"
+                            :style="{ backgroundColor: item.color }"></span>
+                        <el-text class="w-150px mb-2" truncated style="color: #ccc;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_running_mains_right_item__txt">
+                        <span>{{ item.state }}</span>
+                        <span :style="{ color: item.color, 'font-size': '12px', 'margin-left': '5px' }">
+                            {{ item.unit }}
+                        </span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Object,
+        default: {}
+    }
+})
+
+const runningList = ref([
+    { name: '设备在线', state: 0, color: '#409eff', unit: '台' },
+    { name: '设备离线', state: 0, color: '#15acaa', unit: '台' },
+])
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        runningList.value[0].state = newVal.CarbonEmissions
+        runningList.value[1].state = newVal.EnergyIntensity
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+const heightVideo = ref(0)
+// 生命周期
+onMounted(() => {
+    var element = document.getElementById("videoWidth");
+    var width = element.offsetWidth;
+    heightVideo.value = width
+});
+</script>
+<style lang="scss" scoped>
+._running {
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+
+        &_left {
+            width: 50%;
+            height: var(--heightRun);
+            position: relative;
+
+            &_tuan {
+                width: 100%;
+                height: 100%;
+                background: url("@/assets/images/content_circle.png");
+                background-size: 100% 100%;
+                background-position: center;
+                background-repeat: no-repeat;
+                animation: scanning 4s linear infinite;
+            }
+
+            &_conter {
+                position: absolute;
+                left: 0;
+                top: 0;
+                flex-shrink: 0;
+                width: 100%;
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                justify-content: center;
+                color: #fff;
+
+                &_num {
+                    font-size: 18px;
+                }
+
+                &_text {
+                    font-size: 12px;
+                }
+            }
+        }
+
+        &_right {
+            margin-left: 10px;
+            flex: 1;
+            color: #fff;
+
+            &_item {
+                display: flex;
+                align-items: center;
+                gap: 40px;
+                padding: 5px 0;
+
+                &_tuan {
+                    display: flex;
+                    align-items: center;
+
+                    &_flag {
+                        display: block;
+                        width: 7px;
+                        height: 7px;
+                        border-radius: 50%;
+                        margin-right: 10px;
+                    }
+                }
+
+                &__txt {
+                    font-size: 24px;
+                }
+            }
+        }
+
+
+    }
+}
+
+@keyframes scanning {
+    to {
+        transform: rotate(1turn);
+    }
+}
+</style>

+ 237 - 0
src/views/system/wirelessIntercom/modules/malfunction.vue

@@ -0,0 +1,237 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag value="对讲使用信息" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.DeviceName }}
+                        </el-text>
+                    </div>
+                    <div class="view_item">{{ item.Value }}次</div>
+                    <div class="view_item">2025-06-19 05:02:44</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+
+            &_btn {
+                color: red;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 239 - 0
src/views/system/wirelessIntercom/modules/running.vue

@@ -0,0 +1,239 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag value="无线对讲设备信息" style="flex-shrink: 0;"></HeadlineTag>
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text view_item" style="width: 45%;">
+                        <div :class="item.Id === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}F#{{ item.Name }}
+                        </el-text>
+                    </div>
+                    <div style="justify-content: flex-start;width: 60px;"
+                        :class="item.State == 0 ? 'blue_title' : 'red_title'">
+                        {{ item.State == 0 ? '正常' : '故障' }}</div>
+                    <div class="view_item">{{ item.Date }}</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const data = ref(); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style lang="scss" scoped>
+._success {
+    background: #15acaa;
+}
+
+._warning {
+    background: #FFC107;
+}
+
+._eventList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+
+    &_mains {
+        margin: 10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        // 鼠标移入时改变手势
+        cursor: pointer;
+
+        &_item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+
+                &_flag {
+                    width: 10px;
+                    height: 10px;
+                    border-radius: 50%;
+                    margin-left: 10px;
+                }
+
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+
+            &_btn {
+                color: red;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+    }
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    // flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    width: 33%;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 1 - 1
vite.config.js

@@ -20,7 +20,7 @@ export default defineConfig(({ mode, command }) => {
     },
     // vite 相关配置
     server: {
-      port: 80,
+      port: 8080,
       host: true,
       open: true,
       proxy: {

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно