index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. <template>
  2. <div id="globalMap">
  3. <div class="global-map">
  4. <Map
  5. v-if="mapStore.isAMap"
  6. ref="mapRef"
  7. @handle-show-video="handleShowVideo"
  8. @handle-show-warehouse="handleShowWarehouse"
  9. @handle-show-people="handleShowPeople"
  10. @handle-show-track="handleShowTrack"
  11. />
  12. <YztMap
  13. v-else
  14. ref="map2Ref"
  15. @handle-show-video="handleShowVideo"
  16. @handle-show-warehouse="handleShowWarehouse"
  17. @handle-show-people="handleShowPeople"
  18. @handle-show-track="handleShowTrack"
  19. />
  20. <!--左侧菜单-->
  21. <LeftMenu
  22. ref="leftMenuRef"
  23. style="position: absolute; top: 20px; left: 20px"
  24. @click-menu="clickMenu"
  25. @select-search-marker="selectSearchMarker"
  26. />
  27. <!--右侧功能模块-->
  28. <RightMenu ref="rightMenuRef" />
  29. <!--切换地图图层-->
  30. <SwitchMapTool class="tool-box" />
  31. <!--右下角地图工具 -->
  32. <RightTool />
  33. <!--时间轴-->
  34. <TimeAxis ref="timeAxisRef" />
  35. <!--绘制工具-->
  36. <DrawTools v-if="showDrawTools" @handle-analysis-data="handleAnalysisData" />
  37. <!--附近视频-->
  38. <NearbyVideos v-if="showNearbyVideos" v-model="showNearbyVideos" :location="location" />
  39. <!--格点雨量-->
  40. <GridPointRainfall v-if="showRainfall" v-model="showRainfall" :location="location" />
  41. <!--物资详情-->
  42. <MaterialDetail v-if="showWarehouse" v-model="showWarehouse" :warehouse-data="warehouseData" />
  43. <!--通讯保障-->
  44. <CommunicationSupport v-if="communicationSupport.show" @close="handleHideCommunicationSupport" />
  45. <!--应急人员详情-->
  46. <EmergencyCrew v-if="showPeople" v-model="showPeople" :id="teamId" />
  47. </div>
  48. </div>
  49. </template>
  50. <script lang="ts" setup name="globalMap">
  51. import Map from '@/components/Map/index.vue';
  52. import YztMap from '@/components/Map/YztMap/index.vue';
  53. import SwitchMapTool from './SwitchMapTool.vue';
  54. import LeftMenu from './LeftMenu.vue';
  55. import RightMenu from './RightMenu/index.vue';
  56. import MaterialDetail from './MaterialDetail.vue';
  57. import TimeAxis from '@/components/TimeAxis/index.vue';
  58. import DrawTools from './RightMenu/DrawTools.vue';
  59. import CommunicationSupport from './RightMenu/CommunicationSupport.vue';
  60. import GridPointRainfall from './RightMenu/GridPointRainfall.vue';
  61. import EmergencyCrew from './RightMenu/EmergencyCrew.vue';
  62. import useMapStore from '@/store/modules/map';
  63. import { getPointInfo } from '@/api/globalMap';
  64. import { getVehicleTrajectory } from '@/api/globalMap/KeyVehicles';
  65. import { iconList } from './data/mapData';
  66. import { deepClone } from '@/utils';
  67. import { parseTime } from '@/utils/ruoyi';
  68. import { getLocationVideos } from '@/api/videoMonitor';
  69. //dom元素
  70. const rightMenuRef = ref(null);
  71. const mapRef = ref(null);
  72. const map2Ref = ref(null);
  73. const leftMenuRef = ref(null);
  74. const timeAxisRef = ref(null);
  75. // 地图
  76. let map: any = {};
  77. // 地图类
  78. let mapUtils: any = {};
  79. const mapStore = useMapStore();
  80. // 附近视频菜单数据
  81. let tempMenu = ref({
  82. name: '',
  83. checked: false
  84. });
  85. const communicationSupport = reactive({
  86. show: false,
  87. data: {}
  88. });
  89. let markerList = ref([]);
  90. let addMarkersTimer;
  91. // 添加打点
  92. const addMarkers = (item) => {
  93. const dom = mapStore.isAMap ? mapRef.value : map2Ref.value;
  94. if (dom) {
  95. if (!item.checked) {
  96. let index = mapStore.pointType.findIndex((item2) => item.component === item2.component);
  97. if (index > -1) {
  98. mapStore.pointType.splice(index, 1);
  99. }
  100. if (mapStore.pointType && mapStore.pointType.length === 0) {
  101. dom.clearMarker('point');
  102. return;
  103. }
  104. } else {
  105. // 右侧图层分析状态
  106. item.checked2 = true;
  107. mapStore.pointType.push(item);
  108. }
  109. addMarkersMethod();
  110. }
  111. };
  112. const adjustPoint = (data) => {
  113. data.forEach((item2) => {
  114. // 获取图标
  115. if (iconList[item2.dataType]) {
  116. item2.icon = iconList[item2.dataType].image;
  117. item2.image = iconList[item2.dataType].image;
  118. item2.imageHover = iconList[item2.dataType].imageHover;
  119. item2.size = iconList[item2.dataType].size;
  120. } else {
  121. item2.icon = iconList['common'].image;
  122. item2.image = iconList['common'].image;
  123. item2.imageHover = iconList['common'].imageHover;
  124. item2.size = iconList['common'].size;
  125. }
  126. if (item2.materia_name) {
  127. item2.name = item2.materia_name;
  128. }
  129. if (item2.dataType === 43) {
  130. item2.showName = true;
  131. }
  132. item2.lnglat = [item2.longitude, item2.latitude];
  133. });
  134. return data;
  135. };
  136. const addMarkersMethod = () => {
  137. const dom = mapStore.isAMap ? mapRef.value : map2Ref.value;
  138. const path = [];
  139. mapStore.pointType.forEach((item) => {
  140. path.push(item.component);
  141. });
  142. getPointInfo(path.toString()).then((res) => {
  143. const data = res.data && res.data.list ? res.data?.list : [];
  144. markerList.value = adjustPoint(data);
  145. dom?.addMarker(data);
  146. });
  147. if (!path.includes('43') && addMarkersTimer) {
  148. clearInterval(addMarkersTimer);
  149. addMarkersTimer = null;
  150. }
  151. if (path.includes('43')) {
  152. addMarkersTimer = setInterval(addMarkersMethod, 60 * 1000);
  153. }
  154. };
  155. // 跳转指定地点
  156. const toAddress = (item) => {
  157. const dom = mapStore.activeMap === 'imageMap' ? map2Ref.value : mapRef.value;
  158. dom.setCenter(item);
  159. };
  160. let showDrawTools = ref(false);
  161. // 点击菜单
  162. const clickMenu = (item, dataList) => {
  163. if (item.path === '1') {
  164. // 空间分析
  165. if (item.component === 'spatial') {
  166. showDrawTools.value = !showDrawTools.value;
  167. }
  168. rightMenuRef.value.updateMenu(item.checked ? '1' : '2', item);
  169. } else if (item.path === '2') {
  170. let checked = item.checked ? '1' : '2';
  171. // 打点信息
  172. addMarkers(item);
  173. if (
  174. [
  175. '易涝隐患点',
  176. '省政务无人机',
  177. '铁塔运行监测',
  178. // '物资与装备',
  179. '通讯保障',
  180. '路网视频',
  181. '江湖河库',
  182. '防溺水',
  183. // '森林防火',
  184. '森防视频',
  185. '防灾救援',
  186. '救援队伍',
  187. '交通局视频',
  188. '重点车辆'
  189. ].includes(item.name)
  190. ) {
  191. rightMenuRef.value.updateMenu(checked, item);
  192. } else {
  193. let index = findChecked(dataList, item.name);
  194. if (item.checked && index > 0) {
  195. // 已有图层分析直接切换到该index
  196. rightMenuRef.value.showIndexMenu('图层分析');
  197. } else if (item.checked || (!item.checked && index === 0)) {
  198. rightMenuRef.value.updateMenu(checked, { name: '图层分析', meta: { icon: 'icon1' } });
  199. }
  200. }
  201. } else if (item.path === '3') {
  202. // 通讯保障
  203. communicationSupport.show = !communicationSupport.show;
  204. communicationSupport.data = item;
  205. } else if (item.path === '4') {
  206. // 附近视频
  207. tempMenu.value = item;
  208. //为地图注册click事件获取鼠标点击出的经纬度坐标
  209. map.on('click', handleClickMap);
  210. mapStore.setIsMapSelect(true);
  211. }
  212. };
  213. const handleHideCommunicationSupport = () => {
  214. communicationSupport.show = false;
  215. leftMenuRef.value.setMenuChange(communicationSupport.data, false);
  216. };
  217. const findChecked = (dataList, name) => {
  218. let index = 0;
  219. dataList.forEach((item) => {
  220. if (item.name !== name && item.path === '2' && !!item.checked) {
  221. index++;
  222. }
  223. if (item.children && item.children.length > 0) {
  224. let res = findChecked(item.children, name);
  225. index += res;
  226. }
  227. });
  228. return index;
  229. };
  230. // 点击搜索结果,添加标注
  231. const selectSearchMarker = (item) => {
  232. const dom = mapStore.isAMap ? mapRef.value : map2Ref.value;
  233. let item2 = deepClone(item);
  234. // 获取图标
  235. if (iconList[item2.dataType]) {
  236. item2.icon = iconList[item2.dataType].imageHover;
  237. item2.image = iconList[item2.dataType].image;
  238. item2.imageHover = iconList[item2.dataType].imageHover;
  239. item2.size = iconList[item2.dataType].size;
  240. } else {
  241. item2.icon = iconList['common'].imageHover;
  242. item2.image = iconList['common'].image;
  243. item2.imageHover = iconList['common'].imageHover;
  244. item2.size = iconList['common'].size;
  245. }
  246. item2.lnglat = [item2.longitude, item2.latitude];
  247. dom.addSearchMarker(item2);
  248. };
  249. const handleAnalysisData = (data) => {
  250. rightMenuRef.value.handleMenu('空间分析', data);
  251. };
  252. const getMapUtils = () => {
  253. const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
  254. return !!domRef ? domRef.getMapUtils() : {};
  255. };
  256. // 获取地图元素操作
  257. const getMap = () => {
  258. const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
  259. return !!domRef ? domRef.getMap() : {};
  260. };
  261. const showDetail = (data, dataType) => {
  262. const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
  263. if (!!domRef) {
  264. domRef.handleHover(data, dataType);
  265. }
  266. };
  267. const getDrawTool = () => {
  268. return mapStore.isAMap ? mapRef.value.drawTool : mapUtils;
  269. };
  270. const getPlaceSearch = () => {
  271. const domRef = mapStore.isAMap ? mapRef.value : map2Ref.value;
  272. if (!!domRef) {
  273. domRef.getPlaceSearch();
  274. }
  275. };
  276. const trackPlayback = (data) => {
  277. const domRef = mapStore.isAMap ? mapRef.value : mapUtils;
  278. if (!!domRef) {
  279. domRef.trackPlayback(data);
  280. }
  281. };
  282. let showNearbyVideos = ref(false);
  283. let showRainfall = ref(false);
  284. let location = ref([]);
  285. watch(showNearbyVideos, () => {
  286. if (!!showNearbyVideos.value) {
  287. closeClickMap();
  288. } else {
  289. clearData();
  290. }
  291. });
  292. watch(showRainfall, () => {
  293. if (!!showRainfall.value) {
  294. closeClickMap();
  295. } else {
  296. clearData();
  297. }
  298. });
  299. const clearData = () => {
  300. location.value = [];
  301. if (!!tempMenu.value && !!tempMenu.value.name) {
  302. leftMenuRef.value.setMenuChange(tempMenu.value, false);
  303. tempMenu.value = {
  304. name: '',
  305. checked: false
  306. };
  307. }
  308. };
  309. const closeClickMap = () => {
  310. if (mapStore.isAMap) {
  311. map.off('click', handleClickMap);
  312. } else {
  313. map.un('click', handleClickMap);
  314. }
  315. mapStore.setIsMapSelect(false);
  316. };
  317. // 显示附近视频
  318. const handleShowVideo = (data) => {
  319. location.value = [data.longitude, data.latitude];
  320. showNearbyVideos.value = true;
  321. };
  322. const handleClickMap = (e) => {
  323. if (mapStore.isAMap) {
  324. location.value = [e.lnglat.lng, e.lnglat.lat];
  325. } else {
  326. location.value = e.coordinate;
  327. }
  328. if (!!tempMenu.value && tempMenu.value.name === '附近视频') {
  329. showNearbyVideos.value = true;
  330. } else if (!!tempMenu.value && tempMenu.value.name === '格点雨量') {
  331. showRainfall.value = true;
  332. } else if (!!tempMenu.value && tempMenu.value.name === '定点分析') {
  333. const item = deepClone(tempMenu.value);
  334. const nowLocation = deepClone(location.value);
  335. tempMenu.value = {
  336. name: '',
  337. checked: false
  338. };
  339. if (mapStore.isAMap) {
  340. map.off('click', handleClickMap);
  341. } else {
  342. map.un('click', handleClickMap);
  343. }
  344. mapStore.setIsMapSelect(false);
  345. rightMenuRef.value.updateMenu(item.checked ? '1' : '2', item, nowLocation);
  346. }
  347. };
  348. let showWarehouse = ref(false);
  349. let warehouseData = ref('');
  350. const handleShowWarehouse = (data) => {
  351. warehouseData.value = data;
  352. showWarehouse.value = true;
  353. };
  354. // 救援队伍人员列表
  355. let showPeople = ref(false);
  356. let teamId = ref('');
  357. const handleShowPeople = (data) => {
  358. teamId.value = data.id;
  359. showPeople.value = true;
  360. };
  361. watch(
  362. () => mapStore.trackState.show,
  363. (show) => {
  364. if (!!show) {
  365. const dom = mapStore.isAMap ? mapRef.value : map2Ref.value;
  366. dom?.addMarker(adjustPoint(mapStore.trackState.data));
  367. mapStore.trackState.data = [];
  368. mapStore.trackState.show = false;
  369. }
  370. }
  371. );
  372. const handleShowTrack = (data) => {
  373. getVehicleTrajectory(data.id).then((res) => {
  374. const trajectory = [];
  375. if (res.data && res.data.list) {
  376. res.data.list.forEach((item) => {
  377. trajectory.push({
  378. time: !!item.gpsDate ? parseTime(item.gpsDate, '{h}:{i}') : '',
  379. lnglat: [item.lng, item.lat]
  380. });
  381. });
  382. initDataToPlay({ type: 'track', data: trajectory });
  383. }
  384. });
  385. };
  386. // 传递数据给时间轴
  387. const initDataToPlay = (data) => {
  388. if (!!timeAxisRef.value) {
  389. timeAxisRef.value.initDataToPlay(data);
  390. }
  391. };
  392. watch(
  393. () => mapStore.mapLoaded,
  394. (loaded) => {
  395. if (loaded) {
  396. map = getMap();
  397. mapUtils = getMapUtils();
  398. }
  399. },
  400. {
  401. immediate: true
  402. }
  403. );
  404. // 监听视频打点
  405. watch(
  406. () => mapStore.videoPointParams,
  407. () => {
  408. if (!mapStore.videoPointParams.flag) return;
  409. const queryParams = {
  410. zoom_level: mapStore.mapState.zoom,
  411. // zoom_level: 8,
  412. latitude_min: '',
  413. latitude_max: '',
  414. longitude_min: '',
  415. longitude_max: '',
  416. dict_value: 'slfh'
  417. // dict_value: mapStore.videoPointParams.dict_value
  418. };
  419. if (mapStore.isAMap) {
  420. const AMap = mapUtils.getAMap();
  421. const pixel = new AMap.Pixel(0, 0);
  422. const size = mapRef.value.getMapDomSize();
  423. const pixel2 = new AMap.Pixel(size[0], size[1]);
  424. const lnglat = map.containerToLngLat(pixel);
  425. const lnglat2 = map.containerToLngLat(pixel2);
  426. queryParams.longitude_min = lnglat.lng;
  427. queryParams.latitude_max = lnglat.lat;
  428. queryParams.longitude_max = lnglat2.lng;
  429. queryParams.latitude_min = lnglat2.lat;
  430. }
  431. getLocationVideos(queryParams).then((res) => {
  432. mapUtils.addMarker2(res.data);
  433. });
  434. },
  435. {
  436. deep: true
  437. }
  438. );
  439. onMounted(() => {
  440. mapStore.initData();
  441. });
  442. onBeforeUnmount(() => {
  443. if (!!map) {
  444. if (mapStore.isAMap) {
  445. map.off('click', handleClickMap);
  446. } else {
  447. map.un('click', handleClickMap);
  448. }
  449. mapStore.setIsMapSelect(false);
  450. }
  451. if (!!addMarkersTimer) {
  452. clearInterval(addMarkersTimer);
  453. addMarkersTimer = null;
  454. }
  455. });
  456. provide('getMapUtils', getMapUtils);
  457. provide('getMap', getMap);
  458. provide('trackPlayback', trackPlayback);
  459. provide('showDetail', showDetail);
  460. provide('getDrawTool', getDrawTool);
  461. provide('getPlaceSearch', getPlaceSearch);
  462. provide('initDataToPlay', initDataToPlay);
  463. </script>
  464. <style lang="scss" scoped>
  465. #globalMap {
  466. width: 100%;
  467. height: 100%;
  468. }
  469. .global-map {
  470. width: 100%;
  471. height: 100%;
  472. position: relative;
  473. //overflow: hidden;
  474. .tool-box {
  475. position: absolute;
  476. right: 85px;
  477. bottom: 90px;
  478. z-index: 10;
  479. color: #fff;
  480. }
  481. }
  482. .box {
  483. position: absolute;
  484. top: 20px;
  485. right: 20px;
  486. width: 300px;
  487. background-color: #041d55;
  488. display: flex;
  489. flex-direction: column;
  490. padding: 20px;
  491. color: #fff;
  492. font-size: 16px;
  493. height: 500px;
  494. max-height: 90%;
  495. div {
  496. line-height: 30px;
  497. cursor: pointer;
  498. }
  499. }
  500. .fixed {
  501. position: fixed !important;
  502. }
  503. </style>