index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. <template>
  2. <div ref="containerRef" class="map-container">
  3. <div id="aMap" class="map-container2" :class="{ 'custom-cursor': mapStore.isMapSelect }" :style="{ width: width, height: height }" />
  4. </div>
  5. </template>
  6. <script setup lang="ts" name="Map">
  7. import { useAMap } from '@/hooks/AMap/useAMap';
  8. import { useDrawTool } from '@/hooks/AMap/useDrawTool';
  9. import { getPointInfoList2 } from '@/api/globalMap';
  10. import { getDictLabel } from '@/utils/dict';
  11. import { methodList, titleList } from './data';
  12. import { pointDetailTemplate } from '@/views/globalMap/data/mapData';
  13. import useAppStore from '@/store/modules/app';
  14. import useMapStore from '@/store/modules/map';
  15. import mmJson from '@/assets/json/mm2.json';
  16. import { deepClone } from '@/utils';
  17. const containerScale = inject('containerScale');
  18. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  19. const { point_type } = toRefs<any>(proxy?.useDict('point_type'));
  20. const appStore = useAppStore();
  21. const mapStore = useMapStore();
  22. const emits = defineEmits(['handleShowVideo', 'handleShowWarehouse', 'handleShowPeople', 'handleShowVideoDetail', 'closeDetailDialog', 'resize']);
  23. const containerRef = ref();
  24. const width = ref('100%');
  25. const height = ref('100%');
  26. let AMap, map;
  27. // 鼠标绘制工具
  28. const drawTool = useDrawTool();
  29. mapStore.setMapLoaded(false);
  30. // 初始化地图
  31. const mapUtils = useAMap({
  32. key: '9c5041381e5e824f9ee324d8f7a40150', // 申请好的Web端开发者Key,首次调用 load 时必填
  33. securityJsCode: '4868bc1b8fac7d9e54e7279ed556879a', // 安全密钥
  34. version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  35. pitch: mapStore.mapState.isThreeDimensional ? 45 : 0,
  36. zoom: mapStore.mapState.zoom,
  37. maxZoom: mapStore.mapState.maxZoom,
  38. center: mapStore.mapState.center,
  39. dragEnable: true,
  40. scrollWheel: true,
  41. showScale: true,
  42. enableMouseTool: true,
  43. // 加载完成事件
  44. onLoadCompleted: () => {
  45. AMap = getAMap();
  46. map = getMap();
  47. mapStore.setMapLoaded(true);
  48. if (!['logical', 'vectorgraph'].includes(mapStore.activeMap)) {
  49. switchMap(mapStore.activeMap);
  50. }
  51. map.on('zoomchange', zoomChangeHandler);
  52. // 添加遮罩
  53. if (mapStore.showMask) {
  54. creatMask2(mmJson, { strokeWeight: 2 });
  55. }
  56. drawTool.initMouseTool({ container: 'aMap', map, AMap });
  57. handleResize();
  58. },
  59. onMarkerClick: (data, flag) => {
  60. let title = titleList[data.dataType];
  61. if (flag && !!title) {
  62. data.title = title;
  63. emits('closeDetailDialog', data);
  64. }
  65. // 多点位
  66. if (data.type === '1') {
  67. getPointInfoList2({
  68. option: mapStore.pointParams.option,
  69. dict_value: mapStore.pointParams.dict_value.toString(),
  70. zoom_level: mapStore.mapState.zoom,
  71. longitude: data.longitude.toString(),
  72. latitude: data.latitude.toString()
  73. }).then((res) => {
  74. const data2 = res.data;
  75. let content = document.createElement('div');
  76. // content.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  77. content.className = 'point-info';
  78. let content2 = '';
  79. content2 += '<div class="title-box"><div class="gradient-text">多点位信息</div></div>';
  80. content2 += '<div class="icon1"></div>';
  81. content2 += '<div class="icon2"></div>';
  82. content2 += '<div class="icon3"></div>';
  83. content2 += '<div class="icon4"></div>';
  84. content.innerHTML = content2;
  85. let tableBox = document.createElement('div');
  86. tableBox.className = 'table-box';
  87. let table = document.createElement('div');
  88. table.className = 'table';
  89. table.innerHTML = '<div class="point-item"><div class="td3">主题</div><div class="td3">名称</div></div>';
  90. data2.forEach((item) => {
  91. item.image = data.image;
  92. item.imageHover = data.imageHover;
  93. item.size = data.size;
  94. const div = document.createElement('div');
  95. div.className = 'point-item point-item-hover';
  96. div.innerHTML =
  97. '<div class="td4">' + getDictLabel(point_type.value, item.dataType.toString()) + '</div><div class="td4">' + item.name + '</div>';
  98. div.addEventListener('click', () => {
  99. const newItem = deepClone(item);
  100. if (newItem.dataType === 'video') {
  101. newItem.id = newItem.gbIndexCode;
  102. }
  103. handlePointDetails(newItem);
  104. });
  105. table.appendChild(div);
  106. });
  107. tableBox.appendChild(table);
  108. content.appendChild(tableBox);
  109. let closeBtn = document.createElement('div');
  110. closeBtn.className = 'close';
  111. closeBtn.onclick = () => {
  112. hideInfo(true);
  113. emits('closeDetailDialog', data);
  114. };
  115. content.appendChild(closeBtn);
  116. showInfo(content, [data.longitude, data.latitude], -data.size[1], true);
  117. });
  118. } else {
  119. handlePointDetails(data);
  120. }
  121. }
  122. });
  123. const { getMap, getAMap, switchMap, addSearchMarker, clearMarker, showInfo, hideInfo, handleHover, creatMask2, removeMask2, trackPlayback } = {
  124. ...mapUtils
  125. };
  126. const handlePointDetails = (data) => {
  127. let method = methodList[data.dataType];
  128. let title = !!titleList[data.dataType] ? titleList[data.dataType] : '信息';
  129. if (!method) return;
  130. method(data.id).then((res) => {
  131. if (!!pointDetailTemplate[data.dataType]) {
  132. let div = document.createElement('div');
  133. // div.style.cssText = 'transform: scale(' + containerScale().scaleX + ');';
  134. div.className = 'point-info';
  135. let titleDom = document.createElement('div');
  136. titleDom.className = 'title-box';
  137. titleDom.innerHTML = '<div class="gradient-text">' + title + '</div></div>';
  138. div.appendChild(titleDom);
  139. const objs = {
  140. '2': {
  141. title: '物资详情',
  142. method: 'handleShowWarehouse'
  143. },
  144. '41': {
  145. title: '人员列表',
  146. method: 'handleShowPeople'
  147. },
  148. '43': {
  149. title: '历史轨迹',
  150. method: 'handleShowTrack'
  151. }
  152. };
  153. let obj = objs[data.dataType];
  154. if ([4, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58].includes(data.dataType)) {
  155. obj = {
  156. title: '附近视频',
  157. method: 'handleShowVideo'
  158. };
  159. }
  160. if (!!obj) {
  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 = () => {
  210. hideInfo(true);
  211. emits('closeDetailDialog', data);
  212. };
  213. div.appendChild(closeBtn);
  214. showInfo(div, [data.longitude, data.latitude], -data.size[1], true);
  215. } else if (data.dataType === 'video') {
  216. emits('handleShowVideoDetail', res.data, true);
  217. }
  218. });
  219. };
  220. const filterTd = (obj, dataType) => {
  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. const handleResize = () => {
  290. const containerWidth = !!containerScale ? containerRef.value.clientWidth * containerScale().scaleX : 1;
  291. const containerHeight = !!containerScale ? containerRef.value.clientHeight * containerScale().scaleY : 1;
  292. width.value = containerWidth + 'px';
  293. height.value = containerHeight + 'px';
  294. map.resize();
  295. nextTick(() => {
  296. emits('resize');
  297. });
  298. };
  299. watch(
  300. () => appStore.showLeftSection,
  301. () => {
  302. nextTick(() => {
  303. handleResize();
  304. });
  305. }
  306. );
  307. watch(
  308. () => appStore.showRightSection,
  309. () => {
  310. nextTick(() => {
  311. handleResize();
  312. });
  313. }
  314. );
  315. const getMapDomSize = () => {
  316. const containerWidth = containerRef.value.clientWidth * containerScale().scaleX;
  317. const containerHeight = containerRef.value.clientHeight * containerScale().scaleY;
  318. return [containerWidth, containerHeight];
  319. };
  320. onMounted(() => {
  321. window.addEventListener('resize', handleResize);
  322. });
  323. // 卸载事件
  324. onUnmounted(() => {
  325. if (map) {
  326. map.off('zoomchange', zoomChangeHandler);
  327. }
  328. window.removeEventListener('resize', handleResize);
  329. });
  330. defineExpose({
  331. addSearchMarker,
  332. setCenter,
  333. clearMarker,
  334. getMap,
  335. drawTool,
  336. handleHover,
  337. trackPlayback,
  338. getMapUtils,
  339. getMapDomSize
  340. });
  341. </script>
  342. <style lang="scss" scoped>
  343. @import 'map.scss';
  344. .map-container2 {
  345. width: 100%;
  346. height: 100%;
  347. position: relative;
  348. overflow: hidden;
  349. }
  350. .map-container {
  351. width: 100%;
  352. height: 100%;
  353. :deep(.amap-scalecontrol) {
  354. left: unset !important;
  355. background-color: unset !important;
  356. right: 62px;
  357. bottom: 55px !important;
  358. }
  359. :deep(.amap-scale-text) {
  360. text-align: left !important;
  361. padding-left: 20px;
  362. color: #eaf3fc;
  363. font-size: 12px;
  364. font-family: 'SourceHanSansCN';
  365. }
  366. :deep(.amap-scale-edgeleft),
  367. :deep(.amap-scale-middle),
  368. :deep(.amap-scale-edgeright) {
  369. border: 1px solid #bfe1fc !important;
  370. background: transparent !important;
  371. }
  372. :deep(.amap-logo),
  373. :deep(.amap-copyright) {
  374. display: none !important;
  375. }
  376. /* 自定义测距工具样式 */
  377. :deep(.amap-ranger) {
  378. background-color: #ffffff;
  379. border: 1px solid #888888;
  380. border-radius: 5px;
  381. padding: 5px;
  382. }
  383. :deep(.amap-ranger .amap-toolbar) {
  384. color: #333;
  385. font-size: 12px;
  386. padding: 5px;
  387. }
  388. :deep(.amap-ranger .amap-toolbar button) {
  389. background-color: #022577;
  390. color: #fff;
  391. border: none;
  392. border-radius: 4px;
  393. padding: 5px;
  394. margin-right: 5px;
  395. cursor: pointer;
  396. }
  397. :deep(.amap-ranger .amap-toolbar button:hover) {
  398. background-color: #1a3c75;
  399. }
  400. :deep(.amap-ranger .amap-toolbar .amap-toolbar-text) {
  401. color: #444;
  402. font-size: 14px;
  403. }
  404. :deep(.amap-marker) {
  405. font-size: 16px;
  406. }
  407. :deep(.amap-marker-label) {
  408. border: 1px solid #474747;
  409. background-color: rgba(255, 255, 255, 0.65);
  410. padding: 3px 8px;
  411. border-radius: 6px;
  412. }
  413. }
  414. .custom-cursor {
  415. cursor:
  416. url('@/assets/images/map/mark.png') 13 31,
  417. auto !important;
  418. }
  419. </style>