index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  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 { getPointInfoList } 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. let path = [];
  86. mapStore.pointType.forEach((item) => {
  87. path.push(item.component);
  88. });
  89. getPointInfoList({
  90. option: path.toString(),
  91. longitude: data.longitude.toString(),
  92. latitude: data.latitude.toString()
  93. }).then((res) => {
  94. const data2 = res.data.list;
  95. let content = document.createElement('div');
  96. // content.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  97. content.className = 'point-info';
  98. let content2 = '';
  99. content2 += '<div class="title-box"><div class="gradient-text">多点位信息</div></div>';
  100. content2 += '<div class="icon1"></div>';
  101. content2 += '<div class="icon2"></div>';
  102. content2 += '<div class="icon3"></div>';
  103. content2 += '<div class="icon4"></div>';
  104. content.innerHTML = content2;
  105. let tableBox = document.createElement('div');
  106. tableBox.className = 'table-box';
  107. let table = document.createElement('div');
  108. table.className = 'table';
  109. table.innerHTML = '<div class="point-item"><div class="td3">主题</div><div class="td3">名称</div></div>';
  110. data2.forEach((item) => {
  111. item.longitude = data.longitude;
  112. item.latitude = data.latitude;
  113. const div = document.createElement('div');
  114. div.className = 'point-item point-item-hover';
  115. div.innerHTML =
  116. '<div class="td4">' + getDictLabel(point_type.value, item.dataType.toString()) + '</div><div class="td4">' + item.name + '</div>';
  117. div.addEventListener('click', () => {
  118. handlePointDetails(item);
  119. });
  120. table.appendChild(div);
  121. });
  122. tableBox.appendChild(table);
  123. content.appendChild(tableBox);
  124. let closeBtn = document.createElement('div');
  125. closeBtn.className = 'close';
  126. closeBtn.onclick = hideInfo;
  127. content.appendChild(closeBtn);
  128. showInfo(content, [data.longitude, data.latitude], true);
  129. });
  130. } else {
  131. handlePointDetails(data);
  132. }
  133. }
  134. });
  135. const {
  136. getMap,
  137. getAMap,
  138. switchMap,
  139. addMarker,
  140. addSearchMarker,
  141. clearMarker,
  142. showInfo,
  143. hideInfo,
  144. handleHover,
  145. creatMask2,
  146. removeMask2,
  147. trackPlayback
  148. } = { ...mapUtils };
  149. const handlePointDetails = (data) => {
  150. let method = methodList[data.dataType];
  151. let title = !!titleList[data.dataType] ? titleList[data.dataType] : '信息';
  152. if (!method) return;
  153. method(data.id).then((res) => {
  154. if (!!pointDetailTemplate[data.dataType]) {
  155. let div = document.createElement('div');
  156. // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  157. div.className = 'point-info';
  158. let titleDom = document.createElement('div');
  159. titleDom.className = 'title-box';
  160. titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
  161. div.appendChild(titleDom);
  162. if ([2, 4, 41, 43].includes(data.dataType)) {
  163. const objs = {
  164. '2': {
  165. title: '物资详情',
  166. method: 'handleShowWarehouse'
  167. },
  168. '4': {
  169. title: '附近视频',
  170. method: 'handleShowVideo'
  171. },
  172. '41': {
  173. title: '人员列表',
  174. method: 'handleShowPeople'
  175. },
  176. '43': {
  177. title: '历史轨迹',
  178. method: 'handleShowTrack'
  179. }
  180. };
  181. const obj = objs[data.dataType];
  182. let btnBox = document.createElement('div');
  183. let btn = document.createElement('div');
  184. btnBox.className = 'flex';
  185. btn.className = 'btn';
  186. btn.innerHTML = '<div class="video-icon"></div>' + obj.title + '<div></div>';
  187. btn.onclick = () => {
  188. emits(obj.method, data);
  189. };
  190. btnBox.appendChild(btn);
  191. div.appendChild(btnBox);
  192. }
  193. let icon1 = document.createElement('div');
  194. icon1.className = 'icon1';
  195. let icon2 = document.createElement('div');
  196. icon2.className = 'icon2';
  197. let icon3 = document.createElement('div');
  198. icon3.className = 'icon3';
  199. let icon4 = document.createElement('div');
  200. icon4.className = 'icon4';
  201. div.appendChild(icon1);
  202. div.appendChild(icon2);
  203. div.appendChild(icon3);
  204. div.appendChild(icon4);
  205. let table = document.createElement('div');
  206. table.className = 'table-box';
  207. let content = '';
  208. content += '<div class="table">';
  209. const newData = filterTd(res.rows[0], data.dataType);
  210. newData.forEach((item) => {
  211. if (item.type === 'shortText') {
  212. content += '<div class="tr">';
  213. item.data.forEach((item2) => {
  214. content += '<div class="point-item">';
  215. content += '<div class="td1">' + item2.label + '</div><div class="td2">' + item2.value + '</div>';
  216. content += '</div>';
  217. });
  218. content += '</div>';
  219. } else {
  220. content += '<div class="point-item2">';
  221. content += '<div class="td1">' + item.data[0].label + '</div><div class="td2">' + item.data[0].value + '</div>';
  222. content += '</div>';
  223. }
  224. });
  225. content += '</div>';
  226. table.innerHTML = content;
  227. div.appendChild(table);
  228. let closeBtn = document.createElement('div');
  229. closeBtn.className = 'close';
  230. closeBtn.onclick = hideInfo;
  231. div.appendChild(closeBtn);
  232. showInfo(div, [data.longitude, data.latitude], true);
  233. }
  234. });
  235. };
  236. const filterTd = (obj, dataType) => {
  237. if (!obj) {
  238. return [];
  239. }
  240. let data = [];
  241. let tempData = {};
  242. let i = 0;
  243. const pointDetailTemplateObj = pointDetailTemplate[dataType];
  244. Object.keys(pointDetailTemplateObj).forEach((key) => {
  245. let keyLabel = pointDetailTemplateObj[key];
  246. if (!!keyLabel) {
  247. if (i === 2) {
  248. i = 0;
  249. }
  250. const value = !!obj[key] ? obj[key] : '';
  251. if (value && value.length > 8) {
  252. if (i === 0) {
  253. data.push({ type: 'longText', data: [{ label: keyLabel, value: value }] });
  254. i = 0;
  255. } else {
  256. tempData = { type: 'longText', data: [{ label: keyLabel, value: value }] };
  257. }
  258. } else {
  259. if (i === 0) {
  260. data.push({ type: 'shortText', data: [{ label: keyLabel, value: value }] });
  261. } else {
  262. data[data.length - 1].data.push({ label: keyLabel, value: value });
  263. }
  264. i++;
  265. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  266. data.push(tempData);
  267. tempData = {};
  268. i = 0;
  269. }
  270. }
  271. }
  272. });
  273. if (!!tempData && JSON.stringify(tempData) !== '{}') {
  274. data.push(tempData);
  275. }
  276. if (data[data.length - 1].data && data[data.length - 1].data.length === 1 && data[data.length - 1].type === 'shortText') {
  277. data[data.length - 1].data[1] = { label: '', value: '' };
  278. }
  279. return data;
  280. };
  281. // 监听地图类型变化
  282. watch(
  283. () => mapStore.activeMap,
  284. () => {
  285. switchMap(mapStore.activeMap);
  286. }
  287. );
  288. watch(
  289. () => mapStore.showMask,
  290. () => {
  291. if (mapStore.showMask) {
  292. creatMask2(mmJson, { strokeWeight: 2 });
  293. } else {
  294. removeMask2(true);
  295. }
  296. }
  297. );
  298. // 缩放级别变化
  299. const zoomChangeHandler = () => {
  300. mapStore.setZoom(map.getZoom());
  301. };
  302. const setCenter = (item) => {
  303. map.setCenter([item.longitude, item.latitude]);
  304. };
  305. const getMapUtils = () => {
  306. return mapUtils;
  307. };
  308. const getPlaceSearch = () => {
  309. return placeSearch;
  310. };
  311. let inverseScale = ref({
  312. inverseScaleX: 1,
  313. inverseScaleY: 1
  314. });
  315. const handleResize = () => {
  316. inverseScale.value.inverseScaleX = 1 / containerScale().scaleX;
  317. inverseScale.value.inverseScaleY = 1 / containerScale().scaleY;
  318. const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
  319. const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;
  320. width.value = containerWidth + 'px';
  321. height.value = containerHeight + 'px';
  322. map.resize();
  323. };
  324. watch(
  325. () => appStore.showLeftSection,
  326. () => {
  327. nextTick(() => {
  328. handleResize();
  329. });
  330. }
  331. );
  332. watch(
  333. () => appStore.showRightSection,
  334. () => {
  335. nextTick(() => {
  336. handleResize();
  337. });
  338. }
  339. );
  340. onMounted(() => {
  341. window.addEventListener('resize', handleResize);
  342. });
  343. // 卸载事件
  344. onUnmounted(() => {
  345. if (map) {
  346. map.off('zoomchange', zoomChangeHandler);
  347. }
  348. window.removeEventListener('resize', handleResize);
  349. });
  350. defineExpose({
  351. addMarker,
  352. addSearchMarker,
  353. setCenter,
  354. clearMarker,
  355. getMap,
  356. getMapUtils,
  357. drawTool,
  358. handleHover,
  359. trackPlayback,
  360. getPlaceSearch
  361. });
  362. </script>
  363. <style lang="scss" scoped>
  364. @import 'map.scss';
  365. .map-container2 {
  366. width: 100%;
  367. height: 100%;
  368. position: relative;
  369. overflow: hidden;
  370. }
  371. .map-container {
  372. width: 100%;
  373. height: 100%;
  374. :deep(.amap-scalecontrol) {
  375. left: unset !important;
  376. background-color: unset !important;
  377. right: 56px;
  378. bottom: 17px !important;
  379. }
  380. :deep(.amap-scale-text) {
  381. text-align: left !important;
  382. padding-left: 20px;
  383. color: #eaf3fc;
  384. font-size: vw(32);
  385. font-family: 'SourceHanSansCN';
  386. }
  387. :deep(.amap-scale-edgeleft),
  388. :deep(.amap-scale-middle),
  389. :deep(.amap-scale-edgeright) {
  390. border: 1px solid #bfe1fc !important;
  391. background: transparent !important;
  392. }
  393. :deep(.amap-logo),
  394. :deep(.amap-copyright) {
  395. display: none !important;
  396. }
  397. /* 自定义测距工具样式 */
  398. :deep(.amap-ranger) {
  399. background-color: #ffffff;
  400. border: 1px solid #888888;
  401. border-radius: 5px;
  402. padding: 5px;
  403. }
  404. :deep(.amap-ranger .amap-toolbar) {
  405. color: #333;
  406. font-size: 12px;
  407. padding: 5px;
  408. }
  409. :deep(.amap-ranger .amap-toolbar button) {
  410. background-color: #022577;
  411. color: #fff;
  412. border: none;
  413. border-radius: 4px;
  414. padding: 5px;
  415. margin-right: 5px;
  416. cursor: pointer;
  417. }
  418. :deep(.amap-ranger .amap-toolbar button:hover) {
  419. background-color: #1a3c75;
  420. }
  421. :deep(.amap-ranger .amap-toolbar .amap-toolbar-text) {
  422. color: #444;
  423. font-size: 14px;
  424. }
  425. :deep(.amap-marker) {
  426. font-size: 16px;
  427. }
  428. :deep(.amap-marker-label) {
  429. border: 1px solid #474747;
  430. background-color: rgba(255, 255, 255, 0.65);
  431. padding: 3px 8px;
  432. border-radius: 4px;
  433. }
  434. }
  435. .custom-cursor {
  436. cursor:
  437. url('@/assets/images/map/mark.png') 13 31,
  438. auto !important;
  439. }
  440. </style>