Procházet zdrojové kódy

Merge remote-tracking branch 'origin/dev' into dev

zhangyihao před 3 měsíci
rodič
revize
7066862230

+ 8 - 0
src/api/globalMap/mitigation.ts

@@ -7,3 +7,11 @@ export const getRescue = (data) => {
     data: data
   });
 };
+// 交通视频
+export const getTraffic = (data) => {
+  return request({
+    url: '/api/gateway/v2/get_video_transportation_list',
+    method: 'post',
+    data: data
+  });
+};

+ 3 - 1
src/components/Dialog/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div v-if="modelValue || customShow" class="common-dialog" :style="{ width: computedWidth, height: computedHeight, zIndex: zIndex }">
+  <div v-if="modelValue || customShow"  v-drag="{draggable: draggable, scaleX: containerScale().scaleX, scaleY: containerScale().scaleY}" class="common-dialog" :style="{ width: computedWidth, height: computedHeight, zIndex: zIndex }">
     <div :class="type === 'xs' || headerType === 'header2' ? 'dialog-header2' : 'dialog-header'">
       <div v-if="!hideTitle" class="dialog-title">
         {{ title ? title : '弹窗' }}
@@ -52,7 +52,9 @@ interface Props {
   customShow?: boolean;
   headerType?: string;
   getTagId?: string;
+  draggable?: boolean;
 }
+const containerScale = inject('containerScale');
 const props = withDefaults(defineProps<Props>(), {
   modelValue: false,
   type: 'md',

+ 1 - 1
src/components/Map/YztMap/index.vue

@@ -81,7 +81,7 @@ watch(
   }
 );
 const mapList = reactive({
-  satellite2: ['YZT1715739306532', 'YZT1695608158269'],
+  satellite2: ['YZT1712111943104', 'YZT1695608158269'],
   satellite3: ['YZT1708679726700', 'YZT1695608158269'],
   imageMap: ['YZT1640925052482', 'YZT1695608158269'],
   imageMap2: ['YZT1640925052482', 'YZT1695608158269']

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

@@ -1,5 +1,5 @@
 <template>
-  <Dialog custom-show type="lg" title="附近视频" height="720px" hide-footer @close="handleClose">
+  <Dialog custom-show type="lg" title="附近视频" height="720px" draggable hide-footer @close="handleClose">
     <div class="flex-box">
       <div>附近距离</div>
       <el-select v-model="queryParams.radius" class="custom-select" popper-class="custom-popper" :teleported="false" @change="initData">

+ 42 - 0
src/directive/common/drag.ts

@@ -0,0 +1,42 @@
+export default {
+  mounted(el, binding) {
+    const { draggable, scaleX = 1, scaleY = 1 } = binding.value;
+    if (!draggable) {
+      // 如果拖拽功能被禁用,则不执行任何拖拽相关的逻辑
+      return;
+    }
+
+    el.style.position = 'fixed';
+    el.style.cursor = 'move';
+
+    const dragDom = el;
+
+    let disX = 0; // 鼠标按下时,鼠标与拖拽元素的左边距
+    let disY = 0; // 鼠标按下时,鼠标与拖拽元素的上边距
+
+    dragDom.onmousedown = (e) => {
+      // 鼠标按下,计算当前元素距离可视区的距离
+      disX = e.clientX / scaleX - dragDom.offsetLeft;
+      disY = e.clientY / scaleY - dragDom.offsetTop;
+
+      // 鼠标移动事件
+      document.onmousemove = function (e) {
+        // 通过事件委托,计算移动的距离
+        const l = e.clientX / scaleX - disX;
+        const t = e.clientY / scaleY - disY;
+        // 移动当前元素
+        dragDom.style.left = `${l + 'px'}`;
+        dragDom.style.top = `${t + 'px'}`;
+
+        // 将此时的位置传出去
+        // binding.value({x:e.pageX,y:e.pageY})
+      };
+
+      // 鼠标松开事件
+      document.onmouseup = function (e) {
+        document.onmousemove = null;
+        document.onmouseup = null;
+      };
+    };
+  }
+};

+ 2 - 0
src/directive/index.ts

@@ -1,4 +1,5 @@
 import copyText from './common/copyText';
+import drag from './common/drag';
 import { hasPermi, hasRoles } from './permission';
 import { App } from 'vue';
 
@@ -6,4 +7,5 @@ export default (app: App) => {
   app.directive('copyText', copyText);
   app.directive('hasPermi', hasPermi);
   app.directive('hasRoles', hasRoles);
+  app.directive('drag', drag);
 };

+ 2 - 1
src/hooks/AMap/useAMap.ts

@@ -1,6 +1,6 @@
 import AMapLoader from '@amap/amap-jsapi-loader';
 import { nanoid } from 'nanoid';
-import { deepClone } from '@/utils';
+import { deepClone, initDrag } from '@/utils';
 import { mergeGeoJsonPolygons, wgs_gcj_encrypts } from '@/utils/gisUtils';
 
 export function useAMap(options) {
@@ -257,6 +257,7 @@ export function useAMap(options) {
     // 打开InfoWindow,并设置其内容和位置
     infoWindow.setContent(content);
     infoWindow.open(map, lnglat);
+    initDrag(infoWindow.dom);
     // 解决2.0版本无法滚动问题
     infoWindow.on('mouseover', () => map.setStatus({ zoomEnable: false }));
     infoWindow.on('mouseout', () => map.setStatus({ zoomEnable: true }));

+ 33 - 0
src/utils/index.ts

@@ -435,3 +435,36 @@ export const hexToRgba = (hex, alpha) => {
   // 返回 RGBA 字符串
   return `rgba(${r}, ${g}, ${b}, ${alpha || 1})`;
 };
+
+export const initDrag = (el, scaleX = 1, scaleY = 1) => {
+  if (!el) return;
+  el.style.position = 'fixed';
+  el.style.cursor = 'move';
+
+  const dragDom = el;
+
+  let disX = 0; // 鼠标按下时,鼠标与拖拽元素的左边距
+  let disY = 0; // 鼠标按下时,鼠标与拖拽元素的上边距
+
+  dragDom.onmousedown = (e) => {
+    // 鼠标按下,计算当前元素距离可视区的距离
+    disX = e.clientX / scaleX - dragDom.offsetLeft;
+    disY = e.clientY / scaleY - dragDom.offsetTop;
+
+    // 鼠标移动事件
+    document.onmousemove = function (e) {
+      // 通过事件委托,计算移动的距离
+      const l = e.clientX / scaleX - disX;
+      const t = e.clientY / scaleY - disY;
+      // 移动当前元素
+      dragDom.style.left = `${l + 'px'}`;
+      dragDom.style.top = `${t + 'px'}`;
+    };
+
+    // 鼠标松开事件
+    document.onmouseup = function () {
+      document.onmousemove = null;
+      document.onmouseup = null;
+    };
+  };
+};

+ 2 - 1
src/utils/olMap/olMap.ts

@@ -33,7 +33,7 @@ import Overlay from 'ol/Overlay';
 import { Draw, Select } from 'ol/interaction';
 import { click } from 'ol/events/condition';
 import Circle from 'ol/geom/Circle';
-import { deepClone, hexToRgba } from '@/utils';
+import { deepClone, hexToRgba, initDrag } from '@/utils';
 import { createBox } from 'ol/interaction/Draw';
 import * as turf from '@turf/turf';
 import { nanoid } from 'nanoid';
@@ -620,6 +620,7 @@ export class olMap {
       });
     }
     this.infoWindow.setPosition(position);
+    initDrag(this.infoWindow.element);
     this.map.addOverlay(this.infoWindow);
   }
 

+ 0 - 1
src/views/comprehensiveGuarantee/MaterialReserveManagement/InventoryDetails.vue

@@ -147,7 +147,6 @@ watch(
     // 在这里可以执行其他操作,比如更新组件状态
   },
   { immediate: true } // 立即执行一次回调,以同步初始值
-
 );
 
 const handleSelectionChange = (selection) => {

+ 1 - 1
src/views/globalMap/RightMenu/Fireproofing.vue

@@ -38,7 +38,7 @@
           </div>
         </div>
       </div>
-      <Dialog v-if="showDialog" v-model="showDialog" type="md" title="森林防火" height="720px" hide-footer>
+      <Dialog v-if="showDialog" v-model="showDialog" type="md" draggable title="森林防火" height="720px" hide-footer>
         <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
           <HKVideo :dot_data="videoMonitorData" autoplay />
         </div>

+ 1 - 1
src/views/globalMap/RightMenu/GridPointRainfall.vue

@@ -1,5 +1,5 @@
 <template>
-  <Dialog custom-show title="格点雨量" :height="'450px'" hide-footer @close="handleClose">
+  <Dialog custom-show title="格点雨量" :height="'450px'" draggable hide-footer @close="handleClose">
     <div class="gradient-text title">雨量统计:{{ address }}({{ location[0] }}, {{ location[1] }})</div>
     <Chart :option="chartOption" style="height: 360px" />
   </Dialog>

+ 1 - 1
src/views/globalMap/RightMenu/PreventDrowning.vue

@@ -38,7 +38,7 @@
           </div>
         </div>
       </div>
-      <Dialog v-if="showDialog" v-model="showDialog" type="md" height="720px" title="防溺水" hide-footer>
+      <Dialog v-if="showDialog" v-model="showDialog" type="md" height="720px" title="防溺水" draggable hide-footer>
         <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
           <HKVideo :dot_data="videoMonitorData" />
         </div>

+ 1 - 1
src/views/globalMap/RightMenu/Reservoir.vue

@@ -38,7 +38,7 @@
           </div>
         </div>
       </div>
-      <Dialog v-if="showDialog" v-model="showDialog" type="md" height="720px" title="江湖河库" hide-footer>
+      <Dialog v-if="showDialog" v-model="showDialog" type="md" height="720px" title="江湖河库" draggable hide-footer>
         <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
           <HKVideo :dot_data="videoMonitorData" />
         </div>

+ 1 - 1
src/views/globalMap/RightMenu/ReservoirMonitor.vue

@@ -34,7 +34,7 @@
       </div>
     </div>
   </div>
-  <Dialog v-model="showDialog" type="xl" title="水库监测" hide-footer>
+  <Dialog v-model="showDialog" type="xl" title="水库监测" draggable hide-footer>
     <div class="flex">
       <div class="detail-container">
         <div class="flex">

+ 1 - 1
src/views/globalMap/RightMenu/RiverMonitor.vue

@@ -34,7 +34,7 @@
       </div>
     </div>
   </div>
-  <Dialog v-model="showDialog" type="xl" title="河道监测" hide-footer>
+  <Dialog v-model="showDialog" type="xl" title="河道监测" draggable hide-footer>
     <div class="flex">
       <div class="detail-container">
         <div class="flex">

+ 1 - 1
src/views/globalMap/RightMenu/RoadNetworkVideo.vue

@@ -49,7 +49,7 @@
         </div>
       </div>
     </div>
-    <Dialog v-if="showDialog" v-model="showDialog" type="md" title="路网视频" height="720px" hide-footer>
+    <Dialog v-if="showDialog" v-model="showDialog" type="md" title="路网视频" height="720px" draggable hide-footer>
       <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
         <HKVideo :dot_data="videoMonitorData" />
       </div>

+ 175 - 0
src/views/globalMap/RightMenu/TrafficVideo.vue

@@ -0,0 +1,175 @@
+<template>
+  <div class="menu-content">
+    <div class="container">
+      <div class="gradient-text common-dialog-title2">交通视频</div>
+      <div class="box-left">
+        <el-input v-model="queryParams.keyword" class="custom-input" placeholder="搜索" @input="initData">
+          <template #prefix>
+            <el-icon class="el-input__icon"><search /></el-icon>
+          </template>
+        </el-input>
+        <div class="btn" @click="handleCancel">取消</div>
+      </div>
+      <!--      <div class="gradient-text">视频类型</div>-->
+      <div class="custom-table">
+        <div class="th">
+          <div class="td">
+            <div style="width: 240px">
+              <el-select
+                v-model="queryParams.area"
+                placeholder="所有区县"
+                size="large"
+                class="custom-select2"
+                popper-class="custom-select-popper2"
+                :teleported="false"
+                @change="initData"
+              >
+                <el-option label="所有区县" value="" />
+                <el-option v-for="item in district_type" :key="item.value" :label="item.label" :value="item.label" />
+              </el-select>
+            </div>
+          </div>
+          <div class="td">名称</div>
+        </div>
+        <div class="table-content">
+          <div v-for="(item, index) in listData" :key="index" class="tr" @click="handleShowDialog(item)">
+            <div class="td">{{ item.area }}</div>
+            <div class="td">{{ item.name }}</div>
+          </div>
+        </div>
+      </div>
+      <Dialog v-if="showDialog" v-model="showDialog" type="md" draggable title="交通视频" height="720px" hide-footer>
+        <div style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center">
+          <HKVideo :dot_data="videoMonitorData" />
+        </div>
+      </Dialog>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { Search } from '@element-plus/icons-vue';
+import { getTraffic } from '@/api/globalMap/mitigation';
+import { deepClone } from '@/utils';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { district_type } = toRefs<any>(proxy?.useDict('district_type'));
+const queryParams = reactive({
+  current: 1,
+  size: 10,
+  keyword: '',
+  area: ''
+});
+const total = ref(0);
+
+const listData = ref([]);
+
+let showDialog = ref(false);
+let videoMonitorData = ref({});
+const handleShowDialog = (row) => {
+  showDialog.value = false;
+  nextTick(() => {
+    videoMonitorData.value = row;
+    showDialog.value = true;
+  });
+};
+const initData = () => {
+  let newQueryParams = deepClone(queryParams);
+  newQueryParams.query = {
+    area: queryParams.area,
+    keyword: queryParams.keyword
+  };
+  delete newQueryParams.area;
+  delete newQueryParams.keyword;
+  getTraffic(newQueryParams).then((res) => {
+    listData.value = res.rows;
+    total.value = res.total;
+  });
+};
+const handleCancel = () => {
+  queryParams.keyword = '';
+  initData();
+};
+initData();
+</script>
+
+<style lang="scss" scoped>
+.menu-content {
+  width: 574px;
+  height: 581px;
+  background: url('@/assets/images/map/rightMenu/content.png') no-repeat;
+  background-size: 100% 100%;
+  padding: 60px 10px 10px 15px;
+  font-size: 14px;
+  position: relative;
+  color: #ffffff;
+}
+.custom-table {
+  width: 550px;
+  .table-content {
+    height: 385px;
+    overflow-y: auto;
+    overflow-x: hidden;
+  }
+  .th {
+    background: url('@/assets/images/map/rightMenu/th.png') no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    padding: 7px 12px;
+    height: 32px;
+  }
+  .tr {
+    background: url('@/assets/images/map/rightMenu/td.png') no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    padding: 7px 12px;
+    &:hover {
+      background: url('@/assets/images/map/rightMenu/td_checked.png') no-repeat;
+      background-size: 100% 100%;
+    }
+  }
+  .td {
+    flex: 1;
+    color: #edfaff;
+    font-size: 14px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    cursor: pointer;
+  }
+  .td-text {
+    /* 设置字体透明 */
+    color: transparent;
+    /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
+    -webkit-background-clip: text;
+    /* 非Webkit内核浏览器需要使用标准前缀 */
+    background-clip: text;
+    font-family: 'YouSheBiaoTiHei';
+    /* 设置线性渐变,从红色渐变到蓝色 */
+    background-image: linear-gradient(to bottom, #ffffff 50%, #3075d3 100%);
+    font-size: 14px;
+  }
+  .text-green {
+    background-image: linear-gradient(to bottom, #ffffff 50%, #40c75f 100%);
+  }
+  .text-danger {
+    background-image: linear-gradient(to bottom, #ffffff 50%, #ff2f3c 100%);
+  }
+}
+.box-left {
+  display: flex;
+  align-items: center;
+  margin-bottom: 10px;
+  .btn {
+    width: 59px;
+    height: 23px;
+    background: url('@/assets/images/map/rightMenu/potentialFloodHazard/btn.png') no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    cursor: pointer;
+    margin-left: 20px;
+  }
+}
+</style>

+ 3 - 0
src/views/globalMap/RightMenu/index.vue

@@ -64,6 +64,8 @@
         <Fireproofing v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '森林防火'" @handle-menu="handleMenu" />
         <!--防灾救援-->
         <Mitigation v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '防灾救援'" @handle-menu="handleMenu" />
+        <!--交通视频-->
+        <TrafficVideo v-if="menuState.menuData[menuState.activeIndex]?.name === '交通视频'" @handle-menu="handleMenu" />
         <!--易涝隐患点-->
         <PotentialFloodHazard
           v-if="menuState.showMenu && menuState.menuData[menuState.activeIndex]?.name === '易涝隐患点'"
@@ -124,6 +126,7 @@ import MobileCommandVehicle from './MobileCommandVehicle.vue';
 import Helicopter from './Helicopter.vue';
 import TyphoonVideo from './TyphoonVideo.vue';
 import RescueTeam from './RescueTeam.vue';
+import TrafficVideo from './TrafficVideo.vue';
 interface Props {
   pointType: PointType[];
 }

+ 18 - 16
src/views/globalMap/index.vue

@@ -63,6 +63,8 @@ import GridPointRainfall from '@/views/globalMap/RightMenu/GridPointRainfall.vue
 import NearbyVideos from '@/components/NearbyVideos/index.vue';
 import EmergencyCrew from '@/views/globalMap/RightMenu/EmergencyCrew.vue';
 
+const AMapType = ['vectorgraph', 'satellite'];
+const YMapType = ['satellite2', 'satellite3', 'imageMap', 'imageMap2'];
 const rightMenuRef = ref(null);
 const mapData = reactive(logicalData);
 let map;
@@ -88,7 +90,7 @@ const switchMap = (key) => {
 let pointType = ref<PointType[]>([]);
 let markerList = ref([]);
 const addMarkers = (item) => {
-  const dom = activeMap.value === 'imageMap' ? map2Ref.value : mapRef.value;
+  const dom = YMapType.includes(activeMap.value) ? map2Ref.value : mapRef.value;
   if (dom) {
     if (!item.checked) {
       let index = pointType.value.findIndex((item2) => item.component === item2.component);
@@ -215,7 +217,7 @@ const findChecked = (dataList, name) => {
 };
 // 点击搜索结果,添加标注
 const selectSearchMarker = (item) => {
-  const dom = activeMap.value === 'imageMap' ? map2Ref.value : mapRef.value;
+  const dom = YMapType.includes(activeMap.value) ? map2Ref.value : mapRef.value;
   let item2 = deepClone(item);
   // 获取图标
   if (iconList[item2.dataType]) {
@@ -237,57 +239,57 @@ const handleAnalysisData = (data) => {
 };
 // 获取地图元素操作
 const getMap = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMap();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMap();
   }
   return {};
 };
 const getMapUtils = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMapUtils();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMapUtils();
   }
   return {};
 };
 const showDetail = (data, dataType) => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.handleHover(data, dataType);
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.handleHover(data, dataType);
   }
   return {};
 };
 const getDrawTool = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMapUtils();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.drawTool;
   }
   return {};
 };
 const getMarkers = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getMarkers();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getMarkers();
   }
   return {};
 };
 const getPlaceSearch = () => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.getPlaceSearch();
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.getPlaceSearch();
   }
   return {};
 };
 const trackPlayback = (data) => {
-  if (['imageMap', 'satellite2', 'satellite3'].includes(activeMap.value)) {
+  if (YMapType.includes(activeMap.value)) {
     return map2Ref.value.trackPlayback(data);
-  } else if (['vectorgraph', 'satellite'].includes(activeMap.value)) {
+  } else if (AMapType.includes(activeMap.value)) {
     return mapRef.value.trackPlayback(data);
   }
   return {};