index.vue 17 KB

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