YangJian0701 3 months ago
parent
commit
e24f92c2da
33 changed files with 2980 additions and 109 deletions
  1. 1 0
      package.json
  2. BIN
      src/assets/images/_btn.png
  3. BIN
      src/assets/images/car.jpg
  4. 2 1
      src/layout/components/AppMain.vue
  5. 40 0
      src/views/system/broadcast/index.vue
  6. 161 0
      src/views/system/broadcast/modules/deviceList.vue
  7. 169 0
      src/views/system/broadcast/modules/eventList.vue
  8. 120 0
      src/views/system/broadcast/modules/running.vue
  9. 193 0
      src/views/system/broadcast/modules/sameDay.vue
  10. 186 0
      src/views/system/broadcast/modules/tiring.vue
  11. 4 0
      src/views/system/elevator/modules/consume.vue
  12. 6 1
      src/views/system/elevator/modules/running.vue
  13. 6 1
      src/views/system/energy/modules/running.vue
  14. 6 1
      src/views/system/energy/modules/sameDay.vue
  15. 1 1
      src/views/system/lighting/modules/eventList.vue
  16. 6 2
      src/views/system/lighting/modules/running.vue
  17. 40 0
      src/views/system/message/index.vue
  18. 185 0
      src/views/system/message/modules/carbonEmission.vue
  19. 103 0
      src/views/system/message/modules/consume.vue
  20. 119 0
      src/views/system/message/modules/running.vue
  21. 193 0
      src/views/system/message/modules/sameDay.vue
  22. 183 0
      src/views/system/message/modules/tiring.vue
  23. 44 0
      src/views/system/tenement/index.vue
  24. 239 0
      src/views/system/tenement/modules/carbonEmission.vue
  25. 103 0
      src/views/system/tenement/modules/consume.vue
  26. 179 0
      src/views/system/tenement/modules/eventList.vue
  27. 120 0
      src/views/system/tenement/modules/running.vue
  28. 215 0
      src/views/system/tenement/modules/sameDay.vue
  29. 183 0
      src/views/system/tenement/modules/tiring.vue
  30. 5 1
      src/views/system/video/modules/consume.vue
  31. 1 1
      src/views/system/video/modules/eventList.vue
  32. 4 1
      src/views/system/video/modules/running.vue
  33. 163 99
      src/views/system/video/modules/sameDay.vue

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "axios": "0.28.1",
     "clipboard": "2.0.11",
     "echarts": "5.5.1",
+    "echarts-gl": "^2.0.9",
     "echarts-liquidfill": "^3.1.0",
     "element-plus": "2.7.6",
     "file-saver": "2.0.5",

BIN
src/assets/images/_btn.png


BIN
src/assets/images/car.jpg


+ 2 - 1
src/layout/components/AppMain.vue

@@ -39,7 +39,8 @@ function addIframe() {
   min-height: 100vh;
   width: 100%;
   position: relative;
-  overflow: auto;
+//   overflow: auto;
+  overflow: hidden;
 }
 
 .hasTagsView {

+ 40 - 0
src/views/system/broadcast/index.vue

@@ -0,0 +1,40 @@
+<template>
+    <div class="_energy">
+     <layout>
+         <template #left>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <deviceList  style="flex:1;"/>
+                <tiring style="flex: 1;"/>
+             </div>
+         </template>
+         <template #content>
+             <p>广播</p>
+         </template>
+         <template #right>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <running style="flex: 1;"/>
+                <sameDay style="flex: 1;"/>
+                <eventList style="flex: 1;"/>
+             </div>
+         </template>
+     </layout>
+    </div>
+ </template>
+ 
+ <script setup name="Role">
+ import layout from "@/components/layout_/index.vue";
+
+ import running from './modules/running.vue'
+ import deviceList from './modules/deviceList.vue'
+ import sameDay from './modules/sameDay.vue'
+ import eventList from './modules/eventList.vue'
+ import tiring from './modules/tiring.vue'
+
+ 
+ </script> 
+ <style lang="scss">
+ ._energy{
+     height: 100%;
+ }
+ </style>
+ 

+ 161 - 0
src/views/system/broadcast/modules/deviceList.vue

@@ -0,0 +1,161 @@
+<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="_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 " truncated style="color: white;flex: .4;display: flex;align-items: center;">
+                        <el-icon color="#168cdb" size="16">
+                            <el-icon><Headset /></el-icon>
+                        </el-icon>
+                        <span class="_table_row1">{{ item.name }}</span>
+                    </el-text>
+                    <el-text class="w-150px mb-2" :class="item.state=='播放'?'_success':''" truncated style="color: #fff;flex: .2;">
+                        {{ item.state }}
+                    </el-text>
+                    <el-text class="w-150px mb-2" :class="item.flag=='在线'?'':'_warning'" truncated style="flex: .2;color: #fff;">
+                        {{ item.flag }}
+                    </el-text>
+                    <el-icon color="#168cdb" style="flex: .2;">
+                        <el-icon><Aim /></el-icon>
+                    </el-icon>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+import { Headset, Aim } from '@element-plus/icons-vue'
+const value = ref('')
+const eventList = ref([
+    { name: '终端1', state: '播放', flag: '在线' },
+    { name: '终端2', state: '空闲', flag: '离线' },
+    { name: '终端3', state: '播放', flag: '在线' },
+    { name: '终端4', state: '空闲', flag: '离线' },
+    { name: '终端5', state: '播放', flag: '在线' },
+    { name: '终端6', state: '空闲', flag: '离线' },
+    { name: '终端7', state: '播放', flag: '在线' },
+    { name: '终端8', state: '空闲', flag: '离线' },
+    { name: '终端9', state: '播放', flag: '在线' },
+    { name: '终端10', state: '空闲', flag: '离线' },
+    { name: '终端11', state: '播放', flag: '在线' },
+    { name: '终端12', state: '空闲', flag: '离线' },
+    { name: '终端13', state: '播放', flag: '在线' },
+    { name: '终端14', state: '空闲', flag: '离线' },
+    { name: '终端15', state: '播放', flag: '在线' },
+    { name: '终端16', state: '空闲', flag: '离线' },
+    { name: '终端17', state: '播放', flag: '在线' },
+    { name: '终端18', state: '空闲', flag: '离线' },
+    { name: '终端19', state: '播放', flag: '在线' },
+    { name: '终端20', state: '空闲', flag: '离线' },
+    { name: '终端21', state: '播放', flag: '在线' },
+    { name: '终端22', state: '空闲', flag: '离线' },
+    { name: '终端23', state: '播放', flag: '在线' },
+    { name: '终端24', state: '空闲', flag: '离线' }
+])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._deviceList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 20);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</script>
+
+<style lang="scss" scoped>
+._success {
+    color: #168cdb !important;	
+}
+._warning {
+    color: rgb(244, 67, 54) !important;	
+}
+._table_row1{
+    margin-left: 10px;
+    text-emphasis: none;
+    /* 新增样式让文本超出显示省略号 */
+    white-space: nowrap; 
+    overflow: hidden; 
+    text-overflow: ellipsis; 
+}
+._deviceList{
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
+    &_mains{
+        margin:10px 30px;
+        overflow: hidden; // 隐藏溢出内容
+        cursor: pointer;
+        font-size: 12px;
+        &_item{
+            display: flex;
+            justify-content: space-between;
+            padding: 10px;
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover{
+            cursor: pointer;
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+</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>

+ 169 - 0
src/views/system/broadcast/modules/eventList.vue

@@ -0,0 +1,169 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag 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.store === '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}}#{{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_eventList_mains_item_btn">
+                        2025-04-15 10:20:00
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+
+const eventList = ref([{
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端设备播放',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端设备播放',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端设备停止',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端设备播放',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端设备停止',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端设备停止',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端设备播放',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端设备停止',
+    time: '2025-04-15 10:20:00'
+}])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._eventList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 50);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</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: #fff;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+</style>

+ 120 - 0
src/views/system/broadcast/modules/running.vue

@@ -0,0 +1,120 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="运行统计"></HeadlineTag>
+
+        <div class="_running_mains">
+            <div class="_running_mains_left">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">785</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'
+import { color } from "echarts";
+const runningList = ref([
+    { name:'使用中', state:'154', color:'#409eff', unit:'台'},
+    { name:'空闲中', state:'15', color:'#15acaa', unit:'台'},
+    { name:'已离线', state:'81', color:'rgb(244, 67, 54)', unit:'台'},
+
+])
+</script>
+<style lang="scss">
+._running {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+        &_left {
+            width: 180px;
+            height: 180px;
+            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;
+                }
+            }
+        }
+
+        
+    }
+}
+@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+</style>

+ 193 - 0
src/views/system/broadcast/modules/sameDay.vue

@@ -0,0 +1,193 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag 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="_eventList_mains_item_text_flag">
+                            <el-icon :color="item.store === 'on' ?'#168cdb':'#FFC107'" :size="20">
+                                <Microphone v-if="item.store === 'on'"/>
+                                <Mute v-else/>
+                            </el-icon>
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: #fff;">
+                            {{index+1}}F#{{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_xian"></div>
+                    <div class="_eventList_mains_item_btn">
+                        <div class="_eventList_mains_item_btn_plays">播放</div>
+                        <div class="_eventList_mains_item_btn_plays">暂停</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+
+const eventList = ref([{
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '终端',
+    time: '2025-04-15 10:20:00'
+}])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._eventList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 50);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</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;
+                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;
+                    margin-right: 10px;
+                }
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+            &_btn{
+                color: #fff;
+                display: flex;
+                font-size: 14px;
+                gap: 10px;
+                &_plays {
+                    border-radius: 5px;
+                    cursor: pointer;
+                    border: 2px solid #2ea9d8;
+                    background: #1970c3;
+                    color: #fff;
+                    padding: 0 5px;
+                }
+            }
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+._xian{
+    flex: 1;
+    border-bottom: 1px dashed #168cdb;
+    margin:0 5%;
+}
+</style>

+ 186 - 0
src/views/system/broadcast/modules/tiring.vue

@@ -0,0 +1,186 @@
+<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 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 weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+
+        const option = {
+            xAxis: {
+                type: 'category',
+                data: weekDays,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                }
+            },
+            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: 'line',
+                    data: generateRandomData(7, 50),
+                    lineStyle: {
+                        color: '#de4337',
+                    },
+                    showSymbol: false,
+                    smooth: true,
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(222, 67, 55, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(222, 67, 55, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+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>

+ 4 - 0
src/views/system/elevator/modules/consume.vue

@@ -51,7 +51,11 @@ const runningList = ref([
     // animation: scanning 10s linear infinite;
 }
 ._runnings {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;

+ 6 - 1
src/views/system/elevator/modules/running.vue

@@ -37,9 +37,13 @@ const runningList = ref([
     { name:'运行异常', state:'15', color:'#15acaa', unit:'台'},
 ])
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 ._running {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;
@@ -47,6 +51,7 @@ const runningList = ref([
             width: 180px;
             height: 180px;
             position: relative;
+            flex-shrink: 0;
             &_tuan{
                 width: 100%;
                 height: 100%;

+ 6 - 1
src/views/system/energy/modules/running.vue

@@ -38,9 +38,13 @@ const runningList = ref([
 
 ])
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 ._running {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;
@@ -48,6 +52,7 @@ const runningList = ref([
             width: 180px;
             height: 180px;
             position: relative;
+            flex-shrink: 0;
             &_tuan{
                 width: 100%;
                 height: 100%;

+ 6 - 1
src/views/system/energy/modules/sameDay.vue

@@ -47,9 +47,13 @@ const generateRandomData = (length, max) => {
     return randomData;
 };
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 ._sameDay {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;
@@ -57,6 +61,7 @@ const generateRandomData = (length, max) => {
             width: 180px;
             height: 180px;
             position: relative;
+            flex-shrink: 0;
             &_tuan{
                 width: 100%;
                 height: 100%;

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

@@ -101,7 +101,7 @@ onUnmounted(() => {
 })
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 ._success {
     background: #15acaa;	
 }

+ 6 - 2
src/views/system/lighting/modules/running.vue

@@ -5,7 +5,6 @@
         <div class="_running_mains">
             <div class="_running_mains_left">
                 <div class="_running_mains_left_tuan"></div>
-
                 <div class="_running_mains_left_conter">
                     <div class="_running_mains_left_conter_num">62</div>
                     <div class="_running_mains_left_conter_text">设备总数</div>
@@ -37,9 +36,13 @@ const runningList = ref([
     { name:'故障数量', state:'0', color:'#F44336'}
 ])
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 ._running {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;
@@ -47,6 +50,7 @@ const runningList = ref([
             width: 150px;
             height: 150px;
             position: relative;
+            flex-shrink: 0;
             &_tuan{
                 width: 100%;
                 height: 100%;

+ 40 - 0
src/views/system/message/index.vue

@@ -0,0 +1,40 @@
+<template>
+    <div class="_energy">
+     <layout>
+         <template #left>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <running style="flex: 1;"/>
+                <carbonEmission style="flex:1;"/>
+             </div>
+         </template>
+         <template #content>
+             <p>信息</p>
+         </template>
+         <template #right>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <consume  style="flex:1;"/>
+                <tiring style="flex: 1;"/>
+                <sameDay style="flex: 1;"/>
+             </div>
+         </template>
+     </layout>
+    </div>
+ </template>
+ 
+ <script setup name="Role">
+ 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'
+
+ 
+ </script> 
+ <style lang="scss">
+ ._energy{
+     height: 100%;
+ }
+ </style>
+ 

+ 185 - 0
src/views/system/message/modules/carbonEmission.vue

@@ -0,0 +1,185 @@
+<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 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 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}%)',
+                backgroundColor: 'rgba(0, 0, 0, 0.8)',
+                textStyle: {
+                    color: '#fff'
+                }
+            },
+            // 添加图例配置
+            legend: {
+                orient: 'horizontal', // 图例水平排列
+                bottom: '0%', // 增大距离底部的距离,可根据需求调整
+                left: 'center', // 水平居中
+                data: infoTypes,
+                textStyle: {
+                    color: '#fff'
+                }
+            },
+            series: [
+                {
+                    name: '信息类型',
+                    type: 'pie',
+                    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);
+});
+
+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>

+ 103 - 0
src/views/system/message/modules/consume.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="_runnings">
+        <HeadlineTag value="信息类型占比"></HeadlineTag>
+
+        <div class="_runnings_mains">
+            <div class="_runnings_mains_left">
+                <div class="_runnings_mains_left_tuan tuan1"></div>
+                <div class="_runnings_mains_left_conter">
+                    <div class="_runnings_mains_left_conter_num">75</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">54</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">18</div>
+                    <div class="_runnings_mains_left_conter_text">活动预告</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const runningList = ref([
+    { name:'入侵检测', state:'154', color:'#409eff',tip:'划定区域人员闯入'},
+    { name:'异常行为', state:'15', color:'#15acaa',tip:'徘徊、倒地、聚集'}, 
+    { name:'丢失告警', state:'134', color:'#FFC107',tip:'物品遗留/丢失告警'},
+])
+</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;
+    flex-direction: column;
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        &_left {
+            flex-shrink: 0;
+            width: 140px;
+            height: 140px;
+            position: relative;
+            &_tuan{
+                width: 100%;
+                height: 100%;
+                background-size: 80% 80%;
+                background-position: center;
+                background-repeat: no-repeat;
+            }
+            &_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;
+                }	
+            }
+        }
+        
+    }
+}
+@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+</style>

+ 119 - 0
src/views/system/message/modules/running.vue

@@ -0,0 +1,119 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="实时设备统计"></HeadlineTag>
+
+        <div class="_running_mains">
+            <div class="_running_mains_left">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">38</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'
+import { color } from "echarts";
+const runningList = ref([
+    { name:'正常', state:'20', color:'#409eff', unit:'台'},
+    { name:'故障', state:'18', color:'rgb(244, 67, 54)', unit:'台'},
+    { name:'空闲', state:'15', color:'#15acaa', unit:'台'},
+])
+</script>
+<style lang="scss">
+._running {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+        &_left {
+            width: 180px;
+            height: 180px;
+            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;
+                }
+            }
+        }
+
+        
+    }
+}
+@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+</style>

+ 193 - 0
src/views/system/message/modules/sameDay.vue

@@ -0,0 +1,193 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag 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="_eventList_mains_item_text_flag">
+                            <!-- <el-icon color="#168cdb" :size="20">
+                                <Monitor/>
+                            </el-icon> -->
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: #fff;">
+                            {{index+1}}F#{{ item.name }}
+                        </el-text>
+                    </div>
+                    <div class="_xian"
+                    :class="item.store=='on'?'_success':'_warning'"
+                    >{{item.store=='on'?'播放':'空闲'}}</div>
+                    <div class="_eventList_mains_item_btn">
+                        <div class="_eventList_mains_item_btn_plays">播放</div>
+                        <div class="_eventList_mains_item_btn_plays">暂停</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+
+const eventList = ref([{
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '栋显示大屏',
+    time: '2025-04-15 10:20:00'
+}])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._eventList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 50);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</script>
+
+<style lang="scss" scoped>
+
+._success {
+    color: #15acaa !important;	
+}
+._warning {
+    color: #FFC107 !important;	
+}
+._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;
+                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;
+                    margin-right: 10px;
+                    background: #168cdb;
+                }
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+            &_btn{
+                color: #fff;
+                display: flex;
+                font-size: 14px;
+                gap: 10px;
+                &_plays {
+                    border-radius: 5px;
+                    cursor: pointer;
+                    border: 2px solid #2ea9d8;
+                    background: #1970c3;
+                    color: #fff;
+                    padding: 0 5px;
+                }
+            }
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+._xian{
+    font-size: 12px;
+}
+</style>

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

@@ -0,0 +1,183 @@
+<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 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'
+                    }
+                }
+            },
+            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,
+                    // 设置柱子宽度
+                    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);
+});
+
+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>

+ 44 - 0
src/views/system/tenement/index.vue

@@ -0,0 +1,44 @@
+<template>
+    <div class="_energy">
+     <layout>
+         <template #left>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <consume  style="flex:1;"/>
+                <running style="flex: 1;"/>
+                <carbonEmission style="flex:1;"/>
+
+             </div>
+         </template>
+         <template #content>
+             <p>物业</p>
+         </template>
+         <template #right>
+            <div style="height: 100%;display: flex;flex-direction: column;overflow: hidden;">
+                <eventList style="flex: 1;"/>
+                <sameDay style="flex: 1;"/>
+                <tiring style="flex: 1;"/>
+
+             </div>
+         </template>
+     </layout>
+    </div>
+ </template>
+ 
+ <script setup name="Role">
+ 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 eventList from './modules/eventList.vue'
+ import tiring from './modules/tiring.vue'
+
+ 
+ </script> 
+ <style lang="scss">
+ ._energy{
+     height: 100%;
+ }
+ </style>
+ 

+ 239 - 0
src/views/system/tenement/modules/carbonEmission.vue

@@ -0,0 +1,239 @@
+<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 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 weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+
+        const option = {
+            xAxis: {
+                type: 'category',
+                data: weekDays,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                }
+            },
+            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: generateRandomData(7, 50),
+                    barWidth: '30%',
+                    itemStyle: {
+                        color: {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 0,
+                            y2: 1,
+                            colorStops: [{
+                                offset: 0, color: '#78befe' // 0% 处的颜色
+                            }, {
+                                offset: 1, color: '#0352fb' // 100% 处的颜色
+                            }],
+                            global: false // 缺省为 false
+                        },
+                        barBorderRadius: [10, 10, 10,10]
+                    },
+                },
+                // 曲线表示运行异常
+                {
+                    name: '刷卡进入',
+                    type: 'line',
+                    data: generateRandomData(7, 50),
+                    lineStyle: {
+                        color: '#3b90d7',
+                    },
+                    showSymbol: false,
+                    smooth: true,
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(59,144,215, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(59,144,215, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                },
+                {
+                    name: '远程开门',
+                    type: 'line',
+                    data: generateRandomData(7, 50),
+                    lineStyle: {
+                        color: '#ffc107',
+                    },
+                    showSymbol: false,
+                    smooth: true,
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(255, 193, 7, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(255, 193, 7, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+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>

+ 103 - 0
src/views/system/tenement/modules/consume.vue

@@ -0,0 +1,103 @@
+<template>
+    <div class="_runnings">
+        <HeadlineTag value="统计数据"></HeadlineTag>
+
+        <div class="_runnings_mains">
+            <div class="_runnings_mains_left">
+                <div class="_runnings_mains_left_tuan tuan1"></div>
+                <div class="_runnings_mains_left_conter">
+                    <div class="_runnings_mains_left_conter_num">785</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">54</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">18</div>
+                    <div class="_runnings_mains_left_conter_text">车位数</div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+const runningList = ref([
+    { name:'入侵检测', state:'154', color:'#409eff',tip:'划定区域人员闯入'},
+    { name:'异常行为', state:'15', color:'#15acaa',tip:'徘徊、倒地、聚集'}, 
+    { name:'丢失告警', state:'134', color:'#FFC107',tip:'物品遗留/丢失告警'},
+])
+</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;
+    flex-direction: column;
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        &_left {
+            flex-shrink: 0;
+            width: 140px;
+            height: 140px;
+            position: relative;
+            &_tuan{
+                width: 100%;
+                height: 100%;
+                background-size: 80% 80%;
+                background-position: center;
+                background-repeat: no-repeat;
+            }
+            &_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;
+                }	
+            }
+        }
+        
+    }
+}
+@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+</style>

+ 179 - 0
src/views/system/tenement/modules/eventList.vue

@@ -0,0 +1,179 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag 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.store === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag">
+                            {{ item.store === 'on'?'进':'出' }}
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div :style="{color:item.store === 'on'?'#15acaa':'#FFC107','font-size':'12px'}">
+                        {{ item.store === 'on'?'进入':'出入' }}
+                    </div>
+                    <div class="_eventList_mains_item_btn">
+                        2025-04-15 10:20:00
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+
+const eventList = ref([{
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '赵**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '陈**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '王**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '陈**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '李**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '陈**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '张**',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '杨**',
+    time: '2025-04-15 10:20:00'
+}])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._eventList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 50);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</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: 30px;
+                    height: 30px;
+                    border-radius: 50%;
+                    margin-left: 10px; 
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #fff;
+                    font-size: 14px;
+                }
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+            &_btn{
+                color: #fff;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+</style>

+ 120 - 0
src/views/system/tenement/modules/running.vue

@@ -0,0 +1,120 @@
+<template>
+    <div class="_running">
+        <HeadlineTag value="年龄分布"></HeadlineTag>
+
+        <div class="_running_mains">
+            <div class="_running_mains_left">
+                <div class="_running_mains_left_tuan"></div>
+                <div class="_running_mains_left_conter">
+                    <div class="_running_mains_left_conter_num">12212</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'
+import { color } from "echarts";
+const runningList = ref([
+    { name:'30岁以下', state:'154', color:'#409eff', unit:'人'},
+    { name:'40岁以下', state:'15', color:'#15acaa', unit:'人'},
+    { name:'50岁以下', state:'15', color:'#FFC107', unit:'人'},
+    { name:'60岁以上', state:'15', color:'rgb(244, 67, 54)', unit:'人'},
+])
+</script>
+<style lang="scss">
+._running {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    &_mains {
+        flex: 1;
+        margin: 30px;
+        display: flex;
+        align-items: center;
+        &_left {
+            width: 180px;
+            height: 180px;
+            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;
+                }
+            }
+        }
+
+        
+    }
+}
+@keyframes scanning {
+		to {
+			transform: rotate(1turn);
+		}
+	}
+</style>

+ 215 - 0
src/views/system/tenement/modules/sameDay.vue

@@ -0,0 +1,215 @@
+<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 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 weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
+
+        const option = {
+            xAxis: {
+                type: 'category',
+                data: weekDays,
+                axisLabel: {
+                    color: '#fff'
+                },
+            },
+            tooltip: {
+                trigger: 'axis',
+                axisPointer: {
+                    type: 'cross',
+                    crossStyle: {
+                        color: '#999'
+                    }
+                }
+            },
+            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: 'line',
+                    data: generateRandomData(7, 50),
+                    lineStyle: {
+                        color: '#3b90d7',
+                    },
+                    showSymbol: false,
+                    smooth: true,
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(59,144,215, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(59,144,215, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                },
+                {
+                    name: '车辆出库(辆)',
+                    type: 'line',
+                    data: generateRandomData(7, 50),
+                    lineStyle: {
+                        color: '#de4337',
+                    },
+                    showSymbol: false,
+                    smooth: true,
+                    areaStyle: {
+                        color: new echarts.graphic.LinearGradient(
+                            0,
+                            0,
+                            0,
+                            1,
+                            [{
+                                offset: 0,
+                                color: 'rgba(222, 67, 55, .6)',
+                            },
+                            {
+                                offset: 0.8,
+                                color: 'rgba(222, 67, 55, .2)',
+                            },
+                            ],
+                            false
+                        ),
+                        shadowColor: 'rgba(0, 0, 0, 0.1)',
+                        shadowBlur: 10,
+                    },
+                }
+            ]
+        };
+
+        chart.setOption(option);
+    }
+    window.addEventListener('resize', handleResize);
+});
+
+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>

+ 183 - 0
src/views/system/tenement/modules/tiring.vue

@@ -0,0 +1,183 @@
+<template>
+    <div class="_eventList">
+        <HeadlineTag 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.store === 'on' ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag">
+                            {{ item.store === 'on'?'进':'出' }}
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.name }}
+                        </el-text>
+                    </div>
+                    <div :style="{color:item.store === 'on'?'#15acaa':'#FFC107','font-size':'12px'}">
+                        {{ item.store === 'on'?'进入':'离开' }}
+                    </div>
+                    <div>
+                        <img src="@/assets/images/car.jpg" style="width: 30px;height: 30px;border-radius: 5px;" srcset="">
+                    </div>
+                    <div class="_eventList_mains_item_btn">
+                        2025-04-15 10:20:00
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted } from "vue";
+import HeadlineTag from '@/components/HeadlineTag'
+
+const eventList = ref([{
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'on',
+    color: 'rgb(82.4, 155.2, 46.4)',
+    name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}, {
+    store: 'off',
+    color: 'red',
+   name: '贵A.CL888',
+    time: '2025-04-15 10:20:00'
+}])
+
+const mainsRef = ref(null)
+const scrollY = ref(0)
+let intervalId = null
+const scrollSpeed = 1 // 滚动速度
+
+const startCarousel = () => {
+    intervalId = setInterval(() => {
+        const itemHeight = mainsRef.value?.querySelector('._eventList_mains_item')?.offsetHeight;
+        if (!itemHeight) return;
+
+        scrollY.value -= scrollSpeed;
+
+        // 检查第一个元素是否完全离开视口
+        if (Math.abs(scrollY.value) >= itemHeight) {
+            // 将第一个元素移到列表末尾
+            const firstItem = eventList.value.shift();
+            if (firstItem) {
+                eventList.value.push(firstItem);
+            }
+            // 调整滚动位置
+            scrollY.value += itemHeight;
+        }
+    }, 50);
+};
+
+const pauseCarousel = () => {
+    clearInterval(intervalId);
+};
+
+const resumeCarousel = () => {
+    startCarousel();
+};
+
+onMounted(() => {
+    startCarousel();
+});
+
+onUnmounted(() => {
+    clearInterval(intervalId);
+});
+</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;
+            justify-content: space-between;
+            padding: 10px 0;
+
+            &_text {
+                display: flex;
+                justify-content: space-between;
+                align-items: center;
+                &_flag {
+                    width: 30px;
+                    height: 30px;
+                    border-radius: 50%;
+                    margin-left: 10px; 
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #fff;
+                    font-size: 14px;
+                }
+                &_p {
+                    margin-left: 10px;
+                }
+            }
+            &_btn{
+                color: #fff;
+                display: flex;
+                font-size: 14px;
+                gap: 20px;
+                &_item {
+                    border-radius: 5px;
+                    cursor: pointer;
+                }
+            }
+        }
+        &_item:nth-child(even){
+            background: rgba($color: #168cdb, $alpha: .05);
+        }
+        &_item:hover {
+            background-image: linear-gradient(to right, #168cdb, transparent); 
+        }
+    }
+}
+</style>

+ 5 - 1
src/views/system/video/modules/consume.vue

@@ -53,7 +53,7 @@ const runningList = ref([
     { name:'丢失告警', state:'134', color:'#FFC107',tip:'物品遗留/丢失告警'},
 ])
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 .tuan1 {
     background: url("@/assets/images/video_bg_1.png");	
     animation: scanning 5s linear infinite;
@@ -67,7 +67,11 @@ const runningList = ref([
     animation: scanning 5s linear infinite;
 }
 ._runnings {
+    display: flex;
+    align-items: center;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;

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

@@ -114,7 +114,7 @@ onUnmounted(() => {
 });
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 ._success {
     background: #15acaa;	
 }

+ 4 - 1
src/views/system/video/modules/running.vue

@@ -39,9 +39,12 @@ const runningList = ref([
     { name:'存储剩余', state:'30', color:'#F44336', unit:'TB'},
 ])
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 ._running {
+    display: flex;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;

+ 163 - 99
src/views/system/video/modules/sameDay.vue

@@ -1,51 +1,168 @@
 <template>
     <div class="_runnin">
-        <HeadlineTag value="车牌识别统计"></HeadlineTag>
+        <HeadlineTag value="性能统计"></HeadlineTag>
         <div class="_runnin_mains">
-            <div class="_runnin_mains_left _left_1">
-                <div class="_runnin_mains_left_tuan tuan_1"></div>
-                <div class="_runnin_mains_left_conter">
-                    <div class="_runnin_mains_left_conter_num">785</div>
-                    <div class="_runnin_mains_left_conter_text">今日车牌识别</div>
-                </div>
+            <!-- 修正引用名称,与脚本里保持一致 -->
+            <div class="_runnin_mains_item">
+                <div ref="chartRefCPU" style="width: 140px;height: 140px;"></div>
+                <span style="margin-top: 10px;font-weight: bold;">CPU</span>
             </div>
-            <div class="_runnin_mains_left _left_2">
-                <div class="_runnin_mains_left_tuan tuan_2"></div>
-                <div class="_runnin_mains_left_conter">
-                    <div class="_runnin_mains_left_conter_num">14</div>
-                    <div class="_runnin_mains_left_conter_text">黑名单</div>
-                </div>
+            <div class="_runnin_mains_item">
+                <div ref="chartRefRAM" style="width: 140px;height: 140px;"></div>
+                <span style="margin-top: 10px;font-weight: bold;">RAM</span>
             </div>
-            
-            <!-- <div class="_runnin_mains_right">
-                <div class="_runnin_mains_right_item" v-for="item,index in runningList" :key="index">
-                    <div class="_runnin_mains_right_item_tuan">
-                        <span class="_runnin_mains_right_item_tuan_flag" :style="{backgroundColor: item.color}"></span>
-                        <el-tooltip :content="item.tip" placement="top" effect="light">
-                            <el-text class="w-150px mb-2" truncated style="color: #ccc;">
-                                {{item.name}}
-                            </el-text>
-                        </el-tooltip>
-                    </div>
-                    <div class="_runnin_mains_right_item__txt">
-                        {{item.state}}
-                    </div>
-                </div>
-            </div> -->
         </div>
     </div>
 </template>
 
 <script setup>
-import { ref } from "vue";
+import { ref, onMounted, nextTick } from "vue";
+import * as echarts from 'echarts'
+import 'echarts-liquidfill';
 import HeadlineTag from '@/components/HeadlineTag'
-const runningList = ref([
-    { name:'入侵检测', state:'154', color:'#409eff',tip:'划定区域人员闯入'},
-    { name:'异常行为', state:'15', color:'#15acaa',tip:'徘徊、倒地、聚集'}, 
-    { name:'丢失告警', state:'134', color:'#FFC107',tip:'物品遗留/丢失告警'},
-])
+const chartRefCPU = ref(null);
+const chartRefRAM = ref(null);
+let chartInstanceCPU = null;
+let chartInstanceRAM = null;
+
+const initCPUDrop = () => {
+    // 检查 DOM 元素是否存在
+    if (chartRefCPU.value) {
+        var value = 0.45;
+        chartInstanceCPU = echarts.init(chartRefCPU.value);
+        chartInstanceCPU.setOption({
+            series: [
+                {
+                    type: 'liquidFill', //水位图
+                    radius: '100%', //显示比例
+                    center: ['50%', '50%'], //中心点
+                    amplitude: 20, //水波振幅
+                    data: [value, value], // data个数代表波浪数
+                    color: [
+                        {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 0,
+                            y2: 1,
+                            colorStops: [
+                                {
+                                    offset: 0,
+                                    color: '#446bf5',
+                                },
+                                {
+                                    offset: 1,
+                                    color: '#2ca3e2',
+                                },
+                            ],
+                            globalCoord: false,
+                        },
+                    ], //波浪颜色
+                    backgroundStyle: {
+                        borderWidth: 1, //外边框
+                        color: 'RGBA(51, 66, 127, 0.7)',
+                    },
+                    label: {
+                        //标签设置
+                        position: ['50%', '45%'],
+                        formatter: '50%', //显示文本,
+                        textStyle: {
+                            // 修改字体大小,例如改为 20px
+                            fontSize: '20px', 
+                            color: '#fff',
+                        },
+                    },
+                    outline: {
+                        borderDistance: 0,
+                        itemStyle: {
+                            borderWidth: 2,
+                            borderColor: '#112165',
+                        },
+                    },
+                },
+            ],
+        });
+    }
+}
+const initRAMDrop = () => {
+    // 检查 DOM 元素是否存在
+    if (chartRefRAM.value) {
+        var value = 0.65;
+        chartInstanceRAM = echarts.init(chartRefRAM.value);
+        chartInstanceRAM.setOption({
+            series: [
+                {
+                    type: 'liquidFill', //水位图
+                    radius: '100%', //显示比例
+                    center: ['50%', '50%'], //中心点
+                    amplitude: 20, //水波振幅
+                    data: [value, value], // data个数代表波浪数
+                    // 修改波浪颜色
+                    color: [
+                        {
+                            type: 'linear',
+                            x: 0,
+                            y: 0,
+                            x2: 0,
+                            y2: 1,
+                            colorStops: [
+                                {
+                                    offset: 0,
+                                    // 例如改成红色系
+                                    color: '#ff4444', 
+                                },
+                                {
+                                    offset: 1,
+                                    // 例如改成橙色系
+                                    color: '#ff8800', 
+                                },
+                            ],
+                            globalCoord: false,
+                        },
+                    ], 
+                    backgroundStyle: {
+                        borderWidth: 1, //外边框
+                        color: 'RGBA(51, 66, 127, 0.7)',
+                    },
+                    label: {
+                        //标签设置
+                        position: ['50%', '45%'],
+                        // 修改 formatter 为函数,动态显示百分比
+                        formatter: () => `${Math.round(value * 100)}%`, 
+                        textStyle: {
+                            // 修改字体大小,例如改为 20px
+                            fontSize: '20px', 
+                            color: '#fff',
+                        },
+                    },
+                    outline: {
+                        borderDistance: 0,
+                        itemStyle: {
+                            borderWidth: 2,
+                            borderColor: '#112165',
+                        },
+                    },
+                },
+            ],
+        });
+    }
+}
+// 生命周期
+onMounted(() => {
+    nextTick(() => {
+        initCPUDrop()
+        initRAMDrop()
+    })
+});
+// 窗口自适应
+window.addEventListener('resize', () => {
+    chartInstanceRAM?.resize();
+    chartInstanceCPU?.resize();
+});
 </script>
+
 <style lang="scss" scoped>
+// 样式部分保持不变
 .tuan_1 {
     background: url("@/assets/images/video_he.png");
     animation: scanning 5s linear infinite;
@@ -61,75 +178,22 @@ const runningList = ref([
     color: #de4034 !important;	
 }
 ._runnin {
+    display: flex;
+    flex-direction: column;
     &_mains {
+        flex: 1;
         margin: 30px;
         display: flex;
         align-items: center;
-        justify-content: center;
-        &_left {
-            flex-shrink: 0;
-            width: 200px;
-            height: 200px;
-            position: relative;
-            &_tuan{
-                width: 100%;
-                height: 100%;
-                background-size: 80% 80%;
-                background-position: center;
-                background-repeat: no-repeat;
-            }
-            &_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;
-                &_num {
-                    font-size:22px;
-                    font-weight: 500;
-                }
-                &_text {
-                    font-size: 12px;
-                }	
-            }
+        justify-content: space-around;
+        gap: 30px;
+        &_item {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            flex-direction: column;
+            color: #FFF;
         }
-        &_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>