AaronBruin 2 months ago
parent
commit
9d6867b852

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

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747292322459" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3630" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><path d="M512 2C231.5 2 2 231.5 2 512s229.5 510 510 510 510-229.5 510-510S792.5 2 512 2z m204 561H308c-30.6 0-51-20.4-51-51s20.4-51 51-51h408c30.6 0 51 20.4 51 51s-20.4 51-51 51z m0 0" p-id="3631"></path></svg>

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

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747291346423" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3063" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><path d="M511.860364 0.139636A511.813818 511.813818 0 1 0 1023.744 512 511.744 511.744 0 0 0 511.860364 0.139636zM716.8 706.187636a45.149091 45.149091 0 0 1-63.488 6.4L476.346182 567.854545a44.916364 44.916364 0 0 1-16.570182-34.90909v-211.525819a45.102545 45.102545 0 1 1 90.205091 0v190.045091l160.581818 131.351273a45.102545 45.102545 0 0 1 6.283636 63.394909z m0 0" p-id="3064"></path></svg>

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

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747291351400" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3220" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><path d="M744.704 300.672l-308.096 340.736L305.792 512a37.248 37.248 0 0 0-52.608 52.608l158.272 158.272a37.248 37.248 0 0 0 54.016 0l334.656-369.6a38.144 38.144 0 1 0-55.424-52.608zM512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024z" p-id="3221"></path></svg>

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

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747291333065" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2441" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><path d="M488.064 77.312v457.472h456.064C944 787.328 739.84 992 488.064 992 236.16 992 32 787.2 32 534.656 32 282.048 236.16 77.312 488.064 77.312zM564.096 32c226.496 32.448 403.84 207.744 427.904 426.496H564.032V32z" p-id="2442"></path></svg>

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

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747291339328" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2752" xmlns:xlink="http://www.w3.org/1999/xlink" width="81" height="81"><path d="M512 13.74435555C236.82161778 13.74435555 13.74435555 236.82161778 13.74435555 512S236.82161778 1010.25564445 512 1010.25564445 1010.25564445 787.17838222 1010.25564445 512 787.17838222 13.74435555 512 13.74435555zM843.94666667 447.71555555c-4.49991111 0-8.86670222-0.57116445-13.03096889-1.64522666L660.77582222 616.21134222c0.54385778 3.01283555 0.84195555 6.11214222 0.84195556 9.28199111 0 28.74936889-23.30396445 52.05333333-52.05333333 52.05333334S557.51111111 654.24270222 557.51111111 625.49333333c0-3.75239111 0.40504889-7.40807111 1.16053334-10.93518222l-140.96839112-145.03253333a51.95093333 51.95093333 0 0 1-18.05880888 3.22218667 51.89176889 51.89176889 0 0 1-20.11477334-4.03228445L217.72743111 630.51548445c0.46876445 2.80917333 0.72590222 5.68888889 0.72590222 8.63118222 0 28.74936889-23.30510222 52.05333333-52.05333333 52.05333333s-52.05333333-23.30396445-52.05333333-52.05333333S137.65176889 587.09333333 166.4 587.09333333c6.19861333 0 12.14236445 1.08885333 17.65717333 3.07655112l163.84227556-163.84227556a52.64611555 52.64611555 0 0 1-0.30833778-5.63427556c0-28.74823111 23.30510222-52.05333333 52.05333334-52.05333333S451.69777778 391.94510222 451.69777778 420.69333333c0 2.74545778-0.21617778 5.44085333-0.62577778 8.07139556l142.92423111 147.04526222A52.00554667 52.00554667 0 0 1 609.56444445 573.44c5.98584889 0 11.73048889 1.02286222 17.08259555 2.88199111l167.05422222-167.05422222A52.09088 52.09088 0 0 1 791.89333333 395.66222222c0-28.74823111 23.30396445-52.05333333 52.05333334-52.05333333s52.05333333 23.30510222 52.05333333 52.05333333-23.30396445 52.05333333-52.05333333 52.05333333z"  p-id="2753"></path></svg>

+ 0 - 320
src/views/ce.html

@@ -1,320 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Document</title>
-    <style>
-        .card_floor_control {
-            user-select: none;
-            position: absolute;
-            top: 100px;
-            right: calc(25% + 10px);
-            width: 100px;
-            height: 40px;
-        }
-
-        .floor_control {
-            position: absolute;
-            cursor: pointer;
-            top: 0px;
-            right: 0px;
-            width: 100px;
-            height: 40px;
-            background-color: rgba(7, 27, 60, 0.8);
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            color: #ffffff;
-            border-radius: 4px;
-        }
-
-        .triangle {
-            position: relative;
-            width: 15px;
-            height: 15px;
-            margin-left: 15px;
-        }
-
-        .triangle:after {
-            content: '';
-            display: inline-block;
-            position: absolute;
-            top: 0;
-            width: 10px;
-            height: 10px;
-            border-top: 1px solid #ffffff;
-            border-right: 1px solid #ffffff;
-            transform: rotate(135deg);
-            -webkit-transform: rotate(135deg);
-        }
-
-        .all_building_floor {
-            position: absolute;
-            top: 50px;
-            right: 0px;
-            flex-direction: column;
-        }
-
-        .box_item_bf {
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            font-size: 18px;
-        }
-
-        .building_box {
-            cursor: pointer;
-            padding: 10px 20px;
-            background-color: rgba(70, 133, 201, 0.3);
-        }
-
-        .building_box:hover {
-            background-color: rgba(34, 91, 153, 0.3);
-        }
-
-        .floor_box {
-            cursor: pointer;
-            color: rgb(108, 111, 113);
-            --c: rgb(108, 111, 113);
-            --n: 4;
-            --t: 2px;
-            --d: 45deg;
-            aspect-ratio: 1;
-            width: 60px;
-            height: 60px;
-            border-radius: 50%;
-            background-color: rgb(1, 6, 19);
-            margin-bottom: 10px;
-        }
-
-        .floor_box::after {
-            content: "";
-            position: absolute;
-            width: 50px;
-            height: 50px;
-            border-radius: 50%;
-            padding: var(--t);
-            background: var(--c);
-            -webkit-mask:
-                linear-gradient(#0000 0 0) content-box,
-                repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
-            -webkit-mask-composite: source-in;
-            mask:
-                linear-gradient(#0000 0 0) content-box,
-                repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
-            mask-composite: intersect;
-            /* // animation: rotate 5s linear infinite; */
-        }
-
-        .activate_box {
-            cursor: pointer;
-            color: #fff;
-            --c: #fff;
-            --n: 4;
-            --t: 2px;
-            --d: 45deg;
-            aspect-ratio: 1;
-            width: 60px;
-            height: 60px;
-            border-radius: 50%;
-            background-color: rgb(1, 6, 19);
-            margin-bottom: 10px;
-        }
-
-        .activate_box::after {
-            content: "";
-            position: absolute;
-            width: 50px;
-            height: 50px;
-            border-radius: 50%;
-            padding: var(--t);
-            background: var(--c);
-            -webkit-mask:
-                linear-gradient(#0000 0 0) content-box,
-                repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
-            -webkit-mask-composite: source-in;
-            mask:
-                linear-gradient(#0000 0 0) content-box,
-                repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));
-            mask-composite: intersect;
-            animation: rotate 5s linear infinite;
-        }
-
-        @keyframes rotate {
-            from {
-                transform: rotate(0deg);
-            }
-
-            to {
-                transform: rotate(360deg);
-            }
-        }
-    </style>
-</head>
-
-<body>
-    <div class="card_floor_control">
-        <div class="floor_control" id="floorControl">全部<span class="triangle"></span></div>
-        <!-- <div class="floor_control" @click="switchover">{{ currentItem }}<span class="triangle"></span></div> -->
-        <div class="all_building_floor" id="allBuildingFloor">
-            <!-- <div class="box_item_bf" :class="currentId == item.id ? 'activate_box' : 'floor_box'"
-                v-for="(item, index) in currentData" :key="index" @click="switchoverItem(item)">
-                {{ item.title }}
-            </div> -->
-        </div>
-    </div>
-    <script>
-        let currentId = 'all';
-        let currentItem = '全部';
-        let showList = false;
-        const arrData = [{
-            title: '全部',
-            id: 'all',
-        }, {
-            title: 'A栋',
-            id: 'A',
-            childList: [{
-                title: '3F',
-                id: 'A3',
-            }, {
-                title: '2F',
-                id: 'A2',
-            }, {
-                title: '1F',
-                id: 'A1',
-            }, {
-                title: '全部',
-                id: 'omn',
-            }]
-        }, {
-            title: 'B栋',
-            id: 'B',
-            childList: [{
-                title: '3F',
-                id: 'B3',
-            }, {
-                title: '2F',
-                id: 'B2',
-            }, {
-                title: '1F',
-                id: 'B1',
-            }]
-        }, {
-            title: 'C栋',
-            id: 'C',
-            childList: [{
-                title: '4F',
-                id: 'C4',
-            }, {
-                title: '3F',
-                id: 'C3',
-            }, {
-                title: '2F',
-                id: 'C2',
-            }, {
-                title: '1F',
-                id: 'C1',
-            }]
-        }, {
-            title: 'D栋',
-            id: 'D',
-            childList: [{
-                title: '2F',
-                id: 'D2',
-            }, {
-                title: '1F',
-                id: 'D1',
-            }]
-        }]
-        const floorControl = document.getElementById('floorControl');
-        const allBuildingFloor = document.getElementById('allBuildingFloor');
-        floorControl.addEventListener('click', () => {
-            currentId = 'all';
-            allBuildingFloor.style.display = 'flex';
-            renderList(true);
-        });
-        let listData = [];
-        // 渲染列表区域  
-        function renderList(flag) {
-            allBuildingFloor.innerHTML = '';
-            if (currentId === 'all') {
-                currentTitle = '全部';
-                floorControl.childNodes[0].nodeValue = currentTitle;
-                listData = arrData;
-            } else {
-                if (flag) {
-                    listData = arrData;
-                } else {
-                    function find(list, id) {
-                        return list && id ? (function filter(arr) {
-                            for (var i = 0; i < arr.length; ++i) {
-                                var itemId = arr[i].id;
-                                var children = arr[i].childList || [];
-                                var _ret = arr[i];
-                                if (id === itemId) {
-                                    return _ret;
-                                } else if (children.length) {
-                                    _ret = filter(children);
-                                    if (_ret) return _ret;
-                                }
-                            }
-                            return null;
-                        })(list) : null;
-                    }
-                    const currentBuilding = find(arrData, currentId);
-                    if (currentBuilding && currentBuilding.childList) {
-                        listData = currentBuilding.childList;
-                    }
-                }
-            }
-            let allFlag = true
-            listData.forEach(item => {
-                const div = document.createElement('div');
-                div.classList.add('box_item_bf');
-                if (item.id === currentId) {
-                    allFlag = false
-                    div.classList.add('activate_box');
-
-                } else {
-                    if (allFlag && item.id == 'omn') {
-                        div.classList.add('activate_box');
-                    }
-                    console.log(allFlag, item.id, 2);
-
-                    div.classList.add('floor_box');
-                }
-                div.textContent = item.title;
-                div.addEventListener('click', (e) => {
-                    e.stopPropagation();  // 阻止事件冒泡,避免触发外层点击  
-                    switchoverItem(item);
-                });
-                allBuildingFloor.appendChild(div);
-            });
-        }
-        // 切换选中项  
-        function switchoverItem(item) {
-            // 如果点了目前已经选中的项,列表隐藏  
-            if (item.id === currentId) {
-                showList = false;
-                return;
-            }
-            currentId = item.id;
-            currentTitle = item.title;
-            floorControl.childNodes[0].nodeValue = currentTitle;
-            if (currentId === 'all') {
-                renderList(false);
-            } else {
-                const isTopLevel = arrData.some(it => it.id === currentId);
-                if (isTopLevel) {
-                    renderList(false);
-                } else {
-                    renderList(false);
-                }
-            }
-        }
-    </script>
-</body>
-
-</html>

+ 0 - 297
src/views/dddd.html

@@ -1,297 +0,0 @@
-<!DOCTYPE html>  
-<html lang="en">  
-
-<head>  
-  <meta charset="UTF-8" />  
-  <meta name="viewport" content="width=device-width, initial-scale=1" />  
-  <title>纯 JS 楼层切换控件</title>  
-  <style>  
-    .card_floor_control {  
-      user-select: none;  
-      position: absolute;  
-      top: 100px;  
-      right: calc(25% + 10px);  
-      width: 100px;  
-      height: auto;  
-      font-family: Arial, sans-serif;  
-    }  
-
-    .floor_control {  
-      position: relative;  
-      cursor: pointer;  
-      width: 100px;  
-      height: 40px;  
-      background-color: rgba(34, 91, 153, 0.3);  
-      display: flex;  
-      align-items: center;  
-      justify-content: center;  
-      color: #ffffff;  
-      border-radius: 4px;  
-      user-select: none;  
-    }  
-
-    .triangle:after {  
-      content: '';  
-      display: inline-block;  
-      margin-left: 20px;  
-      width: 10px;  
-      height: 10px;  
-      border-top: 1px solid #ffffff;  
-      border-right: 1px solid #ffffff;  
-      transform: rotate(135deg);  
-      -webkit-transform: rotate(135deg);  
-    }  
-
-    .all_building_floor {  
-      position: relative;  
-      margin-top: 10px;  
-      display: none;  
-      flex-direction: column;  
-      right: 0px;  
-    }  
-
-    .box_item_bf {  
-      display: flex;  
-      align-items: center;  
-      justify-content: center;  
-      font-size: 18px;  
-      position: relative;  
-      user-select: none;  
-    }  
-
-    .floor_box {  
-      cursor: pointer;  
-      color: rgb(108, 111, 113);  
-      --c: rgb(108, 111, 113);  
-      --n: 4;  
-      --t: 2px;  
-      --d: 45deg;  
-      aspect-ratio: 1;  
-      width: 60px;  
-      height: 60px;  
-      border-radius: 50%;  
-      background-color: rgb(1, 6, 19);  
-      margin-bottom: 10px;  
-      position: relative;  
-    }  
-
-    .floor_box::after {  
-      content: "";  
-      position: absolute;  
-      top: 2.5px;  
-      left: 2.5px;  
-      width: 55px;  
-      height: 55px;  
-      border-radius: 50%;  
-      padding: var(--t);  
-      background: var(--c);  
-      -webkit-mask:  
-        linear-gradient(#0000 0 0) content-box,  
-        repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));  
-      -webkit-mask-composite: source-in;  
-      mask:  
-        linear-gradient(#0000 0 0) content-box,  
-        repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));  
-      mask-composite: intersect;  
-    }  
-
-    .activate_box {  
-      cursor: pointer;  
-      color: #fff;  
-      --c: #fff;  
-      --n: 4;  
-      --t: 2px;  
-      --d: 45deg;  
-      aspect-ratio: 1;  
-      width: 60px;  
-      height: 60px;  
-      border-radius: 50%;  
-      background-color: rgb(1, 6, 19);  
-      margin-bottom: 10px;  
-      position: relative;  
-    }  
-
-    .activate_box::after {  
-      content: "";  
-      position: absolute;  
-      top: 2.5px;  
-      left: 2.5px;  
-      width: 55px;  
-      height: 55px;  
-      border-radius: 50%;  
-      padding: var(--t);  
-      background: var(--c);  
-      -webkit-mask:  
-        linear-gradient(#0000 0 0) content-box,  
-        repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));  
-      -webkit-mask-composite: source-in;  
-      mask:  
-        linear-gradient(#0000 0 0) content-box,  
-        repeating-conic-gradient(from calc(var(--d)/2), #000 0 calc(360deg/var(--n) - var(--d)), #0000 0 calc(360deg/var(--n)));  
-      mask-composite: intersect;  
-      animation: rotate 5s linear infinite;  
-    }  
-
-    @keyframes rotate {  
-      from {  
-        transform: rotate(0deg);  
-      }  
-
-      to {  
-        transform: rotate(360deg);  
-      }  
-    }  
-  </style>  
-</head>  
-
-<body>  
-  <div class="card_floor_control">  
-    <div class="floor_control" id="floorControl">  
-      全部<span class="triangle"></span>  
-    </div>  
-    <div class="all_building_floor" id="allBuildingFloor"></div>  
-  </div>  
-
-  <script>  
-    (function () {  
-      const arrData = [  
-        { title: '全部', id: 'all' },  
-        {  
-          title: 'A栋', id: 'A', list: [  
-            { title: '3F', id: '3' },  
-            { title: '2F', id: '2' },  
-            { title: '1F', id: '1' }  
-          ]  
-        },  
-        {  
-          title: 'B栋', id: 'B', list: [  
-            { title: '3F', id: '3' },  
-            { title: '2F', id: '2' },  
-            { title: '1F', id: '1' }  
-          ]  
-        },  
-        {  
-          title: 'C栋', id: 'C', list: [  
-            { title: '4F', id: '4' },  
-            { title: '3F', id: '3' },  
-            { title: '2F', id: '2' },  
-            { title: '1F', id: '1' }  
-          ]  
-        },  
-        {  
-          title: 'D栋', id: 'D', list: [  
-            { title: '2F', id: '2' },  
-            { title: '1F', id: '1' }  
-          ]  
-        }  
-      ]  
-
-      // 当前选中项id 和 title  
-      let currentId = 'all';  
-      let currentTitle = '全部';  
-
-      const floorControl = document.getElementById('floorControl');  
-      const allBuildingFloor = document.getElementById('allBuildingFloor');  
-
-      // 控制楼层列表容器显示/隐藏  
-      let showList = false;  
-
-      floorControl.addEventListener('click', () => {  
-        showList = !showList;  
-        if (showList) {  
-          allBuildingFloor.style.display = 'flex';  
-          // 切换显示当前选中项对应的列表  
-          renderList();  
-        } else {  
-          allBuildingFloor.style.display = 'none';  
-        }  
-      });  
-
-      // 渲染列表区域  
-      function renderList() {  
-        allBuildingFloor.innerHTML = '';  
-
-        // 找到当前id对应的item,和列表数据  
-        let listData = [];  
-
-        if (currentId === 'all') {  
-          // 如果是全部,展示顶层选项  
-          listData = arrData;  
-        } else {  
-          const currentBuilding = arrData.find(item => item.id === currentId);  
-          if (currentBuilding && currentBuilding.list) {  
-            listData = currentBuilding.list;  
-          } else {  
-            // 防止找不到对应list  
-            listData = [];  
-          }  
-        }  
-
-        listData.forEach(item => {  
-          const div = document.createElement('div');  
-          div.classList.add('box_item_bf');  
-
-          if (item.id === currentId) {  
-            div.classList.add('activate_box');  
-          } else {  
-            div.classList.add('floor_box');  
-          }  
-
-          div.textContent = item.title;  
-
-          div.addEventListener('click', (e) => {  
-            e.stopPropagation();  // 阻止事件冒泡,避免触发外层点击  
-            switchoverItem(item);  
-          });  
-
-          allBuildingFloor.appendChild(div);  
-        });  
-      }  
-
-      // 切换选中项  
-      function switchoverItem(item) {  
-        // 如果点了目前已经选中的项,列表隐藏  
-        if (item.id === currentId) {  
-          showList = false;  
-          allBuildingFloor.style.display = 'none';  
-          return;  
-        }  
-
-        currentId = item.id;  
-        currentTitle = item.title;  
-
-        // 改变按钮显示文字  
-        floorControl.childNodes[0].nodeValue = currentTitle;  
-
-        // 如果选中的是“全部”,则展示arrData顶层数据  
-        if (currentId === 'all') {  
-          // 当前激活项就是全部  
-          renderList();  
-        } else {  
-          // 判断当前id是否是顶层id,如果在顶层arrData里则显示对应list,否则显示arrData顶层选项  
-          const isTopLevel = arrData.some(it => it.id === currentId);  
-
-          if (isTopLevel) {  
-            renderList();  
-          } else {  
-            // 当前选中的是具体楼层,这时列表显示 arrData 顶层,方便切换楼栋  
-            currentId = 'all'; // 也可以这样处理,或者直接关闭列表  
-            currentTitle = '全部';  
-            floorControl.childNodes[0].nodeValue = currentTitle;  
-            showList = false;  
-            allBuildingFloor.style.display = 'none';  
-          }  
-        }  
-      }  
-
-      // 初始化显示  
-      floorControl.childNodes[0].nodeValue = currentTitle;  
-
-      // 说明:初始时列表隐藏,点击按钮显示顶层选项,也就是arrData。  
-
-    })();  
-  </script>  
-
-</body>  
-
-</html>

+ 93 - 14
src/views/index/reightDesk.vue

@@ -19,7 +19,16 @@
                 <div class="appeal_box">
                     <div class="image_architecture">
                         <div class="tect_arch">
-                            <svg-icon icon-class="architecture" className="arch_icon" />
+                            <svg class="arch_icon" aria-hidden="true">
+                                <defs>
+                                    <linearGradient id="myGradientil" x1="0%" y1="20%" x2="100%" y2="80%">
+                                        <stop offset="0%" stop-color="rgba(4, 223, 247)" />
+                                        <stop offset="50%" stop-color="rgba(255,255,255)" />
+                                        <stop offset="100%" stop-color="rgba(4, 223, 247)" />
+                                    </linearGradient>
+                                </defs>
+                                <use xlink:href="#icon-totality" fill="url('#myGradientil')" />
+                            </svg>
                         </div>
                     </div>
                     <div class="appeal_num">
@@ -30,7 +39,16 @@
                 <div class="appeal_box">
                     <div class="image_architecture">
                         <div class="tect_arch">
-                            <svg-icon icon-class="architecture" className="arch_icon" />
+                            <svg class="arch_icon" aria-hidden="true">
+                                <defs>
+                                    <linearGradient id="myGradientil" x1="0%" y1="20%" x2="100%" y2="80%">
+                                        <stop offset="0%" stop-color="rgb(4, 223, 247)" />
+                                        <stop offset="50%" stop-color="rgb(255,255,255)" />
+                                        <stop offset="100%" stop-color="rgb(4, 223, 247)" />
+                                    </linearGradient>
+                                </defs>
+                                <use xlink:href="#icon-processed" fill="url('#myGradientil')" />
+                            </svg>
                         </div>
                     </div>
                     <div class="appeal_num">
@@ -41,7 +59,16 @@
                 <div class="appeal_box">
                     <div class="image_architecture">
                         <div class="tect_arch">
-                            <svg-icon icon-class="architecture" className="arch_icon" />
+                            <svg class="arch_icon" aria-hidden="true">
+                                <defs>
+                                    <linearGradient id="myGradientil" x1="0%" y1="20%" x2="100%" y2="80%">
+                                        <stop offset="0%" stop-color="rgb(4, 223, 247)" />
+                                        <stop offset="50%" stop-color="rgb(255,255,255)" />
+                                        <stop offset="100%" stop-color="rgb(4, 223, 247)" />
+                                    </linearGradient>
+                                </defs>
+                                <use xlink:href="#icon-forbid" fill="url('#myGradientil')" />
+                            </svg>
                         </div>
                     </div>
                     <div class="appeal_num">
@@ -52,7 +79,16 @@
                 <div class="appeal_box">
                     <div class="image_architecture">
                         <div class="tect_arch">
-                            <svg-icon icon-class="architecture" className="arch_icon" />
+                            <svg class="arch_icon" aria-hidden="true">
+                                <defs>
+                                    <linearGradient id="myGradientil" x1="0%" y1="20%" x2="100%" y2="80%">
+                                        <stop offset="0%" stop-color="rgb(4, 223, 247)" />
+                                        <stop offset="50%" stop-color="rgb(255,255,255)" />
+                                        <stop offset="100%" stop-color="rgb(4, 223, 247)" />
+                                    </linearGradient>
+                                </defs>
+                                <use xlink:href="#icon-trend" fill="url('#myGradientil')" />
+                            </svg>
                         </div>
                     </div>
                     <div class="appeal_num">
@@ -66,14 +102,41 @@
             <HeadlineTag type="right" value="停车系统"></HeadlineTag>
             <div class="box_arch">
                 <div class="box_vehicle">
-                    <div class="color-green-line"></div>
-                    <span>占用</span>
-                    <div class="sum_cle" style="color: rgb(21, 213, 21);">{{ resultData.OccupationDriveway }}</div>
+                    <div class="tect_arch_tc">
+                        <svg class="arch_icon_tc" aria-hidden="true">
+                            <defs>
+                                <linearGradient id="myleisure" x1="0%" y1="100%" x2="0%" y2="0%">
+                                    <stop offset="0%" stop-color="rgb(21, 213, 21)" />
+                                    <stop offset="100%" stop-color="rgb(255,255,255)" />
+                                </linearGradient>
+                            </defs>
+                            <use xlink:href="#icon-leisure" fill="url('#myleisure')" />
+                        </svg>
+                    </div>
+                    <div style="display: flex;align-items: center;">
+                        <div class="color-green-line"></div>
+                        <span>空闲</span>
+                        <div class="sum_cle" style="color: rgb(21, 213, 21);">{{ resultData.IdleDriveway || 0 }}</div>
+                    </div>
                 </div>
                 <div class="box_vehicle">
-                    <div class="color-orange-line"></div>
-                    <span>空闲</span>
-                    <div class="sum_cle" style="color: rgb(215, 113, 23);">{{ resultData.IdleDriveway }}</div>
+                    <div class="tect_arch_tc">
+                        <svg class="arch_icon_tc" aria-hidden="true">
+                            <defs>
+                                <linearGradient id="myforbid" x1="0%" y1="100%" x2="0%" y2="0%">
+                                    <stop offset="0%" stop-color="rgb(215, 113, 23)" />
+                                    <stop offset="100%" stop-color="rgb(255,255,255)" />
+                                </linearGradient>
+                            </defs>
+                            <use xlink:href="#icon-forbid" fill="url('#myforbid')" />
+                        </svg>
+                    </div>
+                    <div style="display: flex;align-items: center;">
+                        <div class="color-orange-line"></div>
+                        <span>占用</span>
+                        <div class="sum_cle" style="color: rgb(215, 113, 23);">{{ resultData.OccupationDriveway || 0 }}
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
@@ -139,7 +202,7 @@ const props = defineProps({
 
         span {
             font-size: 14px;
-            color: rgb(158, 174, 195);
+            color: #ffffff;
         }
     }
 
@@ -190,6 +253,7 @@ const props = defineProps({
 }
 
 .appeal_num {
+    width: 40%;
     display: flex;
     flex-direction: column;
     align-items: center;
@@ -225,10 +289,24 @@ const props = defineProps({
     justify-content: center;
 }
 
+.tect_arch_tc {
+    width: 40%;
+    max-height: 100px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.arch_icon_tc {
+    width: 70%;
+    height: auto;
+    margin-bottom: 20px;
+}
+
 .arch_icon {
-    width: 80%;
-    height: 80%;
-    margin-bottom: 10px;
+    width: 70%;
+    height: 70%;
+    margin-bottom: 20px;
 }
 
 .box_vehicle {
@@ -237,6 +315,7 @@ const props = defineProps({
     display: flex;
     align-items: center;
     justify-content: center;
+    flex-direction: column;
 }
 
 .color-green-line {

+ 171 - 139
src/views/system/broadcast/modules/deviceList.vue

@@ -4,31 +4,28 @@
         <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.DeviceName }}</span>
-                    </el-text>
-                    <el-text class="w-150px mb-2" :class="item.UseState == 1 ? '_success' : ''" truncated
-                        style="color: #fff;flex: .2;">
-                        {{ getUseState(item.UseState) }}
-                    </el-text>
-                    <el-text class="w-150px mb-2" :class="item.OnlineState == 1 ? '' : '_warning'" truncated
-                        style="flex: .2;color: #fff;">
-                        {{ getOnlineState(item.OnlineState) }}
-                    </el-text>
-                    <el-icon color="#168cdb" style="flex: .2;">
-                        <el-icon>
-                            <Aim />
-                        </el-icon>
-                    </el-icon>
+        <div class="page">
+            <div class="header-view">
+                <div class="view_item" v-for="item in headerList">{{ item }}</div>
+            </div>
+            <div class="warning-view">
+                <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+                    <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                        <div class="item" v-for="(item, index) in data" :key="index">
+                            <div class="content view_item">
+                                <el-icon color="#168cdb" size="16">
+                                    <Headset />
+                                </el-icon>
+                                {{ item.DeviceName }}
+                            </div>
+                            <!-- 运行/停止/故障 -->
+                            <div class="time view_item" :class="item.OnlineState == 0 ? 'yellow_title' : 'blue_title'">
+                                {{ item.UseState == 0 ? '空闲' : '播放' }}
+                            </div>
+                            <div class="time view_item" :class="item.OnlineState == 0 ? 'blue_title' : 'red_title'">{{
+                                item.OnlineState == 0 ? '离线' : '在线' }}</div>
+                        </div>
+                    </div>
                 </div>
             </div>
         </div>
@@ -36,7 +33,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from "vue";
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 import { Headset, Aim } from '@element-plus/icons-vue'
 const props = defineProps({
@@ -45,148 +42,183 @@ const props = defineProps({
         default: []
     }
 })
-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 eventList = ref([])
-function getUseState(value) {
-    let title = ''
-    if (value == 0) {
-        title = '空闲'
-    } else {
-        title = '播放'
-    }
-    return title
-}
-function getOnlineState(value) {
-    let title = ''
-    if (value == 0) {
-        title = '离线'
-    } else {
-        title = '在线'
-    }
-    return title
-}
-const mainsRef = ref(null)
-const scrollY = ref(0)
-let intervalId = null
-const scrollSpeed = 0.5 // 滚动速度
-
-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();
+const headerList = ref(['名称', '使用状态', '状态'])
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
 };
 
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        eventList.value = newVal
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-
 onMounted(() => {
-    startCarousel();
-});
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
 
-onUnmounted(() => {
-    clearInterval(intervalId);
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
 <style lang="scss" scoped>
-._success {
-    color: #168cdb !important;
+._deviceList {
+    overflow: hidden;
+    display: flex;
+    flex-direction: column;
 }
 
-._warning {
-    color: rgb(244, 67, 54) !important;
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
 }
 
-._table_row1 {
-    margin-left: 10px;
-    text-emphasis: none;
-    /* 新增样式让文本超出显示省略号 */
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
 }
 
-._deviceList {
-    overflow: hidden;
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.view_item:first-child {
+    flex: 2;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
     display: flex;
     flex-direction: column;
+}
 
-    &_mains {
-        margin: 10px 30px;
-        overflow: hidden; // 隐藏溢出内容
-        cursor: pointer;
-        font-size: 12px;
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
 
-        &_item {
-            display: flex;
-            justify-content: space-between;
-            padding: 10px;
-        }
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
 
-        &_item:nth-child(even) {
-            background: rgba($color: #168cdb, $alpha: .05);
-        }
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
 
-        &_item:hover {
-            cursor: pointer;
-            background-image: linear-gradient(to right, #168cdb, transparent);
-        }
-    }
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+
+.yellow_title {
+    color: #E6A23C;
 }
 </style>
 
 <style lang="scss" scoped>
-.point_box {
-    margin: 10px 30px;
-}
+// .point_box {
+//     margin: 10px 30px;
+// }
 
 .point_box :deep(.el-input__wrapper) {
     background-color: transparent !important;

+ 157 - 89
src/views/system/broadcast/modules/eventList.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="_eventList">
         <HeadlineTag type="right" value="终端日志" style="flex-shrink: 0;"></HeadlineTag>
-        <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
+        <!-- <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">
@@ -16,12 +16,28 @@
                     </div>
                 </div>
             </div>
+        </div> -->
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.store === 'on' ? '_warning' : '_success'"
+                            class="_eventList_mains_item_text_flag"></div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ index + 1 }}#{{ item.TerminalLog }}
+                        </el-text>
+                    </div>
+                    <div class="_eventList_mains_item_btn">
+                        {{ item.TerminalTime }}
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from "vue";
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
@@ -29,96 +45,73 @@ const props = defineProps({
         default: []
     }
 })
-const eventList = ref([])
+const headerList = ref(['名称', '使用状态', '状态'])
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
 
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        eventList.value = newVal
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-// 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 = () => {
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
     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;
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
         }
-    }, 50);
-};
-
-const pauseCarousel = () => {
-    clearInterval(intervalId);
+    }, 20);
 };
 
-const resumeCarousel = () => {
-    startCarousel();
-};
-
-onMounted(() => {
-    startCarousel();
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
 
-onUnmounted(() => {
-    clearInterval(intervalId);
-});
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
 <style lang="scss" scoped>
@@ -136,9 +129,8 @@ onUnmounted(() => {
     flex-direction: column;
 
     &_mains {
-        margin: 10px 30px;
+        // margin: 10px 30px;
         overflow: hidden; // 隐藏溢出内容
-        // 鼠标移入时改变手势
         cursor: pointer;
 
         &_item {
@@ -176,14 +168,90 @@ onUnmounted(() => {
                 }
             }
         }
+    }
+}
 
-        &_item:nth-child(even) {
-            background: rgba($color: #168cdb, $alpha: .05);
-        }
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
 
-        &_item:hover {
-            background-image: linear-gradient(to right, #168cdb, transparent);
-        }
-    }
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    // padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.view_item:first-child {
+    flex: 2;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 0px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
 }
 </style>

+ 156 - 119
src/views/system/broadcast/modules/sameDay.vue

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

+ 147 - 151
src/views/system/building/equipment.vue

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

+ 188 - 0
src/views/system/building/groupControl.vue

@@ -0,0 +1,188 @@
+<template>
+    <div class="page">
+        <div class="header-view">
+            <div class="view_item" v-for="item in headerList">{{ item }}</div>
+        </div>
+        <div class="warning-view">
+            <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+                <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                    <div class="item" v-for="(item, index) in data" :key="index">
+                        <div class="content view_item">{{ item.Name }}</div>
+                        <!-- 运行/停止/故障 -->
+                        <div class="time view_item">
+                            {{ item.Location }}
+                        </div>
+                        <div class="time view_item">{{ item.Date }}</div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
+const props = defineProps({
+    resultData: {
+        type: Array,
+        default: []
+    }
+})
+const headerList = ref(['名称', '位置', '时间'])
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
+watch(() => props.resultData, (newVal) => {
+    if (newVal) {
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
+    }
+}, { deep: true, immediate: true } // 开启深度监听
+)
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
+    intervalId = setInterval(() => {
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
+        }
+    }, 20);
+};
+
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
+});
+
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
+</script>
+
+<style scoped lang="scss">
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
+
+.blue_title {
+    color: #67C23A;
+}
+
+.red_title {
+    color: #F56C6C;
+}
+</style>

+ 186 - 0
src/views/system/building/incident.vue

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

+ 24 - 216
src/views/system/building/index.vue

@@ -3,22 +3,15 @@
         <layout>
             <template #left>
                 <div class="left_spection">
-                    <!-- <div style="height: 30%;">
-                        <HeadlineTag value="水滴图"></HeadlineTag>
-                        <div class="box_arch center_in">
-                            <div style="width: 50%;height: 100%;">
-                                <waterdrop width="100%" color="rgb(42, 139, 247)"></waterdrop>
-                            </div>
-                        </div>
-                    </div> -->
-                    <div class="flex_spection">
+                    <div class="flex_spection" style="height: 100%;">
                         <HeadlineTag value="设备列表"></HeadlineTag>
-                        <div class="box_arch_point">
-                            <div class="point_box" style="margin-top: 10px;margin-left: 15px;">
-                                <el-input v-model="input" placeholder="按设备名称搜索" />
-                            </div>
+                        <div class="point_box" style="margin-top: 10px;margin-left: 15px;">
+                            <el-input v-model="input" placeholder="按设备名称搜索" />
+                        </div>
+                        <div style="height: calc(100% - 85px);">
                             <equipment :resultData="leftData.DeviceList"></equipment>
-                            <!-- <div style="height: calc(100% - 160px);">
+                        </div>
+                        <!-- <div style="height: calc(100% - 160px);">
                                 <Empty></Empty>
                             </div>
                             <div class="center_in">
@@ -26,7 +19,6 @@
                                     v-model:limit="queryParams.pageSize" layout="total, prev, pager, next"
                                     @pagination="getList" />
                             </div> -->
-                        </div>
                     </div>
                 </div>
             </template>
@@ -55,19 +47,16 @@
                             </div>
                         </div>
                     </div>
-                    <div class="flex_spection">
+                    <div class="flex_spection" style="height: 33%;">
                         <HeadlineTag type="right" value="设备群控"></HeadlineTag>
                         <div class="box_arch">
-                            <!-- <div ref="chartRouting" style="width: 100%;height: 100%;"></div> -->
-                            <dv-scroll-board :config="groupControl"
-                                style="width:100%;height:calc(100% - 10px);margin-top: 10px;" />
+                            <groupControl :resultData="leftData.DeviceList"></groupControl>
                         </div>
                     </div>
-                    <div class="flex_spection">
+                    <div class="flex_spection" style="height: 33%;">
                         <HeadlineTag type="right" value="运行事件"></HeadlineTag>
                         <div class="box_arch">
-                            <dv-scroll-board :config="config"
-                                style="width:100%;height:calc(100% - 10px);margin-top: 10px;" />
+                            <incident :resultData="leftData.DeviceList"></incident>
                         </div>
                     </div>
                 </div>
@@ -83,167 +72,14 @@ import HeadlineTag from '@/components/HeadlineTag'
 import Empty from '@/components/Empty'
 import waterdrop from '@/components/waterdrop'
 import equipment from './equipment.vue'
-import * as echarts from 'echarts'
+import groupControl from './groupControl.vue'
+import incident from './incident.vue'
 const total = ref(0);
 const queryParams = ref({
     pageNum: 1,
     pageSize: 10,
 });
 const input = ref('')
-const groupControl = ref({
-    headerBGC: '#10285c',
-    oddRowBGC: '#10285c7f',
-    evenRowBGC: '#10285c00',
-    header: ['名称', '位置', '时间'],
-    data: [
-        ['行1列1', '行1列2', '行1列3'],
-        ['行2列1', '行2列2', '行2列3'],
-        ['行3列1', '行3列2', '行3列3'],
-        ['行4列1', '行4列2', '行4列3'],
-        ['行5列1', '行5列2', '行5列3'],
-        ['行6列1', '行6列2', '行6列3'],
-        ['行7列1', '行7列2', '行7列3'],
-        ['行8列1', '行8列2', '行8列3'],
-        ['行9列1', '行9列2', '行9列3'],
-        ['行10列1', '行10列2', '行10列3']
-    ]
-})
-const config = ref({
-    headerBGC: '#10285c',
-    oddRowBGC: '#10285c7f',
-    evenRowBGC: '#10285c00',
-    header: ['名称', '事件', '时间'],
-    data: [
-        ['行1列1', '行1列2', '行1列3'],
-        ['行2列1', '行2列2', '行2列3'],
-        ['行3列1', '行3列2', '行3列3'],
-        ['行4列1', '行4列2', '行4列3'],
-        ['行5列1', '行5列2', '行5列3'],
-        ['行6列1', '行6列2', '行6列3'],
-        ['行7列1', '行7列2', '行7列3'],
-        ['行8列1', '行8列2', '行8列3'],
-        ['行9列1', '行9列2', '行9列3'],
-        ['行10列1', '行10列2', '行10列3']
-    ]
-})
-
-/** 查询列表 */
-function getList() {
-};
-const chartRouting = ref(null);
-let chartInstance = null;
-// 初始化图表
-const initChart = () => {
-    if (!chartInstance) {
-        chartInstance = echarts.init(chartRouting.value);
-    }
-    chartInstance.setOption({
-        tooltip: {
-            trigger: 'axis',
-            textStyle: {
-                color: '#fafafa',
-            },
-            borderColor: 'transparent',
-            backgroundColor: 'rgba(0, 0, 0, 0.5)',
-            extraCssText: 'backdrop-filter: blur(6px);',
-        },
-        legend: {
-            data: ['产值', '增加值'],
-            icon: 'rich',
-            show: true,
-            itemWidth: 18,
-            itemHeight: 2,
-            textStyle: {
-                color: '#AFBDD1',
-                fontSize: '12px',
-            },
-            top: 8,
-            right: 10,
-            itemGap: 34,
-        },
-        grid: {
-            left: '4%',
-            right: '4%',
-            bottom: '10%',
-            top: '16%',
-            containLabel: true,
-        },
-        xAxis: {
-            data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
-            type: 'category',
-            boundaryGap: false,
-            axisLine: {
-                symbol: 'none',
-                lineStyle: {
-                    color: '#50637A',
-                },
-            },
-            axisTick: {
-                show: false,
-            },
-            axisLabel: {
-                interval: 0,
-                color: '#ffffff',
-                fontSize: 12,
-                padding: [10, 0, 0, 0],
-            },
-        },
-        yAxis: {
-            type: 'value',
-            axisLabel: {
-                color: '#ffffff',
-                fontSize: 12,
-                padding: [0, 10, 0, 0],
-            },
-            splitLine: {
-                show: false,
-            },
-        },
-        series: [
-            {
-                name: '增加值',
-                data: [1, 2, 3, 4, 7, 6, 7, 8, 4, 10],
-                type: 'line',
-                smooth: true,
-                color: '#00F7FF',
-                lineStyle: {
-                    width: 2,
-                },
-                areaStyle: {
-                    color: new echarts.graphic.LinearGradient(
-                        0,
-                        0,
-                        0,
-                        1,
-                        [{
-                            offset: 0,
-                            color: 'rgba(0, 247, 255, .6)',
-                        },
-                        {
-                            offset: 0.8,
-                            color: 'rgba(0, 247, 255, .2)',
-                        },
-                        ],
-                        false
-                    ),
-                    shadowColor: 'rgba(0, 0, 0, 0.1)',
-                    shadowBlur: 10,
-                },
-                symbol: 'circle',
-                symbolSize: 6,
-            },
-        ],
-    });
-};
-// 生命周期
-onMounted(() => {
-    intervalId.value = setInterval(getIntellData, 10000);
-    getIntellData()
-    // initChart()
-});
-onUnmounted(() => {
-    clearInterval(intervalId.value);
-})
 const intervalId = ref(null)
 const leftData = ref({})
 function getIntellData() {
@@ -253,44 +89,14 @@ function getIntellData() {
         }
     })
 }
-
-watch(() => leftData.value, (newVal) => {
-    let arrData = []
-    let arrDatail = newVal.DeviceList
-    if (arrDatail && arrDatail.length > 0) {
-        arrDatail.forEach((item) => {
-            let arrData2 = []
-            arrData2.push(item.Name)
-            arrData2.push(item.AlarmContent)
-            arrData2.push(item.Date)
-            arrData.push(arrData2)
-        })
-    }
-    config.value.data = arrData
-    let arrData1 = []
-    let arrDatail1 = newVal.DeviceList
-    if (arrDatail1 && arrDatail1.length > 0) {
-        arrDatail1.forEach((item) => {
-            let arrData2 = []
-            arrData2.push(item.Name)
-            let title = ''
-            if (item.State == 0) {
-                title = '在线'
-            } else {
-                title = '离线'
-            }
-            arrData2.push(title)
-            arrData2.push(item.Date)
-            arrData1.push(arrData2)
-        })
-    }
-    groupControl.value.data = arrData1
-}, { deep: true, immediate: true } // 开启深度监听
-)
-// 窗口自适应
-window.addEventListener('resize', () => {
-    chartInstance?.resize();
+// 生命周期
+onMounted(() => {
+    intervalId.value = setInterval(getIntellData, 10000);
+    getIntellData()
 });
+onUnmounted(() => {
+    clearInterval(intervalId.value);
+})
 </script>
 
 <style scoped lang="scss">
@@ -308,6 +114,7 @@ window.addEventListener('resize', () => {
     height: 100%;
     display: flex;
     flex-direction: column;
+    overflow: hidden;
 }
 
 .right_spection {
@@ -324,7 +131,8 @@ window.addEventListener('resize', () => {
 }
 
 .operation_spection {
-    height: 250px;
+    height: 30%;
+    flex: none;
     color: #fff;
 }
 
@@ -343,7 +151,7 @@ window.addEventListener('resize', () => {
     position: relative;
     flex: none;
     width: 200px;
-    height: 100%;
+    height: 85%;
     background: url("@/assets/images/tubbiness.png");
     background-size: 80% 80%;
     background-position: center center;

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

@@ -112,7 +112,6 @@ onMounted(() => {
                 }
             ]
         };
-
         chart.setOption(option);
     }
     window.addEventListener('resize', handleResize);

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

@@ -32,8 +32,8 @@ import { ref } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
-        type: Array,
-        default: []
+        type: Object,
+        default: {}
     }
 })
 const runningList = ref([

+ 151 - 93
src/views/system/message/modules/sameDay.vue

@@ -1,14 +1,11 @@
 <template>
     <div class="_eventList">
         <HeadlineTag type="right" value="大屏控制" style="flex-shrink: 0;"></HeadlineTag>
-        <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
-            <div :style="{ transform: `translateY(${scrollY}px)` }">
-                <div class="_eventList_mains_item" v-for="(item, index) in eventList.concat(eventList)" :key="index">
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
                     <div class="_eventList_mains_item_text">
                         <div class="_eventList_mains_item_text_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.DeviceName }}
@@ -27,7 +24,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from "vue";
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
@@ -35,94 +32,72 @@ const props = defineProps({
         default: []
     }
 })
-// 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 eventList = ref([])
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        eventList.value = []
-        eventList.value = newVal
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-const mainsRef = ref(null)
-const scrollY = ref(0)
-let intervalId = null
-const scrollSpeed = 1 // 滚动速度
-
-const startCarousel = () => {
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
     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;
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
         }
-    }, 50);
-};
-
-const pauseCarousel = () => {
-    clearInterval(intervalId);
-};
-
-const resumeCarousel = () => {
-    startCarousel();
+    }, 20);
 };
 
-onMounted(() => {
-    startCarousel();
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
 
-onUnmounted(() => {
-    clearInterval(intervalId);
-});
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
 <style lang="scss" scoped>
@@ -152,8 +127,9 @@ onUnmounted(() => {
             padding: 10px 0;
 
             &_text {
+                width: 50%;
                 display: flex;
-                justify-content: space-between;
+                justify-content: flex-start;
                 align-items: center;
                 max-width: 50%;
                 text-emphasis: none;
@@ -195,18 +171,100 @@ onUnmounted(() => {
                 }
             }
         }
-
-        &_item:nth-child(even) {
-            background: rgba($color: #168cdb, $alpha: .05);
-        }
-
-        &_item:hover {
-            background-image: linear-gradient(to right, #168cdb, transparent);
-        }
     }
 }
 
 ._xian {
+    width: fit-content;
+    width: -webkit-fit-content;
+    width: -moz-fit-content;
+    display: flex;
+    align-items: center;
+    justify-content: center;
     font-size: 12px;
 }
+
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.view_item:first-child {
+    flex: 2;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
 </style>

+ 178 - 96
src/views/system/tenement/modules/eventList.vue

@@ -1,24 +1,44 @@
 <template>
     <div class="_eventList">
         <HeadlineTag type="right" value="人员出入记录" style="flex-shrink: 0;"></HeadlineTag>
-        <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
+        <!-- <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.State === 0 ? '_success' : '_warning'"
+                        <div :class="item.State === 0 ? '_success' : '_warning'"
                             class="_eventList_mains_item_text_flag">
-                            {{ item.State === 0?'进':'出' }}
+                            {{ item.State === 0 ? '进' : '出' }}
                         </div>
                         <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
                             {{ item.Name }}
                         </el-text>
                     </div>
-                    <div :style="{color:item.State === 0?'#15acaa':'#FFC107','font-size':'12px'}">
-                        {{ item.State === 0?'进入':'出入' }}
+                    <div :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
+                        {{ item.State === 0 ? '进入' : '出入' }}
                     </div>
                     <div class="_eventList_mains_item_btn">
-                        {{item.Date}}
+                        {{ item.Date }}
+                    </div>
+                </div>
+            </div>
+        </div> -->
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
+                    <div class="_eventList_mains_item_text">
+                        <div :class="item.State === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag">
+                            {{ item.State === 0 ? '进' : '出' }}
+                        </div>
+                        <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
+                            {{ item.Name }}
+                        </el-text>
+                    </div>
+                    <div :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
+                        {{ item.State === 0 ? '进入' : '出入' }}
+                    </div>
+                    <div class="_eventList_mains_item_btn">
+                        {{ item.Date }}
                     </div>
                 </div>
             </div>
@@ -27,7 +47,7 @@
 </template>
 
 <script setup>
-import { ref, onMounted, onUnmounted } from "vue";
+import { ref, onBeforeMount, onMounted, onBeforeUnmount, nextTick } from "vue";
 import HeadlineTag from '@/components/HeadlineTag'
 const props = defineProps({
     resultData: {
@@ -35,104 +55,84 @@ const props = defineProps({
         default: []
     }
 })
-// 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 eventList = ref([])
+const headerList = ref(['名称', '使用状态', '状态'])
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        eventList.value = []
-        eventList.value = newVal
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-const mainsRef = ref(null)
-const scrollY = ref(0)
-let intervalId = null
-const scrollSpeed = 1 // 滚动速度
-
-const startCarousel = () => {
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
     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;
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
         }
-    }, 50);
-};
-
-const pauseCarousel = () => {
-    clearInterval(intervalId);
-};
-
-const resumeCarousel = () => {
-    startCarousel();
+    }, 20);
 };
 
-onMounted(() => {
-    startCarousel();
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
 
-onUnmounted(() => {
-    clearInterval(intervalId);
-});
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
 <style lang="scss" scoped>
 ._success {
-    background: #15acaa;	
+    background: #15acaa;
 }
+
 ._warning {
-    background: #FFC107;	
+    background: #FFC107;
 }
+
 ._eventList {
     overflow: hidden;
     display: flex;
@@ -154,38 +154,120 @@ onUnmounted(() => {
                 display: flex;
                 justify-content: space-between;
                 align-items: center;
+
                 &_flag {
                     width: 30px;
                     height: 30px;
                     border-radius: 50%;
-                    margin-left: 10px; 
+                    margin-left: 10px;
                     display: flex;
                     align-items: center;
                     justify-content: center;
                     color: #fff;
                     font-size: 14px;
                 }
+
                 &_p {
                     margin-left: 10px;
                 }
             }
-            &_btn{
+
+            &_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); 
-        }
     }
 }
+
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
+
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
+
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.view_item:first-child {
+    flex: 2;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
+}
 </style>

+ 145 - 92
src/views/system/tenement/modules/tiring.vue

@@ -1,11 +1,12 @@
 <template>
     <div class="_eventList">
         <HeadlineTag type="right" value="车辆出入记录" style="flex-shrink: 0;"></HeadlineTag>
-        <div class="_eventList_mains" ref="mainsRef" @mouseenter="pauseCarousel" @mouseleave="resumeCarousel">
-            <div :style="{ transform: `translateY(${scrollY}px)` }">
-                <div class="_eventList_mains_item" v-for="(item, index) in eventList.concat(eventList)" :key="index">
+        <div class="scroll-view" ref="scrollViewRef" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
+            <div ref="listRef" class="list" v-for="(p, n) in count" :key="n">
+                <div class="item" v-for="(item, index) in data" :key="index">
                     <div class="_eventList_mains_item_text">
-                        <div :class="item.State === 0 ? '_success' : '_warning'" class="_eventList_mains_item_text_flag">
+                        <div :class="item.State === 0 ? '_success' : '_warning'"
+                            class="_eventList_mains_item_text_flag">
                             {{ item.State === 0 ? '进' : '出' }}
                         </div>
                         <el-text class="w-150px mb-2" truncated style="color: white;margin-left: 10px;">
@@ -15,8 +16,8 @@
                     <div :style="{ color: item.State === 0 ? '#15acaa' : '#FFC107', 'font-size': '12px' }">
                         {{ item.State === 0 ? '进入' : '离开' }}
                     </div>
-                    <div>
-                        <img :src="item.GarImag" style="width: 30px;height: 30px;border-radius: 5px;" srcset="">
+                    <div style="width: 30px;height: 30px;">
+                        <el-image :src="item.GarImag" style="width: 30px;height: 30px;border-radius: 5px;" />
                     </div>
                     <div class="_eventList_mains_item_btn">
                         {{ item.Date }}
@@ -36,96 +37,73 @@ const props = defineProps({
         default: []
     }
 })
-// 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 eventList = ref([])
+
+const data = ref([]); //列表数据
+const listRef = ref(); //列表dom
+const scrollViewRef = ref(); //滚动区域dom
+const count = ref(1); //列表个数
+
+let intervalId = null;
+let isAutoScrolling = true; //是否自动滚动标识
+
+//获取列表数据
+const getData = () => {
+    //模拟接口请求列表数据
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            //生成10条数据
+            let list = new Array(30).fill().map((item, index) => index);
+            resolve(list);
+        }, 100);
+    });
+};
+
 watch(() => props.resultData, (newVal) => {
     if (newVal) {
-        eventList.value = []
-        eventList.value = newVal
+        // data.value = await getData();
+        data.value = newVal;
+        intervalId && clearInterval(intervalId);
+        nextTick(() => {
+            //判断列表是否生成滚动条
+            count.value = hasScrollBar() ? 2 : 1;
+            //有滚动条开始自动滚动
+            if (count.value == 2) {
+                autoScrolling();
+            }
+        });
     }
 }, { deep: true, immediate: true } // 开启深度监听
 )
-const mainsRef = ref(null)
-const scrollY = ref(0)
-let intervalId = null
-const scrollSpeed = 1 // 滚动速度
-
-const startCarousel = () => {
+onMounted(() => {
+})
+//判断列表是否有滚动条
+const hasScrollBar = () => {
+    return scrollViewRef.value.scrollHeight > scrollViewRef.value.clientHeight;
+};
+//设置自动滚动
+const autoScrolling = () => {
     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;
+        if (scrollViewRef.value.scrollTop < listRef.value[0].clientHeight) {
+            scrollViewRef.value.scrollTop += isAutoScrolling ? 1 : 0;
+        } else {
+            scrollViewRef.value.scrollTop = 0;
         }
-    }, 50);
-};
-
-const pauseCarousel = () => {
-    clearInterval(intervalId);
+    }, 20);
 };
 
-const resumeCarousel = () => {
-    startCarousel();
-};
-
-onMounted(() => {
-    startCarousel();
+onBeforeUnmount(() => {
+    //离开页面清理定时器
+    intervalId && clearInterval(intervalId);
 });
 
-onUnmounted(() => {
-    clearInterval(intervalId);
-});
+//鼠标进入,停止滚动
+const onMouseenter = () => {
+    isAutoScrolling = false;
+};
+//鼠标移出,继续滚动
+const onMouseleave = () => {
+    isAutoScrolling = true;
+};
 </script>
 
 <style lang="scss" scoped>
@@ -189,14 +167,89 @@ onUnmounted(() => {
                 }
             }
         }
+    }
+}
+.page {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
 
-        &_item:nth-child(even) {
-            background: rgba($color: #168cdb, $alpha: .05);
-        }
+.header-view {
+    margin-top: 10px;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 10px 10px 20px;
+    background-color: rgba(16, 40, 92, 1);
+}
 
-        &_item:hover {
-            background-image: linear-gradient(to right, #168cdb, transparent);
-        }
-    }
+.view_item {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    color: #ffffff;
+}
+
+.view_item:first-child {
+    flex: 2;
+}
+
+.warning-view {
+    width: 100%;
+    height: calc(100% - 51px);
+    display: flex;
+    flex-direction: column;
+}
+
+.label {
+    color: #fff;
+    padding: 20px;
+    font-size: 22px;
+}
+
+.scroll-view {
+    flex: 1;
+    height: 0;
+    width: 100%;
+    overflow-y: auto;
+}
+
+.list {
+    width: 100%;
+    box-sizing: border-box;
+}
+
+.item {
+    padding: 0px 10px 0px 20px;
+    width: 100%;
+    height: 50px;
+    min-height: 50px;
+    font-size: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    color: #eee;
+}
+
+.item:nth-child(even) {
+    background: rgba(16, 40, 92, 0.4);
+}
+
+.item:hover {
+    cursor: pointer;
+    background-image: linear-gradient(to right, #168cdb, transparent);
+}
+
+/*隐藏滚动条
+ */
+::-webkit-scrollbar {
+    display: none;
 }
 </style>