Преглед изворни кода

master: Fixed 指标切换动画完善

gitboyzcf пре 4 месеци
родитељ
комит
cb3a6b7820

+ 6 - 2
index.html

@@ -70,9 +70,13 @@
 
 <body>
   <div id="app">
-    <div class="loader"></div>
+    <div
+      style="background: #09284C; position: fixed; top: 0; right: 0; bottom:0; left: 0; display: flex; align-items: center; justify-content: center; align-items: center;">
+      <div class="loader"></div>
+    </div>
+
   </div>
   <script type="module" src="/src/main.js"></script>
 </body>
 
-</html>
+</html>

BIN
src/assets/images/my-icon.png


+ 5 - 1
src/layout/LayoutHeader.vue

@@ -27,11 +27,15 @@
         </router-link>
       </li>
     </ul> -->
-    <Weather />
+    <div class="flex items-center justify-center gap-6">
+      <Avatar />
+      <Weather />
+    </div>
   </div>
 </template>
 
 <script setup>
+  import Avatar from './components/Avatar.vue'
   const title = import.meta.env.VITE_APP_TITLE
   const navBar = ref([
     {

+ 136 - 16
src/layout/Map.vue

@@ -6,21 +6,22 @@
   import { Map, View } from 'ol'
   import SourceVector from 'ol/source/Vector'
   import LayerVector from 'ol/layer/Vector'
-  import Vector from 'ol/source/Vector.js'
+  import Cluster from 'ol/source/Cluster.js'
+  import { defaults } from 'ol/interaction'
   // import OSM from "ol/source/OSM";
   import { Circle, Fill, Stroke, Style, Text, Icon } from 'ol/style'
   import { Zoom } from 'ol/control'
   import { fromLonLat } from 'ol/proj'
+  import { boundingExtent } from 'ol/extent.js'
   import Feature from 'ol/Feature'
   import Point from 'ol/geom/Point'
   import GeoJSON from 'ol/format/GeoJSON'
   import videoIcon from '@/assets/images/video-icon.png'
-  import { router } from '@/router'
   import { useOutsideHomeStore } from '@/stores/modules/home'
 
   const useHomeStore = useOutsideHomeStore()
 
-  const map = ref(null)
+  const map = shallowRef(null)
 
   const labelStyle = new Style({
     text: new Text({
@@ -37,6 +38,18 @@
       })
     })
   })
+  // 基础样式
+  const basePointStyle = new Style({
+    image: new Icon({
+      src: videoIcon,
+      scale: 1,
+      anchor: [0.5, 0.5],
+      rotateWithView: true,
+      rotation: 0,
+      opacity: 1
+    }),
+    count: 1
+  })
   // GeoJson图层列表
   const vectorsJson = [
     // 国界线
@@ -150,7 +163,7 @@
         console.log(result)
       } else {
         result.push(
-          new Vector({
+          new SourceVector({
             url: `/background/geojson/${file}.json`, // 第一个 GeoJSON 文件的路径
             format: new GeoJSON()
           })
@@ -186,6 +199,103 @@
     return layers
   }
 
+  // 根据范围随机生成经纬度点位 rangeArr = [minLat, maxLat, minLon, maxLon]
+  const createPointsByRange = (num, rangeArr = [39.9037, 40.9892, 115.2, 117.4]) => {
+    const [minLat, maxLat, minLon, maxLon] = rangeArr
+    const points = []
+    for (var i = 0; i < num; i++) {
+      var lat = Math.random() * (maxLat - minLat) + minLat
+      var lon = Math.random() * (maxLon - minLon) + minLon
+      points.push([lon, lat])
+    }
+    return points
+  }
+
+  const currentDis = ref(150)
+  // 根据数据创建聚合图层
+  const createCluster = (points, zindex) => {
+    const features = points.map((e) => {
+      // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
+      const feature = new Feature({
+        geometry: new Point(fromLonLat(e)),
+        custom: {
+          id: Math.ceil(Math.random() * 100000)
+        }
+      })
+      return feature
+    })
+    // 根据points创建一个新的数据源和要素数组,
+    const vectorSource = new SourceVector({
+      features
+    })
+
+    // 根据点位创建聚合资源
+    const clusterSource = new Cluster({
+      distance: currentDis.value, // 设置多少像素以内的点位进行聚合
+      source: vectorSource
+    })
+    // 创建带有数据源的矢量图层,将创建的聚合字段作为source
+    const clusters = new LayerVector({
+      source: clusterSource,
+      style: (feature) => {
+        return setFeatureStyle(feature) // 设置聚合点的样式
+      }
+    })
+    // 将矢量图层添加到地图上
+    map.value.addLayer(clusters)
+    // sv.setZIndex(zindex) // 设置层级
+    return clusters
+  }
+
+  const countStyles = {}
+  // 生成点位聚合显示的数字样式
+  const createCountPointStyle = (size) => {
+    // 计算一个动态的 radius
+    const radius = 20 + Math.max(0, String(size).length - 2) * 10
+    // const rcolor =
+    //   '#' +
+    //   parseInt(Math.random() * 0xffffff)
+    //     .toString(16)
+    //     .padStart(6, '0')
+    return new Style({
+      image: new Circle({
+        radius,
+        stroke: new Stroke({
+          color: '#fff'
+        }),
+        fill: new Fill({
+          color: '#3399CC'
+        })
+      }),
+      text: new Text({
+        text: size.toString(),
+        fill: new Fill({
+          color: '#fff'
+        }),
+        scale: 2,
+        textBaseline: 'middle'
+      })
+    })
+  }
+
+  // 设置聚合点的样式
+  const setFeatureStyle = (feature) => {
+    // 获取聚合点小有几个点位
+    const size = feature.get('features').length
+    // 设置聚合点的count参数
+    feature.set('count', size)
+    // 如果是1,直接展示点位的样式
+    if (size === 1) {
+      return basePointStyle
+    } else {
+      // 如果是聚合点,查看countStyles是否存储了这个聚合点的数字样式,如果不存在,生成一个并存储
+      if (!countStyles[size]) {
+        countStyles[size] = createCountPointStyle(size)
+      }
+      return countStyles[size]
+    }
+  }
+
   /**
    * 在地图上设置一个标注。
    *
@@ -282,13 +392,20 @@
         center: fromLonLat([104.912777, 34.730746]),
         zoom: 2
       }),
+      interactions: defaults({
+        dragPan: {
+          duration: 100
+        }
+      }),
       controls: []
     })
     const vlText = setMark('text', { text: '中华人民共和国' })
     const vlImg = []
-    mapData.value.forEach((item) => {
-      vlImg.push(setMark('img', { src: videoIcon, ...item }, [item.longitude, item.latitude]))
-    })
+    const points = createPointsByRange(100)
+    // mapData.value.forEach((item) => {
+    //   vlImg.push(setMark('img', { src: videoIcon, ...item }, [item.longitude, item.latitude]))
+    // })
+    const clusters = createCluster(points)
     // 缩放显示隐藏层级
     const zoomChange = function (e) {
       var zoom = parseInt(map.value.getView().getZoom()) //获取当前地图的缩放级别
@@ -330,18 +447,21 @@
         })
       }
     }
-    map.value.on('singleclick', function (e) {
-      console.log(e.coordinate)
-      if (map.value.hasFeatureAtPixel(e.pixel)) {
-        var feature = map.value.getFeaturesAtPixel(e.pixel)
-        console.log(feature)
-        feature.forEach((item) => {
-          if (mapData.value.find((v) => item.get('name') === v.name)) {
+    map.value.on('click', (e) => {
+      clusters.getFeatures(e.pixel).then((clickedFeatures) => {
+        if (clickedFeatures.length) {
+          // Get clustered Coordinates
+          const features = clickedFeatures[0].get('features')
+          console.log(features)
+          if (features.length > 1) {
+            const extent = boundingExtent(features.map((r) => r.getGeometry().getCoordinates()))
+            map.value.getView().fit(extent, { duration: 1000, padding: [50, 50, 50, 50] })
+          } else {
             console.log('点击了坐标点')
             useHomeStore.temp = 'video'
           }
-        })
-      }
+        }
+      })
     })
     map.value.getView().on('change:resolution', zoomChange)
     zoomChange()

+ 23 - 0
src/layout/components/Avatar.vue

@@ -0,0 +1,23 @@
+<template>
+  <div class="avatar relative">
+    <div class="flex items-center gap-10px cursor-pointer" @click="isShow = !isShow">
+      <div
+        class="w-36px h-36px rd-50% flex justify-center items-center b-[rgba(39,154,255,0.6)] b-2px b-solid"
+      >
+        <img src="@/assets/images/my-icon.png" alt="avatar" />
+      </div>
+      <span class="color-#fff text-18px">管理员</span>
+    </div>
+    <div
+      :class="[
+        isShow ? 'transform-scale-100' : 'transform-scale-0',
+        'overflow-hidden origin-top transition pointer-events-none duration-300 z-999 h-200px w-300px bg-[rgba(11,21,44,1)] absolute left-50% translate-x--50% top-46px'
+      ]"
+      >Tooltip</div
+    >
+  </div>
+</template>
+
+<script setup>
+  const isShow = ref(false)
+</script>

+ 85 - 5
src/views/home/home.vue

@@ -3,17 +3,49 @@
     <template #map>
       <!-- <ThreeMap /> -->
       <!-- <router-view v-slot="{ Component }">
-        <transition name="fade">
+        <Transition name="fade">
           <component :is="Component" />
-        </transition>
+        </Transition>
       </router-view> -->
       <Map />
     </template>
     <template #left>
-      <LeftBox />
+      <!-- <Transition
+        name="left"
+        enter-active-class="animate__animated animate__flipInY"
+        leave-active-class="animate__animated animate__flipOutY"
+      >
+        <component :is="leftBoxCom" />
+      </Transition> -->
+      <div class="flip-card">
+        <div class="flip-card-inner" :class="{ 'is-flipped': isFlipped }">
+          <div class="flip-card-front">
+            <!-- 这里放置前面的内容 -->
+            <LeftBox />
+          </div>
+          <div class="flip-card-back">
+            <!-- 这里放置后面的内容 -->
+            <LeftBoxTwo />
+          </div>
+        </div>
+      </div>
     </template>
     <template #right>
-      <RightBox />
+      <!-- <Transition name="flip">
+        <component :is="rightBoxCom" />
+      </Transition> -->
+      <div class="flip-card">
+        <div class="flip-card-inner" :class="{ 'is-flipped': isFlipped }">
+          <div class="flip-card-front">
+            <!-- 这里放置前面的内容 -->
+            <RightBox />
+          </div>
+          <div class="flip-card-back">
+            <!-- 这里放置后面的内容 -->
+            <RightBoxTwo />
+          </div>
+        </div>
+      </div>
     </template>
     <template #top>
       <MiddleTopBox v-if="isMap" />
@@ -29,15 +61,63 @@
   import Layout from '@/layout/index.vue'
   import LeftBox from './leftbox/index.vue'
   import RightBox from './rightbox/index.vue'
+  import LeftBoxTwo from './leftboxtwo/index.vue'
+  import RightBoxTwo from './rightboxtwo/index.vue'
 
   import MiddleBottomBox from './middleBottomBox/index.vue'
   import MiddleTopBox from './middleTopBox/index.vue'
   import VideoBox from '../VideoBox/index.vue'
   import Map from '@/layout/Map.vue'
-
   import { $mitt } from '@/utils'
   import { useOutsideHomeStore } from '@/stores/modules/home'
 
   const useHomeStore = useOutsideHomeStore()
   const isMap = computed(() => useHomeStore.temp === 'map')
+
+  const isFlipped = ref(false)
+
+  // const leftBoxCom = shallowRef(LeftBox)
+  // const rightBoxCom = shallowRef(RightBox)
+
+  $mitt.on('onThemeIndex', (i) => {
+    if (i) {
+      // leftBoxCom.value = defineAsyncComponent(() => import('./leftboxtwo/index.vue'))
+      // rightBoxCom.value = defineAsyncComponent(() => import('./rightboxtwo/index.vue'))
+      isFlipped.value = true
+    } else {
+      // leftBoxCom.value = LeftBox
+      // rightBoxCom.value = RightBox
+      isFlipped.value = false
+    }
+  })
 </script>
+
+<style>
+  .flip-card {
+    /* perspective: 1000px; */
+    position: relative;
+    width: 453px;
+    height: 100%;
+  }
+  .flip-card-inner {
+    width: 100%;
+    height: 100%;
+    transition: transform 0.8s;
+    transform-style: preserve-3d;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+  }
+  .flip-card-inner.is-flipped {
+    transform: rotateY(180deg);
+  }
+  .flip-card-front,
+  .flip-card-back {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    backface-visibility: hidden;
+    z-index: 99;
+  }
+  .flip-card-back {
+    transform: rotateY(180deg);
+  }
+</style>

+ 1 - 1
src/views/home/leftbox/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="w-453px flex flex-col gap-15px relative lr-box-bg">
+  <div class="w-453px flex flex-col gap-15px lr-box-bg overflow-hidden">
     <Box title="概况" wHeader="60px">
       <TopBox />
     </Box>

+ 64 - 0
src/views/home/leftboxtwo/bottomBox.vue

@@ -0,0 +1,64 @@
+<template>
+  <div class="bottom-box">
+    <div class="flex justify-between relative">
+      <div>
+        <span>用户总数 </span>
+        <Countto class="text-18px color-#389bff font-bold" :endVal="data.yhzs" />
+      </div>
+
+      <ul class="flex gap-15px">
+        <li
+          :class="[
+            'w-50px cursor-pointer text-center b-solid b-1px b-color-#389bff hover:b-color-#FAC858',
+            i === selectedI ? 'b-color-#FAC858' : ''
+          ]"
+          v-for="(item, i) in options"
+          :key="item.code"
+          @click="dayClick(i)"
+        >
+          <span class="text-12px">{{ item.name }}</span>
+        </li>
+      </ul>
+    </div>
+    <ECharts
+      id="yhqk-echarts2"
+      width="100%"
+      height="120px"
+      :loading="loading"
+      :fullOptions="fullOptions"
+    />
+  </div>
+</template>
+
+<script setup>
+  import { chartOptions } from '@/components/ECharts/optionsConfig'
+  const loading = ref(true)
+  const data = reactive({
+    yhzs: 30
+  })
+
+  const fullOptions = ref({ options: {} })
+  const selectedI = ref(0)
+  const options = ref([
+    { name: '当日', code: 0 },
+    { name: '近一周', code: 1 },
+    { name: '近一月', code: 2 }
+  ])
+  const dayClick = (v) => {
+    selectedI.value = v
+    fullOptions.value.options = chartOptions.setYhqkOption([], selectedI.value)
+  }
+  onMounted(() => {
+    setTimeout(() => {
+      loading.value = false
+      fullOptions.value.options = chartOptions.setYhqkOption([], selectedI.value)
+    }, 1000)
+  })
+</script>
+
+<style scoped lang="scss">
+  .box-style {
+    background: url('@/assets/images/box-bg.png') no-repeat center;
+    background-size: 100%;
+  }
+</style>

+ 19 - 0
src/views/home/leftboxtwo/index.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="w-453px flex flex-col gap-15px lr-box-bg overflow-hidden">
+    <Box title="概况2" wHeader="60px">
+      <TopBox />
+    </Box>
+    <Box title="设备信息2">
+      <MiddleBox />
+    </Box>
+    <Box title="用户情况2">
+      <BottomBox />
+    </Box>
+  </div>
+</template>
+
+<script setup>
+  import TopBox from './topBox.vue'
+  import MiddleBox from './middleBox.vue'
+  import BottomBox from './bottomBox.vue'
+</script>

+ 94 - 0
src/views/home/leftboxtwo/middleBox.vue

@@ -0,0 +1,94 @@
+<template>
+  <div class="left-top-box">
+    <div class="flex gap-15px h-90px">
+      <div
+        class="box-style color- flex-1 flex flex-col flex-justify-center flex-items-center gap-2px"
+      >
+        <span>监控点位</span>
+        <Countto class="text-20px color-#389bff font-bold" :endVal="80" />
+      </div>
+      <div class="box-style flex-1 flex flex-col flex-justify-center flex-items-center gap-2px">
+        <span>智能分析</span>
+        <Countto class="text-20px color-#389bff font-bold" :endVal="60" />
+      </div>
+    </div>
+    <swiper
+      :spaceBetween="30"
+      :centeredSlides="true"
+      :autoplay="{
+        delay: 5000,
+        disableOnInteraction: false,
+        pauseOnMouseEnter: true
+      }"
+      :pagination="{
+        clickable: true
+      }"
+      :modules="modules"
+      class="mySwiper"
+    >
+      <swiper-slide>
+        <ECharts
+          id="sbxx-echarts3"
+          width="100%"
+          height="280px"
+          :loading="loading"
+          :fullOptions="{ options: chartOptions.setSbxxOption() }"
+        />
+      </swiper-slide>
+      <swiper-slide>
+        <ECharts
+          id="sbxx-echarts4"
+          width="100%"
+          height="280px"
+          ref="echartsRef"
+          :loading="loading"
+          :fullOptions="{ options: chartOptions.setSbxxOption2() }"
+        />
+      </swiper-slide>
+    </swiper>
+  </div>
+</template>
+
+<script setup>
+  import { chartOptions } from '@/components/ECharts/optionsConfig'
+  import { Swiper, SwiperSlide } from 'swiper/vue'
+  import { Autoplay, Pagination, Navigation } from 'swiper/modules'
+  import 'swiper/css'
+
+  import 'swiper/css/pagination'
+  import 'swiper/css/navigation'
+
+  const modules = [Autoplay, Pagination, Navigation]
+  const loading = ref(true)
+  const echartsRef = ref(null)
+
+  onMounted(() => {
+    setTimeout(() => {
+      loading.value = false
+    }, 1000)
+  })
+</script>
+
+<style scoped lang="scss">
+  .box-style {
+    background: url('@/assets/images/box-bg.png') no-repeat center;
+    background-size: 100% 100%;
+  }
+  .left-top-box {
+    :deep(.swiper) {
+      width: 100%;
+      height: 300px;
+      .swiper-slide {
+        height: 100%;
+      }
+      .swiper-pagination {
+        --swiper-pagination-bottom: -5px;
+      }
+      .swiper-pagination-bullet {
+        --swiper-pagination-bullet-width: 20px;
+        --swiper-pagination-bullet-height: 4px;
+        border-radius: 0;
+      }
+    }
+  }
+</style>

+ 113 - 0
src/views/home/leftboxtwo/topBox.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="coverflow-example">
+    <swiper
+      class="swiper hover:cursor-grab active:cursor-grabbing"
+      :modules="modules"
+      :effect="'coverflow'"
+      :centered-slides="true"
+      :initialSlide="1"
+      :slides-per-view="3"
+      :coverflow-effect="{
+        rotate: 0,
+        stretch: -5,
+        depth: 120,
+        modifier: 2,
+        slideShadows: true
+      }"
+      @slideChange="swiperChange"
+    >
+      <swiper-slide class="slide" v-for="index in 3" :key="index">
+        <img src="@/assets/images/swiper.png" />
+      </swiper-slide>
+    </swiper>
+    <ScrollPanel
+      style="width: 100%; height: 150px"
+      :pt="{
+        barY: '!bg-#6286b6c2'
+      }"
+    >
+      <div v-for="(item, i) in dataList" :key="i" class="text-18px color-#f0f0f0">
+        <template v-if="active === i">
+          <p>{{ item.name }}</p>
+          <p>{{ item.content }}</p>
+        </template>
+      </div>
+    </ScrollPanel>
+  </div>
+</template>
+
+<script setup>
+  import { Pagination, EffectCoverflow } from 'swiper/modules'
+  import { Swiper, SwiperSlide } from 'swiper/vue'
+  import 'swiper/css'
+  import 'swiper/css/pagination'
+  import 'swiper/css/effect-coverflow'
+
+  const modules = [EffectCoverflow]
+  const active = ref(1)
+
+  const dataList = ref([
+    {
+      name: '厂区介绍1',
+      content: `稳高压消防给水系统是现今化工生产厂区最为常见的给水系统,
+          一般由消防水泵、给水管道以及消防水池等设施组成。当我们对消防系统进行设置时,
+          如果其中具有消防通道,
+          则需要做好消防装置外管网以及内消防管网的连接。而对于部分体积小的生产装置,
+          我们则可以通过整合方式的应用对消防管网进行整合,
+          并根据厂区实际情况在必要位置通过支管的应用对消防设施进行设置。在环状管网方面,
+          则需要将阀门分为几个不同的管段, 并在独立管段位置上做好消防设施的设置。在企业灌区,
+          通常情况下都具有着较多数量的石油原料, 如果这部分材料发生了火情,
+          仅仅使用水很难起到良好的灭火作用, 对此,
+          则可以使用固定泡沫灭火系统作为该区域的消防措施。在灭火装置中,
+          装置的是具有抗熔特征的泡沫液, 在化工企业灌区, 需要保证该装置中的泡沫两为160L/s,
+          以此保证灌区出现火灾问题时能够做出持续的灭火控制作用。而在火灾情况出现时,
+          该区域控制室则能够通过信号的接收, 在掌握厂区内出现火情之后对消防水泵进行及时的启动,
+          保证灭火工作能够在第一时间开展。`
+    },
+    {
+      name: '厂区介绍2',
+      content: `在化工生产区中, 所设置消防设施的目的就是要保证厂区在火灾情况出现时能够及时对火情进行控制, 对此, 就需要在设置完毕后做好相关设施的维护工作。而在消火栓以及消防水炮设置时, 则需要其位置能够控制在目标范围之内, 并保证目标对象通消防设施间需要良好贯通, 避免存在障碍物对消防效果产生影响。而在消火栓设置时, 避免对阀门进行随意的切断, 尽可能将水炮设置为蝶阀类型, 并对管道做好保温措施。而对于防火堤区域之外的水炮, 则需要做好其高度的控制, 需要在能够高于防火堤顶部的同时使其在实际喷射过程中产生的水流能够良好的覆盖到根部位置。`
+    },
+    {
+      name: '厂区介绍3',
+      content: `稳高压消防给水系统是现今化工生产厂区最为常见的给水系统,
+          一般由消防水泵、给水管道以及消防水池等设施组成。当我们对消防系统进行设置时,
+          如果其中具有消防通道,
+          则需要做好消防装置外管网以及内消防管网的连接。而对于部分体积小的生产装置,
+          我们则可以通过整合方式的应用对消防管网进行整合,
+          并根据厂区实际情况在必要位置通过支管的应用对消防设施进行设置。在环状管网方面,
+          则需要将阀门分为几个不同的管段, 并在独立管段位置上做好消防设施的设置。在企业灌区,
+          通常情况下都具有着较多数量的石油原料, 如果这部分材料发生了火情,
+          仅仅使用水很难起到良好的灭火作用`
+    }
+  ])
+
+  const swiperChange = (v) => {
+    console.log(v)
+    active.value = v.activeIndex
+  }
+</script>
+
+<style lang="scss" scoped>
+  .coverflow-example {
+    position: relative;
+    height: 100%;
+  }
+
+  .swiper {
+    height: 100%;
+    width: 100%;
+    padding-bottom: 15px;
+
+    .slide {
+      width: 300px;
+
+      img {
+        display: block;
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+      }
+    }
+  }
+</style>

+ 1 - 3
src/views/home/tool/index.vue → src/views/home/middleTopBox/components/tool.vue

@@ -81,8 +81,6 @@
   ])
 
   const toolClick = (item) => {
-    console.log(123)
-
     item.selected = !item.selected
   }
 
@@ -122,6 +120,6 @@
   ])
   const twoClick = (i) => {
     twoActive.value = i
-    $mitt.emit('onTheme', i)
+    $mitt.emit('onThemeIndex', i)
   }
 </script>

+ 1 - 1
src/views/home/middleTopBox/index.vue

@@ -20,7 +20,7 @@
 
 <script setup>
   import { useOutsideHomeStore } from '@/stores/modules/home'
-  import Tool from '../tool/index.vue'
+  import Tool from './components/tool.vue'
 
   const useHomeStore = useOutsideHomeStore()
 </script>

+ 1 - 1
src/views/home/rightbox/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="w-453px flex flex-col gap-15px lr-box-bg">
+  <div class="w-453px flex flex-col gap-15px lr-box-bg overflow-hidden">
     <Box title="报警中心">
       <RightTopBox />
     </Box>

+ 31 - 0
src/views/home/rightboxtwo/bottomBox.vue

@@ -0,0 +1,31 @@
+<template>
+  <div class="right-bottom-box">
+    <ECharts
+      id="bjlx-echarts2"
+      width="100%"
+      height="290px"
+      :loading="loading"
+      :fullOptions="fullOptions"
+    />
+  </div>
+</template>
+
+<script setup>
+  import { chartOptions } from '@/components/ECharts/optionsConfig'
+  const loading = ref(true)
+
+  const fullOptions = ref({ options: {} })
+  onMounted(() => {
+    setTimeout(() => {
+      loading.value = false
+      fullOptions.value.options = chartOptions.setBjlxOption([])
+    }, 1000)
+  })
+</script>
+
+<style scoped lang="scss">
+  .box-style {
+    background: url('@/assets/images/box-bg.png') no-repeat center;
+    background-size: 100% 100%;
+  }
+</style>

+ 15 - 0
src/views/home/rightboxtwo/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div class="w-453px flex flex-col gap-15px lr-box-bg overflow-hidden">
+    <Box title="报警中心2">
+      <RightTopBox />
+    </Box>
+    <Box title="报警类型TOP52" wHeader="124px">
+      <RightBottomBox />
+    </Box>
+  </div>
+</template>
+
+<script setup>
+  import RightTopBox from './topBox.vue'
+  import RightBottomBox from './bottomBox.vue'
+</script>

+ 164 - 0
src/views/home/rightboxtwo/topBox.vue

@@ -0,0 +1,164 @@
+<template>
+  <div class="right-top-box">
+    <div class="flex gap-15px h-90px mb-15px">
+      <div
+        class="box-style color- flex-1 flex flex-col flex-justify-center flex-items-center gap-2px"
+      >
+        <span>已处理</span>
+        <Countto class="text-20px color-#379ED7 font-bold" :endVal="data.ycl" />
+      </div>
+      <div class="box-style flex-1 flex flex-col flex-justify-center flex-items-center gap-2px">
+        <span>未处理</span>
+        <Countto class="text-20px color-#F0BC1E font-bold" :endVal="data.wcl" />
+      </div>
+    </div>
+    <div class="flex justify-between relative">
+      <ul class="flex gap-15px justify-start">
+        <li
+          :class="[
+            'w-50px h-30px line-height-25px cursor-pointer text-center b-solid b-1px b-color-#389bff hover:b-color-#FAC858',
+            i === typeSelectedI ? 'color-#FAC858 b-color-#FAC858' : ''
+          ]"
+          v-for="(item, i) in typeOptions"
+          :key="item.code"
+          @click="typeClick(i)"
+        >
+          <span class="text-12px">{{ item.name }}</span>
+        </li>
+      </ul>
+      <ul class="flex gap-15px">
+        <li
+          :class="[
+            'w-50px cursor-pointer text-center b-solid b-1px b-color-#389bff hover:b-color-#FAC858',
+            i === selectedI ? 'color-#FAC858 b-color-#FAC858' : ''
+          ]"
+          v-for="(item, i) in options"
+          :key="item.code"
+          @click="dayClick(i)"
+        >
+          <span class="text-12px">{{ item.name }}</span>
+        </li>
+      </ul>
+    </div>
+    <ECharts
+      id="bjzx-echarts2"
+      width="100%"
+      height="200px"
+      :loading="loading"
+      :fullOptions="fullOptions"
+    />
+    <div class="table-box w-full">
+      <div class="text-left bg-#389bff94 flex">
+        <span class="p-2 w-60px">报警</span>
+        <span class="p-2 w-80px">报警类型</span>
+        <span class="p-2 flex-1">报警时间</span>
+        <span class="p-2 w-80px">报警级别</span>
+        <span class="p-2 w-60px">处理</span>
+      </div>
+      <ScrollBox :list="data.list">
+        <div
+          :class="[
+            'text-left  text-10px hover:bg-#389bff24 flex',
+            (i + 1) % 2 == 0 ? 'bg-#ffffff24' : ''
+          ]"
+          v-for="(item, i, index) in data.list"
+          :key="index"
+        >
+          <span class="p-2 w-60px">{{ item.name }}</span>
+          <span class="p-2 w-80px">{{ item.type }}</span>
+          <span class="p-2 flex-1">{{ item.time }}</span>
+          <span class="p-2 w-80px">{{ item.level ? '告警' : '故障' }}</span>
+          <span :class="['p-2 w-60px', item.cl ? 'color-#389bff' : 'color-red']">{{
+            item.cl ? '已处理' : '未处理'
+          }}</span>
+        </div>
+      </ScrollBox>
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { chartOptions } from '@/components/ECharts/optionsConfig'
+  const loading = ref(true)
+  const data = reactive({
+    ycl: 105,
+    wcl: 2,
+    list: [
+      {
+        name: 'XY7809',
+        type: '工服',
+        time: '2022-03-01 12:00:00',
+        level: 0,
+        cl: 0
+      },
+      {
+        name: 'XY7809',
+        type: '烟雾',
+        time: '2024-03-01 12:00:00',
+        level: 1,
+        cl: 1
+      },
+      {
+        name: 'XY7809',
+        type: '工服',
+        time: '2022-03-01 12:00:00',
+        level: 0,
+        cl: 0
+      },
+      {
+        name: 'XY7809',
+        type: '烟雾',
+        time: '2024-03-01 12:00:00',
+        level: 1,
+        cl: 1
+      },
+      {
+        name: 'XY7809',
+        type: '工服',
+        time: '2022-03-01 12:00:00',
+        level: 0,
+        cl: 0
+      },
+      {
+        name: 'XY7809',
+        type: '烟雾',
+        time: '2024-03-01 12:00:00',
+        level: 1,
+        cl: 1
+      }
+    ]
+  })
+  const selectedI = ref(0)
+  const fullOptions = ref({ options: {} })
+
+  const options = ref([
+    { name: '当日', code: 0 },
+    { name: '近一周', code: 1 },
+    { name: '近一月', code: 2 }
+  ])
+  const dayClick = (v) => {
+    selectedI.value = v
+    fullOptions.value.options = chartOptions.setBjzxOption([], selectedI.value)
+  }
+  const typeSelectedI = ref(0)
+  const typeOptions = ref([
+    { name: '趋势', code: 0 },
+    { name: '类型', code: 1 }
+  ])
+  const typeClick = (v) => {
+    typeSelectedI.value = v
+  }
+  onMounted(() => {
+    setTimeout(() => {
+      loading.value = false
+      fullOptions.value.options = chartOptions.setBjzxOption([], selectedI.value)
+    }, 1000)
+  })
+</script>
+
+<style scoped lang="scss">
+  .box-style {
+    background: url('@/assets/images/box-bg.png') no-repeat center;
+    background-size: 100% 100%;
+  }
+</style>