index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <template>
  2. <div ref="containerRef" class="map-container">
  3. <div
  4. id="aMap"
  5. class="map-container2"
  6. :class="{ 'custom-cursor': mapStore.isMapSelect }"
  7. :style="{
  8. width: width,
  9. height: height,
  10. transform: 'scale(' + inverseScale.inverseScaleX + ', ' + inverseScale.inverseScaleY + ')',
  11. transformOrigin: '0 0'
  12. }"
  13. />
  14. </div>
  15. </template>
  16. <script setup lang="ts" name="Map">
  17. import { useAMap } from '@/hooks/AMap/useAMap';
  18. import { useDrawTool } from '@/hooks/AMap/useDrawTool';
  19. import { getPointInfoList2 } from '@/api/globalMap';
  20. import { getDictLabel } from '@/utils/dict';
  21. import { methodList, titleList } from './data';
  22. import { pointDetailTemplate } from '@/views/globalMap/data/mapData';
  23. import useAppStore from '@/store/modules/app';
  24. import mmJson from '@/assets/json/mm2.json';
  25. import useMapStore from '@/store/modules/map';
  26. const containerScale = inject('containerScale');
  27. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  28. const { point_type } = toRefs<any>(proxy?.useDict('point_type'));
  29. const appStore = useAppStore();
  30. const mapStore = useMapStore();
  31. const emits = defineEmits([
  32. 'selectGraphics',
  33. 'unSelectGraphics',
  34. 'showTextEditBox',
  35. 'onDrawCompleted',
  36. 'handleShowVideo',
  37. 'handleShowWarehouse',
  38. 'handleShowPeople'
  39. ]);
  40. const containerRef = ref();
  41. const width = ref('100%');
  42. const height = ref('100%');
  43. let map, AMap, placeSearch;
  44. // 鼠标绘制工具
  45. const drawTool = useDrawTool();
  46. mapStore.setMapLoaded(false);
  47. // 初始化地图
  48. const mapUtils = useAMap({
  49. key: '9c5041381e5e824f9ee324d8f7a40150', // 申请好的Web端开发者Key,首次调用 load 时必填
  50. securityJsCode: '4868bc1b8fac7d9e54e7279ed556879a',
  51. version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  52. pitch: mapStore.mapState.isThreeDimensional ? 45 : 0,
  53. zoom: mapStore.mapState.zoom,
  54. maxZoom: mapStore.mapState.maxZoom,
  55. center: mapStore.mapState.center,
  56. dragEnable: true,
  57. scrollWheel: true,
  58. showScale: true,
  59. enableMouseTool: true,
  60. // 加载完成事件
  61. onLoadCompleted: () => {
  62. AMap = getAMap();
  63. map = getMap();
  64. mapStore.setMapLoaded(true);
  65. if (!['logical', 'vectorgraph'].includes(mapStore.activeMap)) {
  66. switchMap(mapStore.activeMap);
  67. }
  68. placeSearch = new AMap.PlaceSearch({
  69. pageSize: 30,
  70. pageIndex: 1,
  71. city: '0668',
  72. extensions: 'base'
  73. });
  74. map.on('zoomchange', zoomChangeHandler);
  75. // 添加遮罩
  76. if (mapStore.showMask) {
  77. creatMask2(mmJson, { strokeWeight: 2 });
  78. }
  79. drawTool.initMouseTool({ map, AMap });
  80. handleResize();
  81. },
  82. onMarkerClick: (data) => {
  83. // 多点位
  84. if (data.type === '1') {
  85. getPointInfoList2({
  86. option: mapStore.pointParams.option,
  87. dict_value: mapStore.pointParams.dict_value.toString(),
  88. zoom_level: mapStore.mapState.zoom,
  89. longitude: data.longitude.toString(),
  90. latitude: data.latitude.toString()
  91. }).then((res) => {
  92. const data2 = res.data;
  93. let content = document.createElement('div');
  94. // content.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  95. content.className = 'point-info';
  96. let content2 = '';
  97. content2 += '<div class="title-box"><div class="gradient-text">多点位信息</div></div>';
  98. content2 += '<div class="icon1"></div>';
  99. content2 += '<div class="icon2"></div>';
  100. content2 += '<div class="icon3"></div>';
  101. content2 += '<div class="icon4"></div>';
  102. content.innerHTML = content2;
  103. let tableBox = document.createElement('div');
  104. tableBox.className = 'table-box';
  105. let table = document.createElement('div');
  106. table.className = 'table';
  107. table.innerHTML = '<div class="point-item"><div class="td3">主题</div><div class="td3">名称</div></div>';
  108. data2.forEach((item) => {
  109. item.image = data.image;
  110. item.imageHover = data.imageHover;
  111. item.size = data.size;
  112. const div = document.createElement('div');
  113. div.className = 'point-item point-item-hover';
  114. div.innerHTML =
  115. '<div class="td4">' + getDictLabel(point_type.value, item.dataType.toString()) + '</div><div class="td4">' + item.name + '</div>';
  116. div.addEventListener('click', () => {
  117. handlePointDetails(item);
  118. });
  119. table.appendChild(div);
  120. });
  121. tableBox.appendChild(table);
  122. content.appendChild(tableBox);
  123. let closeBtn = document.createElement('div');
  124. closeBtn.className = 'close';
  125. closeBtn.onclick = hideInfo;
  126. content.appendChild(closeBtn);
  127. showInfo(content, [data.longitude, data.latitude], -data.size[1], true);
  128. });
  129. } else {
  130. handlePointDetails(data);
  131. }
  132. }
  133. });
  134. const { getMap, getAMap, switchMap, addSearchMarker, clearMarker, showInfo, hideInfo, handleHover, creatMask2, removeMask2, trackPlayback } = {
  135. ...mapUtils
  136. };
  137. const handlePointDetails = (data) => {
  138. let method = methodList[data.dataType];
  139. let title = !!titleList[data.dataType] ? titleList[data.dataType] : '信息';
  140. if (!method) return;
  141. method(data.id).then((res) => {
  142. if (!!pointDetailTemplate[data.dataType]) {
  143. let div = document.createElement('div');
  144. // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  145. div.className = 'point-info';
  146. let titleDom = document.createElement('div');
  147. titleDom.className = 'title-box';
  148. titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
  149. div.appendChild(titleDom);
  150. if ([2, 4, 41, 43].includes(data.dataType)) {
  151. const objs = {
  152. '2': {
  153. title: '物资详情',
  154. method: 'handleShowWarehouse'
  155. },
  156. '4': {
  157. title: '附近视频',
  158. method: 'handleShowVideo'
  159. },
  160. '41': {
  161. title: '人员列表',
  162. method: 'handleShowPeople'
  163. },
  164. '43': {
  165. title: '历史轨迹',
  166. method: 'handleShowTrack'
  167. }
  168. };
  169. const obj = objs[data.dataType];
  170. let btnBox = document.createElement('div');
  171. let btn = document.createElement('div');
  172. btnBox.className = 'flex';
  173. btn.className = 'btn';
  174. btn.innerHTML = '<div class="video-icon"></div>' + obj.title + '<div></div>';
  175. btn.onclick = () => {
  176. emits(obj.method, data);
  177. };
  178. btnBox.appendChild(btn);
  179. div.appendChild(btnBox);
  180. }
  181. let icon1 = document.createElement('div');
  182. icon1.className = 'icon1';
  183. let icon2 = document.createElement('div');
  184. icon2.className = 'icon2';
  185. let icon3 = document.createElement('div');
  186. icon3.className = 'icon3';
  187. let icon4 = document.createElement('div');
  188. icon4.className = 'icon4';
  189. div.appendChild(icon1);
  190. div.appendChild(icon2);
  191. div.appendChild(icon3);
  192. div.appendChild(icon4);
  193. let table = document.createElement('div');
  194. table.className = 'table-box';
  195. let content = '';
  196. content += '<div class="table">';
  197. const newData = filterTd(res.rows[0], data.dataType);
  198. newData.forEach((item) => {
  199. if (item.type === 'shortText') {
  200. content += '<div class="tr">';
  201. item.data.forEach((item2) => {
  202. content += '<div class="point-item">';
  203. content += '<div class="td1">' + item2.label + '</div><div class="td2">' + item2.value + '</div>';
  204. content += '</div>';
  205. });
  206. content += '</div>';
  207. } else {
  208. content += '<div class="point-item2">';
  209. content += '<div class="td1">' + item.data[0].label + '</div><div class="td2">' + item.data[0].value + '</div>';
  210. content += '</div>';
  211. }
  212. });
  213. content += '</div>';
  214. table.innerHTML = content;
  215. div.appendChild(table);
  216. let closeBtn = document.createElement('div');
  217. closeBtn.className = 'close';
  218. closeBtn.onclick = hideInfo;
  219. div.appendChild(closeBtn);
  220. showInfo(div, [data.longitude, data.latitude], -data.size[1], true);
  221. }
  222. });
  223. };
  224. const filterTd = (obj, dataType) => {
  225. if (!obj) {
  226. return [];
  227. }
  228. let data = [];
  229. let tempData = {};
  230. let i = 0;
  231. const pointDetailTemplateObj = pointDetailTemplate[dataType];
  232. Object.keys(pointDetailTemplateObj).forEach((key) => {
  233. let keyLabel = pointDetailTemplateObj[key];
  234. if (!!keyLabel) {
  235. if (i === 2) {
  236. i = 0;
  237. }
  238. const value = !!obj[key] ? obj[key] : '';
  239. if (value && value.length > 8) {
  240. if (i === 0) {
  241. data.push({ type: 'longText', data: [{ label: keyLabel, value: value }] });
  242. i = 0;
  243. } else {
  244. tempData = { type: 'longText', data: [{ label: keyLabel, value: value }] };
  245. }
  246. } else {
  247. if (i === 0) {
  248. data.push({ type: 'shortText', data: [{ label: keyLabel, value: value }] });
  249. } else {
  250. data[data.length - 1].data.push({ label: keyLabel, value: value });
  251. }
  252. i++;
  253. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  254. data.push(tempData);
  255. tempData = {};
  256. i = 0;
  257. }
  258. }
  259. }
  260. });
  261. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  262. data.push(tempData);
  263. }
  264. if (data[data.length - 1].data && data[data.length - 1].data.length === 1 && data[data.length - 1].type === 'shortText') {
  265. data[data.length - 1].data[1] = { label: '', value: '' };
  266. }
  267. return data;
  268. };
  269. // 监听地图类型变化
  270. watch(
  271. () => mapStore.activeMap,
  272. () => {
  273. switchMap(mapStore.activeMap);
  274. }
  275. );
  276. watch(
  277. () => mapStore.showMask,
  278. () => {
  279. if (mapStore.showMask) {
  280. creatMask2(mmJson, { strokeWeight: 2 });
  281. } else {
  282. removeMask2(true);
  283. }
  284. }
  285. );
  286. // 缩放级别变化
  287. const zoomChangeHandler = () => {
  288. mapStore.setZoom(map.getZoom());
  289. };
  290. const setCenter = (item) => {
  291. map.setCenter([item.longitude, item.latitude]);
  292. };
  293. const getMapUtils = () => {
  294. return mapUtils;
  295. };
  296. const getPlaceSearch = () => {
  297. return placeSearch;
  298. };
  299. let inverseScale = ref({
  300. inverseScaleX: 1,
  301. inverseScaleY: 1
  302. });
  303. const handleResize = () => {
  304. inverseScale.value.inverseScaleX = 1 / containerScale().scaleX;
  305. inverseScale.value.inverseScaleY = 1 / containerScale().scaleY;
  306. const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
  307. const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;
  308. width.value = containerWidth + 'px';
  309. height.value = containerHeight + 'px';
  310. map.resize();
  311. };
  312. watch(
  313. () => appStore.showLeftSection,
  314. () => {
  315. nextTick(() => {
  316. handleResize();
  317. });
  318. }
  319. );
  320. watch(
  321. () => appStore.showRightSection,
  322. () => {
  323. nextTick(() => {
  324. handleResize();
  325. });
  326. }
  327. );
  328. const getMapDomSize = () => {
  329. const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
  330. const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;
  331. return [containerWidth, containerHeight];
  332. };
  333. onMounted(() => {
  334. window.addEventListener('resize', handleResize);
  335. });
  336. // 卸载事件
  337. onUnmounted(() => {
  338. if (map) {
  339. map.off('zoomchange', zoomChangeHandler);
  340. }
  341. window.removeEventListener('resize', handleResize);
  342. });
  343. defineExpose({
  344. addSearchMarker,
  345. setCenter,
  346. clearMarker,
  347. getMap,
  348. getMapUtils,
  349. drawTool,
  350. handleHover,
  351. trackPlayback,
  352. getPlaceSearch,
  353. getMapDomSize
  354. });
  355. </script>
  356. <style lang="scss" scoped>
  357. @import 'map.scss';
  358. .map-container2 {
  359. width: 100%;
  360. height: 100%;
  361. position: relative;
  362. overflow: hidden;
  363. }
  364. .map-container {
  365. width: 100%;
  366. height: 100%;
  367. :deep(.amap-scalecontrol) {
  368. left: unset !important;
  369. background-color: unset !important;
  370. right: 56px;
  371. bottom: 17px !important;
  372. }
  373. :deep(.amap-scale-text) {
  374. text-align: left !important;
  375. padding-left: 20px;
  376. color: #eaf3fc;
  377. font-size: vw(32);
  378. font-family: 'SourceHanSansCN';
  379. }
  380. :deep(.amap-scale-edgeleft),
  381. :deep(.amap-scale-middle),
  382. :deep(.amap-scale-edgeright) {
  383. border: 1px solid #bfe1fc !important;
  384. background: transparent !important;
  385. }
  386. :deep(.amap-logo),
  387. :deep(.amap-copyright) {
  388. display: none !important;
  389. }
  390. /* 自定义测距工具样式 */
  391. :deep(.amap-ranger) {
  392. background-color: #ffffff;
  393. border: 1px solid #888888;
  394. border-radius: 5px;
  395. padding: 5px;
  396. }
  397. :deep(.amap-ranger .amap-toolbar) {
  398. color: #333;
  399. font-size: 12px;
  400. padding: 5px;
  401. }
  402. :deep(.amap-ranger .amap-toolbar button) {
  403. background-color: #022577;
  404. color: #fff;
  405. border: none;
  406. border-radius: 4px;
  407. padding: 5px;
  408. margin-right: 5px;
  409. cursor: pointer;
  410. }
  411. :deep(.amap-ranger .amap-toolbar button:hover) {
  412. background-color: #1a3c75;
  413. }
  414. :deep(.amap-ranger .amap-toolbar .amap-toolbar-text) {
  415. color: #444;
  416. font-size: 14px;
  417. }
  418. :deep(.amap-marker) {
  419. font-size: 16px;
  420. }
  421. :deep(.amap-marker-label) {
  422. border: 1px solid #474747;
  423. background-color: rgba(255, 255, 255, 0.65);
  424. padding: 3px 8px;
  425. border-radius: 4px;
  426. }
  427. }
  428. .custom-cursor {
  429. cursor:
  430. url('@/assets/images/map/mark.png') 13 31,
  431. auto !important;
  432. }
  433. </style>