Browse Source

fix🐛: 修复测试问题

gitboyzcf 2 weeks ago
parent
commit
91d0d9f711

+ 1 - 1
.env.development

@@ -7,4 +7,4 @@ VITE_APP_TITLE = 智能安全管控平台
 VITE_APP_PREFIX = demo
 
 # 网络请求
-VITE_APP_API_BASEURL = http://192.168.105.40:8082/api/mr
+VITE_APP_API_BASEURL = http://192.168.211.58:8082/api/mr

+ 0 - 1
src/App.vue

@@ -15,7 +15,6 @@
   import { useOutsideSystemStore } from '@/stores/modules/system.js'
 
   const useSystem = useOutsideSystemStore()
-  useSystem.getPermissions()
 
   const locale = shallowRef(zhCN)
   const dateLocale = shallowRef(dateZhCN)

+ 1 - 1
src/api/service.js

@@ -37,7 +37,7 @@ export function createService() {
             // 不是正确的 code
             return requestError(response)
           case '401':
-            requestError(response)
+            throttleToLogin()
             // 错误登录
             return throttleToLogin()
           default:

+ 2 - 1
src/components/ECharts/ECharts.vue

@@ -67,7 +67,7 @@
   const showChartLoading = () => {
     myChart.value?.showLoading({
       text: '加载中...',
-      color: '#409eff',
+      color: '#2E89F3',
       maskColor: 'rgba(255, 255, 255, 0)',
       zlevel: 0
     })
@@ -114,6 +114,7 @@
   onBeforeUnmount(() => {
     window.removeEventListener('resize', handleResize)
     if (myChart.value) {
+      myChart.value.clear()
       myChart.value.dispose()
       myChart.value = null
     }

+ 124 - 18
src/layout/Map.vue

@@ -3,7 +3,8 @@
 </template>
 
 <script setup>
-  import { Map, View } from 'ol'
+  import { useModal } from 'naive-ui'
+  import { Map, View, Overlay } from 'ol'
   import SourceVector from 'ol/source/Vector'
   import LayerVector from 'ol/layer/Vector'
   import Cluster from 'ol/source/Cluster.js'
@@ -11,16 +12,19 @@
   // 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 { fromLonLat, toLonLat } from 'ol/proj'
   import { boundingExtent } from 'ol/extent.js'
+  import { useOutsideHomeStore } from '@/stores/modules/home'
   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 { useOutsideHomeStore } from '@/stores/modules/home'
+  import AddDevice from './components/addDevice.vue'
 
   const useHomeStore = useOutsideHomeStore()
+  const { API_MR_CAMERA_GET, API_MR_VIDEO_LIST_GET } = useRequest()
 
+  const modal = useModal()
   const map = shallowRef(null)
 
   const labelStyle = new Style({
@@ -277,23 +281,33 @@
   }
 
   const currentDis = ref(150)
+  // 根据points创建一个新的数据源和要素数组,
+  const vectorSource = new SourceVector()
   // 根据数据创建聚合图层
-  const createCluster = (points, zindex) => {
-    const features = points.map((e) => {
-      // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
+  const createCluster = (points, custom = {}, zindex) => {
+    if (custom.type === 'add') {
       const feature = new Feature({
-        geometry: new Point(fromLonLat(e)),
+        geometry: new Point(fromLonLat(points[0])),
         custom: {
-          id: Math.ceil(Math.random() * 100000)
+          ...custom
         }
       })
-      return feature
-    })
-    // 根据points创建一个新的数据源和要素数组,
-    const vectorSource = new SourceVector({
-      features
-    })
-
+      vectorSource.addFeature(feature)
+    } else {
+      const features = points.map(([x, y, RtspMain, id]) => {
+        // ol.proj.fromLonLat用于将经纬度坐标从 WGS84 坐标系转换为地图投影坐标系
+        const feature = new Feature({
+          geometry: new Point(fromLonLat([x, y])),
+          custom: {
+            ...custom,
+            id,
+            RtspMain
+          }
+        })
+        return feature
+      })
+      vectorSource.addFeatures(features)
+    }
     // 根据点位创建聚合资源
     const clusterSource = new Cluster({
       distance: currentDis.value, // 设置多少像素以内的点位进行聚合
@@ -309,6 +323,7 @@
     // 将矢量图层添加到地图上
     map.value.addLayer(clusters)
     // sv.setZIndex(zindex) // 设置层级
+
     return clusters
   }
 
@@ -361,6 +376,13 @@
     }
   }
 
+  const contextMenu = document.createElement('div')
+  contextMenu.className = 'ol-context-menu'
+  contextMenu.innerHTML = `
+    <div ><div class="py-2 px-4 bg-#2262acb3 hover:bg-#2262ac" data-action="create-point">创建点位</div>
+`
+  contextMenu.style.display = 'none' // 初始隐藏
+
   /**
    * 在地图上设置一个标注。
    *
@@ -449,6 +471,29 @@
     return vectorLayer
   }
 
+  let clusters = null
+  const getPoint = () => {
+    API_MR_CAMERA_GET().then((res) => {
+      const points = res.map((item) => toLonLat([item.X, item.Y]).concat([item.RtspMain, item.Id]))
+      clusters = createCluster(points, {})
+      console.log(clusters)
+    })
+  }
+
+  const createOverlay = (map) => {
+    const overlay = new Overlay({
+      element: contextMenu,
+      positioning: 'bottom-left', // 菜单的定位点相对于坐标
+      offset: [0, 0], // 偏移量
+      autoPan: true,
+      autoPanAnimation: {
+        duration: 250
+      }
+    })
+    map.addOverlay(overlay)
+    return overlay
+  }
+
   const initMap = () => {
     map.value = new Map({
       target: 'map',
@@ -466,11 +511,11 @@
     })
     const vlText = setMark('text', { text: '中华人民共和国' })
     const vlImg = []
-    const points = createPointsByRange(100)
     // mapData.value.forEach((item) => {
     //   vlImg.push(setMark('img', { src: videoIcon, ...item }, [item.longitude, item.latitude]))
     // })
-    const clusters = createCluster(points)
+    getPoint()
+    // const clusters = createCluster(points)
     // 缩放显示隐藏层级
     const zoomChange = function (e) {
       var zoom = parseInt(map.value.getView().getZoom()) //获取当前地图的缩放级别
@@ -521,22 +566,83 @@
         })
       }
     }
+    let coordinate = null
+    const contextMenuClick = (event) => {
+      const action = event.target.getAttribute('data-action')
+      if (action) {
+        switch (action) {
+          case 'create-point':
+            console.log('创建点位')
+            // 在这里添加创建点位的逻辑
+            modal.create({
+              title: '新建点位',
+              preset: 'card',
+              maskClosable: false,
+              trapFocus: false,
+              style: {
+                marginTop: '10%'
+              },
+              content: () =>
+                h(AddDevice, {
+                  intersection: {
+                    x: coordinate ? coordinate[0] : 0,
+                    y: coordinate ? coordinate[1] : 0,
+                    z: 0
+                  },
+                  onCloseAddTagModal: ({ x, y, z, RtspMain, id }) => {
+                    createCluster([toLonLat([x, y])], {
+                      RtspMain,
+                      id,
+                      type: 'add'
+                    })
+                    modal.destroyAll()
+                  }
+                })
+            })
+            break
+          default:
+            break
+        }
+        contextMenu.style.display = 'none' // 隐藏菜单
+      }
+    }
     map.value.on('click', (e) => {
+      contextMenu.style.display = 'none'
+      contextMenu.removeEventListener('click', contextMenuClick)
+      console.log(clusters)
+
       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.vId = features[0].get('custom').RtspMain
             useHomeStore.temp = 'video'
           }
         }
       })
     })
+    // 右键添加标记
+    map.value.on('contextmenu', (event) => {
+      console.log(event)
+
+      event.preventDefault() // 阻止默认右键菜单
+      if (!event.pixel) {
+        console.error('点击位置不在地图范围内')
+        return
+      }
+      const overlay = createOverlay(map.value)
+      coordinate = event.coordinate
+      overlay.setPosition(coordinate)
+      contextMenu.style.display = 'block'
+      contextMenu.addEventListener('click', contextMenuClick)
+      console.log('右键点击坐标:', coordinate)
+      // setMark('img', { src: videoIcon }, coordinate) // 添加标记,使用图片作为标记
+    })
     map.value.getView().on('change:resolution', zoomChange)
     zoomChange()
 

+ 8 - 1
src/layout/Three3D/components/addDevice.vue

@@ -86,7 +86,14 @@
       }
       API_MR_CAMERA_POST(params).then((res) => {
         props.addSprite([
-          { x: params.X, y: params.Y, z: params.Z, RtspMain: params.RtspMain, id: res.id }
+          {
+            x: params.X,
+            y: params.Y,
+            z: params.Z,
+            RtspMain: params.RtspMain,
+            id: res.id,
+            name: row.name
+          }
         ])
         emits('closeAddTagModal')
       })

+ 51 - 7
src/layout/Three3D/index.vue

@@ -17,6 +17,20 @@
       :y="rightClickD.y"
       :intersection="rightClickD.intersection"
     />
+    <div
+      id="tooltip"
+      style="
+        position: absolute;
+        background: rgba(0, 0, 0, 0.8);
+        color: white;
+        padding: 8px 12px;
+        border-radius: 4px;
+        font-size: 14px;
+        display: none;
+        pointer-events: none;
+        z-index: 100;
+      "
+    ></div>
   </div>
 </template>
 
@@ -83,7 +97,7 @@
   }
   const sprites = []
   function addSprite(arr = []) {
-    arr.forEach(({ x, y, z, id, RtspMain }) => {
+    arr.forEach(({ x, y, z, id, RtspMain, Alias, name }) => {
       const sprite = new THREE.Sprite(
         new THREE.SpriteMaterial({
           map: new THREE.TextureLoader().load('textures/video-icon.png'),
@@ -94,7 +108,8 @@
       )
       sprite.vData = {
         id,
-        RtspMain
+        RtspMain,
+        Alias: Alias ? Alias : name
       }
       sprite.position.set(x, y, z)
       sprite.scale.set(0.4, 0.45, 0.5)
@@ -132,6 +147,36 @@
     })
 
     dom.addEventListener('contextmenu', onRightClick)
+    dom.addEventListener('mousemove', onMouseMove)
+  }
+
+  let hoveredObject,
+    tooltip = null
+
+  function onMouseMove(event) {
+    // 1. 获取鼠标位置(归一化到 -1 到 1)
+    pointer.x = (event.clientX / window.innerWidth) * 2 - 1
+    pointer.y = -(event.clientY / window.innerHeight) * 2 + 1
+
+    // 2. 设置光线投射器
+    raycaster.setFromCamera(pointer, camera)
+
+    const intersects = raycaster.intersectObjects(sprites)
+
+    // 4. 处理悬停
+    if (intersects.length > 0) {
+      const object = intersects[0].object
+
+      if (hoveredObject !== object) {
+        // 显示提示
+        tooltip.style.display = 'block'
+        tooltip.textContent = object.vData.Alias
+        tooltip.style.left = event.clientX / getAutofitScale() + 15 + 'px'
+        tooltip.style.top = event.clientY / getAutofitScale() - 15 + 'px'
+      }
+    } else {
+      tooltip.style.display = 'none'
+    }
   }
   function onRightClick(event) {
     event.preventDefault()
@@ -201,6 +246,7 @@
     controls = new OrbitControls(camera, renderer.domElement)
     controls.enableDamping = true
     controls.dampingFactor = 0.05
+    tooltip = document.getElementById('tooltip')
 
     // 加载模型
     percentageFlag.value = true
@@ -227,10 +273,7 @@
           duration: 1,
           ease: 'power1.inOut',
           onComplete: async () => {
-            if (
-              useSystemStore.permissions &&
-              !hasPermission(['mrdevicePointShow'], useSystemStore.permissions)
-            ) {
+            if (useSystemStore.permissions && !hasPermission(['mrdevicePointShow'])) {
               return
             }
             const res = await API_MR_CAMERA_GET()
@@ -245,7 +288,8 @@
               y: item.Y,
               z: item.Z,
               id: item.Id,
-              RtspMain: item.RtspMain
+              RtspMain: item.RtspMain,
+              Alias: item.Alias
             }))
             addSprite(sprites)
           }

+ 128 - 0
src/layout/components/addDevice.vue

@@ -0,0 +1,128 @@
+<template>
+  <n-data-table :columns="columns" :data="data" bordered max-height="500px" />
+  <div class="flex justify-end mt-2">
+    <n-pagination
+      v-model:page="pagination.page"
+      :page-sizes="pagination.pageSizes"
+      :page-count="pagination.pageSize"
+      :on-update:page="pagination.onChange"
+      :on-update:page-size="pagination.onUpdatePageSize"
+      show-quick-jumper
+      show-size-picker
+    />
+  </div>
+</template>
+
+<script setup>
+  import { NDataTable, NButton, NPagination, NSelect } from 'naive-ui'
+  const { API_MR_CAMERA_POST, API_DEVICES_GET } = useRequest()
+  const props = defineProps({
+    intersection: Object,
+    addSprite: Function
+  })
+  const emits = defineEmits(['closeAddTagModal'])
+
+  function createColumns({ handleSubmit }) {
+    return [
+      {
+        title: '设备序列号',
+        key: 'sn'
+      },
+      {
+        title: '设备名称',
+        key: 'name'
+      },
+      {
+        title: '设备状态',
+        key: 'status',
+        render(row) {
+          return h(
+            'span',
+            { class: row.status === 1 ? 'color-green' : 'color-red' },
+            row.status === 1 ? '在线' : '离线'
+          )
+        }
+      },
+      {
+        title: '设备版本',
+        key: 'version'
+      },
+      {
+        title: '设备IP',
+        key: 'ip_address'
+      },
+      {
+        title: '设备端口',
+        key: 'port'
+      },
+      {
+        title: '操作',
+        key: 'actions',
+        render(row) {
+          return h(
+            NButton,
+            {
+              strong: true,
+              tertiary: true,
+              size: 'small',
+              onClick: () => handleSubmit(row)
+            },
+            { default: () => '绑定' }
+          )
+        }
+      }
+    ]
+  }
+  const data = ref([])
+  const columns = createColumns({
+    handleSubmit(row) {
+      const params = {
+        X: props.intersection.x, //必传
+        Y: props.intersection.y, //必传
+        Z: props.intersection.z, //必传
+        Type: 'FallView', //必传
+        RtspMain: row.sn,
+        Alias: row.name
+      }
+      API_MR_CAMERA_POST(params).then((res) => {
+        emits('closeAddTagModal', {
+          x: params.X,
+          y: params.Y,
+          z: params.Z,
+          RtspMain: params.RtspMain,
+          id: res.id
+        })
+      })
+    }
+  })
+  const pagination = reactive({
+    page: 1,
+    pageSize: 10,
+    showSizePicker: true,
+    pageSizes: [10, 50, 100],
+    onChange: (page) => {
+      pagination.page = page
+      init()
+    },
+    onUpdatePageSize: (pageSize) => {
+      pagination.pageSize = pageSize
+      pagination.page = 1
+      init()
+    }
+  })
+
+  const init = async () => {
+    const res = await API_DEVICES_GET({
+      page: pagination.page,
+      limit: pagination.pageSize
+    })
+    data.value = res?.items || []
+    pagination.total = res?.total_items
+    pagination.page = res?.current
+    pagination.pageSize = res?.limit
+    console.log(res)
+  }
+  onMounted(() => {
+    init()
+  })
+</script>

+ 3 - 0
src/main.js

@@ -6,6 +6,7 @@ import { i18n } from '@/locales'
 import { setupStore } from '@/stores'
 import { setupRouter, router } from './router'
 import { urlParamsLogin } from './router/helper'
+import { useOutsideSystemStore } from '@/stores/modules/system.js'
 
 import './assets/main.css'
 import 'virtual:uno.css'
@@ -15,6 +16,7 @@ import '@/assets/scss/adapter.scss'
 import '@/assets/scss/theme.scss'
 
 async function bootstrap() {
+  const useSystem = useOutsideSystemStore()
   const app = createApp(App)
 
   app.use(directives)
@@ -22,6 +24,7 @@ async function bootstrap() {
 
   await setupStore(app)
   await urlParamsLogin(router)
+  await useSystem.getPermissions()
   await setupRouter(app)
 
   app.mount('#app')

+ 2 - 1
src/stores/modules/home.js

@@ -9,6 +9,7 @@ export const useHomeStore = defineStore('home', () => {
   const codes = ref([{ name: '中国', code: 100000 }])
   const isNull = ref(false)
   const temp = ref('map')
+  const vId = ref('')
   function setCode(code) {
     isNull.value = true
     codes.value.push(code)
@@ -29,7 +30,7 @@ export const useHomeStore = defineStore('home', () => {
     { deep: true }
   )
 
-  return { codes, isNull, setCode, clear, middleBottomBoxShow, temp }
+  return { codes, isNull, setCode, clear, middleBottomBoxShow, temp, vId }
 })
 
 export function useOutsideHomeStore() {

+ 37 - 9
src/stores/modules/system.js

@@ -101,7 +101,7 @@ export const useSystemStore = defineStore('systemStore', {
     },
     // 连接报警信息
     async connectAlarmWS() {
-      if (this.permissions && !hasPermission(['mralarmMessageList'], this.permissions)) {
+      if (this.permissions && !hasPermission(['mralarmMessageList'])) {
         return
       }
       const useHomeStore = useOutsideHomeStore()
@@ -174,7 +174,7 @@ export const useSystemStore = defineStore('systemStore', {
                       dayjs(parseInt(`${data.time}`.padEnd(13, '0'))).format('YYYY-MM-DD HH:mm:ss')
                     )
                   ]),
-                  that.permissions && !hasPermission(['mralarmVideoShow'], this.permissions)
+                  !hasPermission(['mralarmVideoShow'])
                     ? null
                     : h(
                         NButton,
@@ -303,14 +303,42 @@ export const useSystemStore = defineStore('systemStore', {
         const parentUID =
           storage.cookie.get(window.top.document, 'Admin-UID') ||
           storage.cookie.get(window, 'Admin-UID')
-        API_RO_GET({ userId: parentUID }).then((res) => {
-          if (!res) {
-            reject(new Error('权限获取失败'))
-          }
+        API_RO_GET({ userId: 123 })
+          .then((res) => {
+            if (!res) {
+              reject(new Error('权限获取失败'))
+            }
 
-          this.permissions = res.map((item) => item.resource_id + item.operation)
-          resolve(res.permissions)
-        })
+            this.permissions = res.map((item) => item.resource_id + item.operation)
+            // this.permissions = [
+            //   'mrdashboardShow',
+            //   'mrdevicePointShow',
+            //   'mraddPointBtn',
+            //   'mralarmMessageList',
+            //   'mralarmVideoShow',
+            //   'mrhistoricalAlarmBtn',
+            //   'mrpanoramicPlaybackBtn',
+            //   'mrscreenshotBtn',
+            //   'mrrecordingBtn',
+            //   'mrroamingMagnifierBtn',
+            //   'mrfullScreenBtn',
+            //   'mrpanoramicTagShow',
+            //   'mrtagCreationBtn',
+            //   'mrpartScreenMenu',
+            //   'mrglanceViewMenu',
+            //   'mradvancedLinkageMenu',
+            //   'mrtourMenu'
+            // ]
+
+            resolve(res.permissions)
+          })
+          .catch((err) => {
+            reject(err)
+          })
+        // setTimeout(() => {
+        //   this.permissions = []
+        //   resolve(this.permissions)
+        // }, 1000)
       })
     }
   }

+ 1 - 1
src/views/VideoBox/RemotePlayback/RemotePlayback.vue

@@ -176,7 +176,7 @@
         new Promise((resolve) => {
           API_SR_GET({
             device_id: props.device_id,
-            form,
+            from: form,
             to
           }).then((res) => {
             resolve(res)

+ 22 - 21
src/views/VideoBox/components/TagInfo.vue

@@ -115,7 +115,7 @@
             >
               <Icon icon="loading" width="30px" height="30px" color="#8ac5ff" />
             </div>
-            <pub-video
+            <pub-video-new
               v-if="item.value"
               class="h-full"
               :newClass="[
@@ -318,6 +318,7 @@
   import useWorker from 'omnimatrix-video-player'
   import { UseFullscreen } from '@vueuse/components'
   import { useOutsideSystemStore } from '@/stores/modules/system.js'
+  import { omatVideoPlayer } from '@/assets/js/video-lib/flv/omatVideoPlayer'
 
   const useSystem = useOutsideSystemStore()
   const pubPlayerRefs = ref([])
@@ -435,20 +436,32 @@
       initVideo(formData.value)
     })
   }
-  const workerObj = ref([])
+  const workerObj = shallowRef([])
   provide('workerObj', workerObj)
   const initVideo = async (arr) => {
     nextTick(() => {
       arr.forEach((item) => {
         if (!item.value) return
         const isBallPtz = item.type === 'ball_ptz'
-        if (item.type === 'video_s' || isBallPtz) {
+        if (item.type === 'video_s') {
           item.loading = true
-          const url = `wss://${useSystem.ipF}/VideoShow/Rtsp?rtsp=${isBallPtz ? item.value[0].url : item.value}`
+          // const url = `wss://${useSystem.ipF}/VideoShow/Rtsp?rtsp=${isBallPtz ? item.value[0].url : item.value}`
+
           workerObj.value.push(
-            useWorker(url, '.tag_video' + item.id, () => {
-              item.loading = false
-            })
+            // useWorker(url, '.tag_video' + item.id, () => {
+            //   item.loading = false
+            // })
+            omatVideoPlayer(
+              '.tag_video' + item.id,
+              item.value,
+              () => {
+                item.loading = false
+              },
+              {
+                enableZoom: false,
+                enableDrag: false
+              }
+            )
           )
         }
       })
@@ -456,24 +469,12 @@
   }
   const clearWorker = () => {
     return new Promise((resolve) => {
-      resizeViode.value = false
       for (let i = 0; i < workerObj.value.length; i++) {
-        workerObj.value[i].worker.terminate()
-        workerObj.value[i].WebSocketWork.terminate()
+        console.log(workerObj.value[i])
+        workerObj.value[i]?.destroyed()
       }
-      workerObj.value = []
-      setTimeout(() => {
-        resizeViode.value = true
-        resolve()
-      }, 60)
     })
   }
-
-  // 关闭线程
-  // const closeWorker = (index) => {
-  //   if (!workerObj[index]) return
-  //   workerObj[index].terminate()
-  // }
   const update = () => {
     getGroupFormat(props.content)
   }

+ 2 - 1
src/views/VideoBox/components/addTag.vue

@@ -198,7 +198,8 @@
           X: tagForm.X,
           Y: tagForm.Y,
           Color: tagForm.Color,
-          Follow: `${tagForm.Follow}`
+          Follow: `${tagForm.Follow}`,
+          AreaId: +tagForm.AreaId
         }).then((res) => {
           API_TAG_DATA_PUT({ TagUUID: res.TagUUID }, tagContent).then(() => {
             msg('success', '添加成功')

+ 40 - 23
src/views/VideoBox/index.vue

@@ -31,11 +31,16 @@
                 v-auth="item.auth"
                 @click.stop="toolsHandler(item.type)"
               >
-                <div
-                  :class="[item.iCom, 'cursor-pointer']"
-                  :style="{ width: item.w + 'px', height: item.h + 'px', color: item.color }"
-                >
-                </div>
+                <n-tooltip trigger="hover" placement="bottom">
+                  <template #trigger>
+                    <div
+                      :class="[item.iCom, 'cursor-pointer']"
+                      :style="{ width: item.w + 'px', height: item.h + 'px', color: item.color }"
+                    >
+                    </div>
+                  </template>
+                  {{ item.label }}
+                </n-tooltip>
                 <!-- <Icon
                   v-else
                   :class="[item.class]"
@@ -46,23 +51,33 @@
                 /> -->
               </li>
             </ul>
-            <span
-              class="size-28px mr-2 color-#8ac5ff cursor-pointer i-ph:magnifying-glass-plus"
-              @click="magnifying(!magnifyingFlag)"
-            ></span>
+            <n-tooltip trigger="hover" placement="bottom">
+              <template #trigger>
+                <span
+                  class="size-28px mr-2 color-#8ac5ff cursor-pointer i-ph:magnifying-glass-plus"
+                  @click="magnifying(!magnifyingFlag)"
+                ></span>
+              </template>
+              放大镜
+            </n-tooltip>
             <!-- 全屏按钮 -->
-            <div v-auth="['mrfullScreenBtn']">
-              <span
-                v-if="isFullscreen"
-                class="block size-28px color-#8ac5ff cursor-pointer i-icon-park-outline:off-screen"
-                @click="toggle"
-              ></span>
-              <span
-                v-else
-                class="block size-28px color-#8ac5ff cursor-pointer i-iconamoon:screen-full"
-                @click="toggle"
-              ></span>
-            </div>
+            <n-tooltip trigger="hover" placement="bottom">
+              <template #trigger>
+                <div v-auth="['mrfullScreenBtn']">
+                  <span
+                    v-if="isFullscreen"
+                    class="block size-28px color-#8ac5ff cursor-pointer i-icon-park-outline:off-screen"
+                    @click="toggle"
+                  ></span>
+                  <span
+                    v-else
+                    class="block size-28px color-#8ac5ff cursor-pointer i-iconamoon:screen-full"
+                    @click="toggle"
+                  ></span>
+                </div>
+              </template>
+              全屏按钮
+            </n-tooltip>
           </div>
           <div
             v-if="loading"
@@ -86,11 +101,12 @@
 </template>
 
 <script setup>
-  import { useModal, NSpin } from 'naive-ui'
+  import { useModal, NSpin, NTooltip } from 'naive-ui'
   // import useWorker from '@/assets/js/video-lib/omnimatrix-video-player'
   // import { useWindowSize, watchDebounced } from '@vueuse/core'
   import { omatVideoPlayer } from '@/assets/js/video-lib/flv/omatVideoPlayer'
   import { useOutsideSystemStore } from '@/stores/modules/system.js'
+  import { useOutsideHomeStore } from '@/stores/modules/home'
   import { useOutsideTagStore } from '@/stores/modules/tag.js'
   import TagBox from './components/tagBox.vue'
   import RemotePlayback from './RemotePlayback/RemotePlayback.vue'
@@ -108,6 +124,7 @@
   })
   const playbackModal = useModal()
   const useSystem = useOutsideSystemStore()
+  const useHomeStore = useOutsideHomeStore()
   const useTag = useOutsideTagStore()
   const loading = ref(true)
   let playerObj = null
@@ -119,7 +136,7 @@
 
   const init = async () => {
     loading.value = true
-    const { url } = await useSystem.getStream({ device_id: props.RtspMain })
+    const { url } = await useSystem.getStream({ device_id: props.RtspMain || useHomeStore.vId })
     useSystem.videoUrl = url
     console.log('url', url)
     try {

+ 11 - 6
src/views/home/home.vue

@@ -47,11 +47,12 @@
   import MiddleTopBox from './middleTopBox/index.vue'
   import VideoBox from '../VideoBox/index.vue'
   // import Map from '@/layout/Map.vue'
-  // import HomeMap from '@/layout/HomeMap/HomeMap.vue'
+  import HomeMap from '@/layout/HomeMap/HomeMap.vue'
   import Three3D from '@/layout/Three3D/index.vue'
   import { $mitt } from '@/utils'
   import { useOutsideHomeStore } from '@/stores/modules/home'
   import { useOutsideSystemStore } from '@/stores/modules/system.js'
+  import { hasPermission } from '@/utils'
 
   const useHomeStore = useOutsideHomeStore()
   const useSystem = useOutsideSystemStore()
@@ -66,13 +67,17 @@
   $mitt.on('onThemeIndex', (i) => {
     if (flag === i) return
     if (i === 1) {
-      leftBoxCom.value = defineAsyncComponent(() => import('./leftboxtwo/index.vue'))
-      rightBoxCom.value = defineAsyncComponent(() => import('./rightboxtwo/index.vue'))
+      if (hasPermission(['mrdashboardShow'])) {
+        leftBoxCom.value = defineAsyncComponent(() => import('./leftboxtwo/index.vue'))
+        rightBoxCom.value = defineAsyncComponent(() => import('./rightboxtwo/index.vue'))
+      }
       mapBoxCom.value = defineAsyncComponent(() => import('@/layout/Map.vue'))
     } else if (i === 2) {
-      leftBoxCom.value = defineAsyncComponent(() => import('./leftboxthree/index.vue'))
-      rightBoxCom.value = defineAsyncComponent(() => import('./rightboxthree/index.vue'))
-      mapBoxCom.value = defineAsyncComponent(() => import('@/layout/HomeMap/HomeMap.vue'))
+      if (hasPermission(['mrdashboardShow'])) {
+        leftBoxCom.value = defineAsyncComponent(() => import('./leftboxthree/index.vue'))
+        rightBoxCom.value = defineAsyncComponent(() => import('./rightboxthree/index.vue'))
+      }
+      mapBoxCom.value = HomeMap
     } else {
       leftBoxCom.value = LeftBox
       rightBoxCom.value = RightBox

+ 27 - 26
src/views/home/leftbox/historyAlarm.vue

@@ -1,32 +1,32 @@
 <template>
   <div class="absolute top-10px right-15px">
     <n-button class="bg-#389bff94" size="small" @click="historyAlarm">历史告警</n-button>
+    <n-modal
+      v-model:show="showModal"
+      class="custom-card"
+      preset="card"
+      title="告警记录"
+      size="huge"
+      :style="{
+        marginTop: '10%',
+        width: '50%'
+      }"
+      :bordered="false"
+    >
+      <n-data-table :columns="columns" :data="data" max-height="500px" bordered />
+      <div class="flex justify-end mt-2">
+        <n-pagination
+          v-model:page="pagination.page"
+          :page-sizes="pagination.pageSizes"
+          :page-count="pagination.pageSize"
+          :on-update:page="pagination.onChange"
+          :on-update:page-size="pagination.onUpdatePageSize"
+          show-quick-jumper
+          show-size-picker
+        />
+      </div>
+    </n-modal>
   </div>
-  <n-modal
-    v-model:show="showModal"
-    class="custom-card"
-    preset="card"
-    title="告警记录"
-    size="huge"
-    :style="{
-      marginTop: '10%',
-      width: '50%'
-    }"
-    :bordered="false"
-  >
-    <n-data-table :columns="columns" :data="data" max-height="500px" bordered />
-    <div class="flex justify-end mt-2">
-      <n-pagination
-        v-model:page="pagination.page"
-        :page-sizes="pagination.pageSizes"
-        :page-count="pagination.pageSize"
-        :on-update:page="pagination.onChange"
-        :on-update:page-size="pagination.onUpdatePageSize"
-        show-quick-jumper
-        show-size-picker
-      />
-    </div>
-  </n-modal>
 </template>
 
 <script setup>
@@ -34,6 +34,7 @@
   import { Events } from '@/utils/enum'
   import dayjs from 'dayjs'
   import { h } from 'vue'
+  import { hasPermission } from '@/utils'
 
   const modal = useModal()
   const { API_ALARM_HISTORY_GET } = useRequest()
@@ -143,6 +144,6 @@
     console.log(res)
   }
   onMounted(() => {
-    init()
+    hasPermission(['mrhistoricalAlarmBtn']) && init()
   })
 </script>

+ 1 - 2
src/views/home/middleBottomBox/components/partBox.vue

@@ -121,9 +121,8 @@
                 py < 132 ? 0 : py > height ? height : py - 132
               ]
               videoDom.style.transform = `translate(${-x}px, ${-y}px)`
-
               loading.value = false
-            }, 200)
+            }, 150)
           },
           {
             enableZoom: false, // 启用缩放

+ 55 - 27
src/views/home/middleBottomBox/components/tour.vue

@@ -15,12 +15,15 @@
             <div
               v-for="item in fullRoutes"
               v-show="item.show"
+              :a="item.show"
               :key="item.key"
               class="absolute top-0 left-0 w-full h-full"
             >
               <pub-video-new :newClass="item.className" />
+              {{ item.show }}
             </div>
           </template>
+
           <!-- 工具面板 -->
           <!-- <operation-panel
           :index="currentActive"
@@ -78,7 +81,7 @@
 <script setup>
   import { NSpin } from 'naive-ui'
   import { useOutsideSystemStore } from '@/stores/modules/system.js'
-  import { uuid } from '@/utils'
+  import { uuid, getAutofitScale, msg } from '@/utils'
   import { omatVideoPlayer } from '@/assets/js/video-lib/flv/omatVideoPlayer'
   import { UseFullscreen } from '@vueuse/components'
 
@@ -121,6 +124,7 @@
   ])
   let tempArrI = 0
   let tempArrT = null
+  let ratio = 0
   const loading = ref(true)
   const videoClick = async (item, index) => {
     if (playerObj) return
@@ -150,19 +154,41 @@
           '.t-video' + props.currentActive,
           videoUrl.value,
           () => {
-            const videoDom = document.querySelector('.t-video' + props.currentActive)
-            console.log(videoDom.style.transform)
-
-            videoDom.style.transform = `scale(3) translate(${-tempArr.value[tempArrI].x}px, ${-tempArr.value[tempArrI].y}px)`
             loading.value = false
-            tempArrT = setInterval(() => {
-              if (tempArrI < tempArr.value.length - 1) {
-                ++tempArrI
-              } else {
-                tempArrI = 0
+            setTimeout(() => {
+              const videoDom = document.querySelector('.t-video' + props.currentActive)
+              console.dir(videoDom)
+              ratio = videoDom.offsetWidth / (videoDom.videoWidth / getAutofitScale())
+              let w = videoDom.videoWidth
+              let h = videoDom.videoHeight
+              videoDom.style.width = w + 'px'
+              videoDom.style.height = h + 'px'
+              // const { width, height } = videoDom.getBoundingClientRect()
+              const { width, height } = document.querySelector('.tour-box').getBoundingClientRect()
+              console.log(width, height)
+
+              const fn = () => {
+                let x = tempArr.value[tempArrI].x / getAutofitScale() - width / 2
+                let y = tempArr.value[tempArrI].y / getAutofitScale() - height / 2
+
+                if (x > w - width / getAutofitScale()) {
+                  x = w - width / getAutofitScale()
+                }
+                if (y > h - height / getAutofitScale()) {
+                  y = h - height / getAutofitScale()
+                }
+                videoDom.style.transform = `translate(${-x}px, ${-y}px)`
               }
-              videoDom.style.transform = `scale(3) translate(${-tempArr.value[tempArrI].x}px, ${-tempArr.value[tempArrI].y}px)`
-            }, tempArr.value[tempArrI].duration * 1000)
+              fn()
+              tempArrT = setInterval(() => {
+                if (tempArrI < tempArr.value.length - 1) {
+                  ++tempArrI
+                } else {
+                  tempArrI = 0
+                }
+                fn()
+              }, tempArr.value[tempArrI].duration * 1000)
+            }, 150)
           },
           {
             enableZoom: false,
@@ -231,6 +257,8 @@
         if (playerObj[tempArrI - 1]) {
           fullRoutes.value[tempArrI].show = true
           fullRoutes.value[tempArrI - 1].show = false
+          console.log(fullRoutes.value)
+
           closeWorker(tempArrI - 1)
           playerObj[tempArrI - 1] = null
           if (tempArrI === fullRoutes.value.length - 1) {
@@ -257,24 +285,27 @@
   }
 
   const closeVideo = () => {
-    if (noFull.value) {
-      closeWorker()
-    } else {
-      for (let key in playerObj) {
-        closeWorker(key)
-      }
-      playerObj = null
-    }
+    closeWorker()
+    playerObj = null
+    clearCom()
   }
   // 关闭线程
   const closeWorker = (index) => {
     if (!playerObj) return
-    if (noFull.value) {
-      playerObj?.destroyed()
-    } else {
+    if (typeof index === 'number') {
       playerObj[index].destroyed()
       playerObj[index].key = uuid()
+      return
+    }
+    for (let index in playerObj) {
+      if (noFull.value) {
+        playerObj?.destroyed()
+      } else {
+        playerObj[index].destroyed()
+        playerObj[index].key = uuid()
+      }
     }
+    clearInterval(tempArrT)
   }
 
   onMounted(() => {
@@ -287,10 +318,7 @@
   })
 
   onBeforeUnmount(() => {
-    for (let key in playerObj) {
-      closeWorker(key)
-    }
-    clearInterval(tempArrT)
+    closeWorker()
   })
 </script>
 <style scoped lang="scss"></style>

+ 28 - 19
src/views/home/middleTopBox/components/tool.vue

@@ -1,13 +1,18 @@
 <template>
   <div class="flex flex-col">
-    <div
-      class="relative mb-2 w-52px h-52px flex justify-center items-center cursor-pointer"
-      v-for="(tool, i) in tools"
-      :key="i"
-      :style="{ background: `url(${tool.selected ? tool.yes : tool.no}) no-repeat center` }"
-      @click="toolClick(tool)"
-    >
-      <img :src="tool.icon" />
+    <div v-for="(tool, i) in tools" :key="i" class="w-52px h-52px mb-2">
+      <n-tooltip trigger="hover" placement="right">
+        <template #trigger>
+          <div
+            class="relative w-52px h-52px flex justify-center items-center cursor-pointer"
+            :style="{ background: `url(${tool.selected ? tool.yes : tool.no}) no-repeat center` }"
+            @click="toolClick(tool)"
+          >
+            <img :src="tool.icon" />
+          </div>
+        </template>
+        {{ tool.label }}
+      </n-tooltip>
     </div>
     <div
       :class="[
@@ -30,7 +35,7 @@
     </div>
     <div
       :class="[
-        tools[3].selected ? 'transform-scale-100' : 'transform-scale-0',
+        tools[2].selected ? 'transform-scale-100' : 'transform-scale-0',
         'overflow-hidden origin-top-right transition duration-300 z-999 w-180px bg-[rgba(11,21,44,1)] absolute right-62px top-180px'
       ]"
     >
@@ -52,6 +57,7 @@
 
 <script setup>
   import { getStaticResource, $mitt } from '@/utils'
+  import { NTooltip } from 'naive-ui'
 
   const tools = ref([
     {
@@ -59,27 +65,30 @@
       yes: getStaticResource('assets/images/gn-yes-bg.png'),
       no: getStaticResource('assets/images/gn-no-bg.png'),
       selected: true,
-      type: 'mr'
+      type: 'mr',
+      label: '类型展示'
     },
     {
       icon: getStaticResource('assets/images/gn2-icon.png'),
       yes: getStaticResource('assets/images/gn-yes-bg.png'),
       no: getStaticResource('assets/images/gn-no-bg.png'),
       selected: false,
-      type: 'rlt'
+      type: 'rlt',
+      label: '热力图'
     },
+    // {
+    //   icon: getStaticResource('assets/images/gn3-icon.png'),
+    //   yes: getStaticResource('assets/images/gn-yes-bg.png'),
+    //   no: getStaticResource('assets/images/gn-no-bg.png'),
+    //   selected: true,
+    //   type: 'xs'
+    // },
     {
       icon: getStaticResource('assets/images/gn3-icon.png'),
       yes: getStaticResource('assets/images/gn-yes-bg.png'),
       no: getStaticResource('assets/images/gn-no-bg.png'),
-      selected: true,
-      type: 'xs'
-    },
-    {
-      icon: getStaticResource('assets/images/gn3-icon.png'),
-      yes: getStaticResource('assets/images/gn-yes-bg.png'),
-      no: getStaticResource('assets/images/gn-no-bg.png'),
-      selected: false
+      selected: false,
+      label: '指标切换'
     }
   ])