index.vue 17 KB

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