index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. <template>
  2. <div ref="containerRef" class="map-container">
  3. <div id="aMap" class="map-container2" :style="{ width: width, height: height }"></div>
  4. <rightTool :mapState="mapState" type="AMap" :showMask="showMask" @changeShowMask="changeShowMask" />
  5. </div>
  6. </template>
  7. <script setup lang="ts" name="Map">
  8. import rightTool from './rightTool.vue';
  9. import ScaleLine from 'ol/control/ScaleLine';
  10. import { useAMap } from '@/hooks/AMap/useAMap';
  11. import { useDrawTool } from '@/hooks/AMap/useDrawTool';
  12. import { getPointInfoList } from '@/api/globalMap';
  13. import { getDictLabel } from '@/utils/dict';
  14. import { PointType } from '@/api/globalMap/type';
  15. import {
  16. getEmergencyExpertDetails,
  17. getEmergencyShelterTypeDetails,
  18. getGasolinestationDetails,
  19. getHospitalDetails,
  20. getMiningcompanyDetails,
  21. getWarehouseDetails,
  22. getChemicalcompanyDetails,
  23. getSchoolDetails,
  24. getWaterloggedRoadsDetails,
  25. getShipRealtilmePositionDetails,
  26. getUAVDetails,
  27. getRainbowDetails,
  28. getTouristAttractionDetails,
  29. getConstructionSitesDetails,
  30. getYardSitesDetails,
  31. getStationInfoDetails,
  32. getMajorHazardSourceDetails,
  33. getBuildingProjectDetails,
  34. getChemicalWarehouseDetails,
  35. getMiningOperationsDetails,
  36. getEmergencyTransportResourcesDetails,
  37. getEmergencyDisasterInfoOfficerDetails,
  38. getMidmapDzzhDetails,
  39. getVehicleDetails,
  40. getVideoDrowning,
  41. getVideoForestFire,
  42. getVideoDisasterPrevention
  43. } from '@/api/globalMap/spatialAnalysis';
  44. import { pointDetailTemplate } from '@/views/globalMap/data/mapData';
  45. import useAppStore from '@/store/modules/app';
  46. import { getWaterList } from '@/api/globalMap/reservoir';
  47. import { getRescueTeamsInfo } from '@/api/globalMap/rescueTeam';
  48. interface Props {
  49. activeMap: string;
  50. pointType: PointType[];
  51. showMask: boolean;
  52. }
  53. const containerScale = inject('containerScale');
  54. const props = withDefaults(defineProps<Props>(), {});
  55. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  56. const { point_type } = toRefs<any>(proxy?.useDict('point_type'));
  57. const appStore = useAppStore();
  58. const emits = defineEmits([
  59. 'update:drawing',
  60. 'update:showMask',
  61. 'selectGraphics',
  62. 'unSelectGraphics',
  63. 'showTextEditBox',
  64. 'onDrawCompleted',
  65. 'handleShowVideo',
  66. 'handleShowWarehouse',
  67. 'handleShowPeople'
  68. ]);
  69. const containerRef = ref();
  70. const width = ref('100%');
  71. const height = ref('100%');
  72. const mapState = reactive<MapState>({
  73. center: [110.925175, 21.678955],
  74. zoom: 8.5,
  75. minZoom: 6,
  76. maxZoom: 20,
  77. isThreeDimensional: false,
  78. // 是否显示比例尺
  79. showScale: true
  80. });
  81. let AMap, map, scale, placeSearch;
  82. // 鼠标绘制工具
  83. const drawTool = useDrawTool();
  84. // 初始化地图
  85. const mapUtils = useAMap({
  86. key: '9c5041381e5e824f9ee324d8f7a40150', // 申请好的Web端开发者Key,首次调用 load 时必填
  87. securityJsCode: '4868bc1b8fac7d9e54e7279ed556879a', // 安全密钥
  88. version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  89. pitch: mapState.isThreeDimensional ? 45 : 0,
  90. zoom: mapState.zoom,
  91. center: [mapState.center[0], mapState.center[1]],
  92. dragEnable: true,
  93. scrollWheel: true,
  94. showScale: true,
  95. enableMouseTool: true,
  96. // 加载完成事件
  97. onLoadCompleted: () => {
  98. AMap = getAMap();
  99. map = getMap();
  100. scale = getScale();
  101. if (!['logical', 'vectorgraph'].includes(props.activeMap)) {
  102. switchMap(props.activeMap);
  103. } else {
  104. map.removeLayer();
  105. }
  106. map.on('zoomchange', zoomChangeHandler);
  107. // 添加遮罩
  108. if (props.showMask) {
  109. creatMask([{ strokeWeight: 2 }]);
  110. }
  111. drawTool.initMouseTool({ container: 'aMap', map, AMap });
  112. placeSearch = new AMap.PlaceSearch({
  113. pageSize: 30,
  114. pageIndex: 1,
  115. city: '0668',
  116. extensions: 'base'
  117. });
  118. handleResize();
  119. },
  120. onMarkerClick: (data) => {
  121. // 多点位
  122. if (data.type === '1') {
  123. let path = [];
  124. props.pointType.forEach((item) => {
  125. path.push(item.component);
  126. });
  127. getPointInfoList({
  128. option: path.toString(),
  129. longitude: data.longitude.toString(),
  130. latitude: data.latitude.toString()
  131. }).then((res) => {
  132. const data2 = res.data.list;
  133. let content = document.createElement('div');
  134. // content.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  135. content.className = 'point-info';
  136. let content2 = '';
  137. content2 += '<div class="title-box"><div class="gradient-text">多点位信息</div></div>';
  138. content2 += '<div class="icon1"></div>';
  139. content2 += '<div class="icon2"></div>';
  140. content2 += '<div class="icon3"></div>';
  141. content2 += '<div class="icon4"></div>';
  142. content.innerHTML = content2;
  143. let tableBox = document.createElement('div');
  144. tableBox.className = 'table-box';
  145. let table = document.createElement('div');
  146. table.className = 'table';
  147. table.innerHTML = '<div class="point-item"><div class="td3">主题</div><div class="td3">名称</div></div>';
  148. data2.forEach((item) => {
  149. item.longitude = data.longitude;
  150. item.latitude = data.latitude;
  151. const div = document.createElement('div');
  152. div.className = 'point-item point-item-hover';
  153. div.innerHTML =
  154. '<div class="td4">' + getDictLabel(point_type.value, item.dataType.toString()) + '</div><div class="td4">' + item.name + '</div>';
  155. div.addEventListener('click', () => {
  156. handlePointDetails(item);
  157. });
  158. table.appendChild(div);
  159. });
  160. tableBox.appendChild(table);
  161. content.appendChild(tableBox);
  162. let closeBtn = document.createElement('div');
  163. closeBtn.className = 'close';
  164. closeBtn.onclick = hideInfo;
  165. content.appendChild(closeBtn);
  166. showInfo(content, [data.longitude, data.latitude], true);
  167. });
  168. } else {
  169. handlePointDetails(data);
  170. }
  171. }
  172. });
  173. const {
  174. getAMap,
  175. getMap,
  176. switchMap,
  177. addMarker,
  178. addSearchMarker,
  179. clearMarker,
  180. getMarkers,
  181. getScale,
  182. showInfo,
  183. hideInfo,
  184. handleHover,
  185. creatMask,
  186. removeMask,
  187. trackPlayback
  188. } = { ...mapUtils };
  189. const handlePointDetails = (data) => {
  190. let methodList = {
  191. '1': getEmergencyExpertDetails,
  192. '2': getWarehouseDetails,
  193. '3': getEmergencyShelterTypeDetails,
  194. '4': getWaterloggedRoadsDetails,
  195. '5': getSchoolDetails,
  196. '6': getHospitalDetails,
  197. '7': getGasolinestationDetails,
  198. '8': getMiningcompanyDetails,
  199. // '9': getChemicalcompanyDetails,
  200. '10': getShipRealtilmePositionDetails,
  201. '11': getChemicalcompanyDetails,
  202. '12': getChemicalcompanyDetails,
  203. '13': getChemicalcompanyDetails,
  204. '14': getChemicalcompanyDetails,
  205. '15': getUAVDetails,
  206. '16': getRainbowDetails,
  207. '17': getMidmapDzzhDetails,
  208. '18': getMiningOperationsDetails,
  209. '21': getBuildingProjectDetails,
  210. '22': getChemicalWarehouseDetails,
  211. '23': getMajorHazardSourceDetails,
  212. '24': getStationInfoDetails,
  213. '25': getYardSitesDetails,
  214. '26': getTouristAttractionDetails,
  215. '27': getConstructionSitesDetails,
  216. '28': getEmergencyTransportResourcesDetails,
  217. '29': getEmergencyDisasterInfoOfficerDetails,
  218. '30': getVehicleDetails,
  219. '31': getWaterList,
  220. '32': getVideoDrowning,
  221. '33': getVideoForestFire,
  222. '34': getVideoDisasterPrevention,
  223. '41': getRescueTeamsInfo
  224. };
  225. let titleList = {
  226. '1': '专家信息',
  227. '2': '物资与装备仓库信息',
  228. '3': '避难所信息',
  229. '4': '易涝点信息',
  230. '5': '学校信息',
  231. '6': '医院信息',
  232. '7': '加油站信息',
  233. '8': '非煤矿山企业信息',
  234. '9': '危化企业信息',
  235. '10': '船舶动态信息',
  236. '11': '危险化学品经营企业信息',
  237. '12': '危险化学品生产企业信息',
  238. '13': '危险化学品使用企业(使用许可)信息',
  239. '14': '化工企业(不发使用许可)信息',
  240. '15': '无人机信息',
  241. '16': '雨窝点',
  242. '17': '地质灾害隐患点',
  243. '18': '矿山施工',
  244. '19': '工矿商贸',
  245. '20': '气象灾害防御重点单位',
  246. '21': '建筑工程',
  247. '22': '储罐信息',
  248. '23': '重大危险源',
  249. '24': '客运站',
  250. '25': '堆场',
  251. '26': '旅游场所',
  252. '27': '在建工地',
  253. '28': '运输资源',
  254. '29': '灾害信息员',
  255. '30': '重点车辆信息',
  256. '31': '江湖河库',
  257. '32': '防溺水',
  258. '33': '森林防火',
  259. '34': '防灾救援',
  260. '41': '救援队伍'
  261. };
  262. let method = methodList[data.dataType];
  263. let title = !!titleList[data.dataType] ? titleList[data.dataType] : '信息';
  264. if (!method) return;
  265. method(data.id).then((res) => {
  266. if (!!pointDetailTemplate[data.dataType]) {
  267. let div = document.createElement('div');
  268. // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  269. div.className = 'point-info';
  270. let titleDom = document.createElement('div');
  271. titleDom.className = 'title-box';
  272. titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
  273. div.appendChild(titleDom);
  274. if (data.dataType === 2) {
  275. let btnBox = document.createElement('div');
  276. let btn = document.createElement('div');
  277. btnBox.className = 'flex';
  278. btn.className = 'btn';
  279. btn.innerHTML = '<div class="video-icon"></div><div>物资详情</div>';
  280. btn.onclick = () => {
  281. emits('handleShowWarehouse', data);
  282. };
  283. btnBox.appendChild(btn);
  284. div.appendChild(btnBox);
  285. } else if (data.dataType === 4) {
  286. let btnBox = document.createElement('div');
  287. let btn = document.createElement('div');
  288. btnBox.className = 'flex';
  289. btn.className = 'btn';
  290. btn.innerHTML = '<div class="video-icon"></div><div>附近视频</div>';
  291. btn.onclick = () => {
  292. emits('handleShowVideo', data);
  293. };
  294. btnBox.appendChild(btn);
  295. div.appendChild(btnBox);
  296. } else if (data.dataType === 41) {
  297. let btnBox = document.createElement('div');
  298. let btn = document.createElement('div');
  299. btnBox.className = 'flex';
  300. btn.className = 'btn';
  301. btn.innerHTML = '<div class="video-icon"></div><div>人员列表</div>';
  302. btn.onclick = () => {
  303. emits('handleShowPeople', data);
  304. };
  305. btnBox.appendChild(btn);
  306. div.appendChild(btnBox);
  307. }
  308. let icon1 = document.createElement('div');
  309. icon1.className = 'icon1';
  310. let icon2 = document.createElement('div');
  311. icon2.className = 'icon2';
  312. let icon3 = document.createElement('div');
  313. icon3.className = 'icon3';
  314. let icon4 = document.createElement('div');
  315. icon4.className = 'icon4';
  316. div.appendChild(icon1);
  317. div.appendChild(icon2);
  318. div.appendChild(icon3);
  319. div.appendChild(icon4);
  320. let table = document.createElement('div');
  321. table.className = 'table-box';
  322. let content = '';
  323. content += '<div class="table">';
  324. const newData = filterTd(res.rows[0], data.dataType);
  325. newData.forEach((item) => {
  326. if (item.type === 'shortText') {
  327. content += '<div class="tr">';
  328. item.data.forEach((item2) => {
  329. content += '<div class="point-item">';
  330. content += '<div class="td1">' + item2.label + '</div><div class="td2">' + item2.value + '</div>';
  331. content += '</div>';
  332. });
  333. content += '</div>';
  334. } else {
  335. content += '<div class="point-item2">';
  336. content += '<div class="td1">' + item.data[0].label + '</div><div class="td2">' + item.data[0].value + '</div>';
  337. content += '</div>';
  338. }
  339. });
  340. content += '</div>';
  341. table.innerHTML = content;
  342. div.appendChild(table);
  343. let closeBtn = document.createElement('div');
  344. closeBtn.className = 'close';
  345. closeBtn.onclick = hideInfo;
  346. div.appendChild(closeBtn);
  347. showInfo(div, [data.longitude, data.latitude], true);
  348. }
  349. });
  350. };
  351. const filterTd = (obj, dataType) => {
  352. let data = [];
  353. let tempData = {};
  354. let i = 0;
  355. for (let key in obj) {
  356. let keyLabel = pointDetailTemplate[dataType][key];
  357. if (!!keyLabel) {
  358. if (i === 2) {
  359. i = 0;
  360. }
  361. const value = !!obj[key] ? obj[key] : '';
  362. if (value && value.length > 8) {
  363. if (i === 0) {
  364. data.push({ type: 'longText', data: [{ label: keyLabel, value: value }] });
  365. i = 0;
  366. } else {
  367. tempData = { type: 'longText', data: [{ label: keyLabel, value: value }] };
  368. }
  369. } else {
  370. if (i === 0) {
  371. data.push({ type: 'shortText', data: [{ label: keyLabel, value: value }] });
  372. } else {
  373. data[data.length - 1].data.push({ label: keyLabel, value: value });
  374. }
  375. i++;
  376. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  377. data.push(tempData);
  378. tempData = {};
  379. i = 0;
  380. }
  381. }
  382. }
  383. }
  384. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  385. data.push(tempData);
  386. }
  387. if (data[data.length - 1].data && data[data.length - 1].data.length === 1 && data[data.length - 1].type === 'shortText') {
  388. data[data.length - 1].data[1] = { label: '', value: '' };
  389. }
  390. return data;
  391. };
  392. // 监听地图类型变化
  393. watch(
  394. () => props.activeMap,
  395. (value, oldValue) => {
  396. switchMap(props.activeMap);
  397. }
  398. );
  399. watch(
  400. () => props.showMask,
  401. () => {
  402. if (props.showMask) {
  403. creatMask([{ strokeWeight: 2 }]);
  404. } else {
  405. removeMask();
  406. }
  407. }
  408. );
  409. // watch(
  410. // () => props.mouseToolState,
  411. // () => {
  412. // if (props.drawing) {
  413. // drawGraphics(props.mouseToolState);
  414. // } else {
  415. // closeDraw();
  416. // }
  417. // },
  418. // {
  419. // deep: true
  420. // }
  421. // );
  422. // 缩放级别变化
  423. const zoomChangeHandler = () => {
  424. mapState.zoom = map.getZoom();
  425. };
  426. const setCenter = (item) => {
  427. map.setCenter([item.longitude, item.latitude]);
  428. };
  429. const getMapUtils = () => {
  430. return mapUtils;
  431. };
  432. const getPlaceSearch = () => {
  433. return placeSearch;
  434. };
  435. const handleResize = () => {
  436. const containerWidth = !!containerScale ? containerRef.value.clientWidth * containerScale().scaleX : 1;
  437. const containerHeight = !!containerScale ? containerRef.value.clientHeight * containerScale().scaleY : 1;
  438. width.value = containerWidth + 'px';
  439. height.value = containerHeight + 'px';
  440. map.resize();
  441. };
  442. const changeShowMask = () => {
  443. emits('update:showMask', !props.showMask);
  444. };
  445. watch(
  446. () => appStore.showLeftSection,
  447. () => {
  448. nextTick(() => {
  449. handleResize();
  450. });
  451. }
  452. );
  453. watch(
  454. () => appStore.showRightSection,
  455. () => {
  456. nextTick(() => {
  457. handleResize();
  458. });
  459. }
  460. );
  461. onMounted(() => {
  462. window.addEventListener('resize', handleResize);
  463. });
  464. // 卸载事件
  465. onUnmounted(() => {
  466. if (map) {
  467. map.off('zoomchange', zoomChangeHandler);
  468. }
  469. window.removeEventListener('resize', handleResize);
  470. });
  471. provide('getMap', getMap);
  472. provide('getScale', getScale);
  473. defineExpose({
  474. addMarker,
  475. addSearchMarker,
  476. setCenter,
  477. getMarkers,
  478. clearMarker,
  479. getMap,
  480. drawTool,
  481. handleHover,
  482. trackPlayback,
  483. getMapUtils,
  484. getPlaceSearch
  485. });
  486. </script>
  487. <style lang="scss" scoped>
  488. @import 'map.scss';
  489. .map-container2 {
  490. width: 100%;
  491. height: 100%;
  492. position: relative;
  493. overflow: hidden;
  494. }
  495. .map-container {
  496. width: 100%;
  497. height: 100%;
  498. :deep(.amap-scalecontrol) {
  499. left: unset !important;
  500. background-color: unset !important;
  501. right: 62px;
  502. bottom: 55px !important;
  503. }
  504. :deep(.amap-scale-text) {
  505. text-align: left !important;
  506. padding-left: 20px;
  507. color: #eaf3fc;
  508. font-size: 12px;
  509. font-family: 'SourceHanSansCN';
  510. }
  511. :deep(.amap-scale-edgeleft),
  512. :deep(.amap-scale-middle),
  513. :deep(.amap-scale-edgeright) {
  514. border: 1px solid #bfe1fc !important;
  515. background: transparent !important;
  516. }
  517. :deep(.amap-logo),
  518. :deep(.amap-copyright) {
  519. display: none !important;
  520. }
  521. /* 自定义测距工具样式 */
  522. :deep(.amap-ranger) {
  523. background-color: #ffffff;
  524. border: 1px solid #888888;
  525. border-radius: 5px;
  526. padding: 5px;
  527. }
  528. :deep(.amap-ranger .amap-toolbar) {
  529. color: #333;
  530. font-size: 12px;
  531. padding: 5px;
  532. }
  533. :deep(.amap-ranger .amap-toolbar button) {
  534. background-color: #022577;
  535. color: #fff;
  536. border: none;
  537. border-radius: 4px;
  538. padding: 5px;
  539. margin-right: 5px;
  540. cursor: pointer;
  541. }
  542. :deep(.amap-ranger .amap-toolbar button:hover) {
  543. background-color: #1a3c75;
  544. }
  545. :deep(.amap-ranger .amap-toolbar .amap-toolbar-text) {
  546. color: #444;
  547. font-size: 14px;
  548. }
  549. :deep(.amap-marker) {
  550. font-size: 16px;
  551. }
  552. :deep(.amap-marker-label) {
  553. border: 1px solid #474747;
  554. background-color: rgba(255, 255, 255, 0.65);
  555. padding: 3px 8px;
  556. border-radius: 6px;
  557. }
  558. }
  559. </style>