index.vue 13 KB

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