index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. <template>
  2. <view class="container-box">
  3. <view :id="mapId" class="container"></view>
  4. <!-- info window弹框窗体 -->
  5. <component :is="componentView" ref="componentViewRef" />
  6. <slot></slot>
  7. </view>
  8. </template>
  9. <script>
  10. import Vue from 'vue'
  11. import AMapLoader from '@amap/amap-jsapi-loader';
  12. import { createPulseLinkLayer, createMarkerCluster, createMoveAlong, createMarker, createPolylineShow } from "./controller/createApi";
  13. import { uniqueId,isEmpty } from 'lodash'
  14. import {getMaxMinBounds,listCrudChange} from '@/utils/gdtq.js'
  15. /**地图展示相关组件*/
  16. import vehicleInfo from './components/vehicleInfo.vue';//聚合车辆弹框
  17. import pastRoute from './components/pastRoute.vue'; //轨迹弹框
  18. //组件映射
  19. const componentsMapDefault = {
  20. markerCluster: vehicleInfo,
  21. moveAnimation: pastRoute,
  22. }
  23. export default {
  24. components: {
  25. },
  26. props: {
  27. //外部重置默认弹窗组件
  28. componentsMapProps: {
  29. type: Object,
  30. default: () => {}
  31. },
  32. mapConfig: {
  33. type: Object,
  34. default: () => { }
  35. },
  36. pointsData: {
  37. type: Array,
  38. default: () => []
  39. },
  40. pointsDataConfig: {
  41. type: Object,
  42. default: () => { }
  43. },
  44. toolMapKey: { //markerCluster moveAnimation(轨迹回放)
  45. type: String,
  46. default: 'markerCluster'
  47. },
  48. isCreateInfoWindow: {
  49. type: Boolean,
  50. default: true
  51. },
  52. infoWindowConfig: {
  53. type: Object,
  54. default: () => {
  55. return {
  56. isCustom: true
  57. }
  58. }
  59. },
  60. //城市定位
  61. enableCityPositon: {
  62. type: Boolean,
  63. default: true
  64. }
  65. },
  66. watch: {
  67. pointsData: function(pointsData, prevPointsData) {
  68. /**对比改变后的数据做业务处理*/
  69. // console.log('watch地图数据', pointsData, prevPointsData)
  70. if (this.map === null) return;
  71. this.createProjects(this.toolMapKey, { points: this.pointsData, prePoints: prevPointsData })
  72. }
  73. },
  74. data() {
  75. return {
  76. map: null,
  77. mapId: uniqueId('gaode_map_'),
  78. /* 聚合相关 */
  79. cluster: undefined, //点聚合实例
  80. /* 轨迹相关 */
  81. createMoveAlongFn: null,//轨迹回放响应式实例对象
  82. allmarkerPro: undefined,
  83. isMoveAlongWindow: undefined,
  84. /* 弹窗相关*/
  85. createInfoWindow: undefined,
  86. componentView: undefined,
  87. satelliteLayer: '', //卫星图
  88. trafficLayer: '', //实时路况
  89. isMoveAlongWindow: false,
  90. }
  91. },
  92. mounted() {
  93. let that = this;
  94. //组件映射
  95. this.componentsMap = Object.assign(componentsMapDefault,this.componentsMapProps)
  96. /* 窗体相关 */
  97. this.createInfoWindow = function createInfoWindowFn() {
  98. let infoWindow;
  99. let cluster2;
  100. function add(data) {
  101. let { cluster, clusterData, lnglat, offset, componentsMapKey } = data
  102. if (clusterData) {
  103. cluster2 = clusterData.length > 0 ? clusterData : [cluster.p]
  104. }
  105. that.componentView = that.componentsMap[componentsMapKey]
  106. that.$nextTick(() => {
  107. console.log(that.$refs.componentViewRef,'that.that.$refs.componentViewRef')
  108. that.$refs.componentViewRef.callBack(data, closeWindow)
  109. if (clusterData?.length > 1) return;
  110. infoWindow = new AMap.InfoWindow({
  111. ...that.infoWindowConfig,
  112. content: that.$refs.componentViewRef.$el,
  113. offset: offset
  114. });
  115. that.map.on('click', function () {
  116. infoWindow.close();
  117. });
  118. setTimeout(() => {
  119. infoWindow.open(that.map, [lnglat.lng, lnglat.lat]);
  120. infoWindow.setOffset(new AMap.Pixel(0, -23));
  121. }, 0);
  122. })
  123. }
  124. function setPosition(data) {
  125. infoWindow.setPosition(data);
  126. }
  127. function closeWindow() {
  128. if (infoWindow) {
  129. infoWindow.close();
  130. }
  131. }
  132. function getItem() {
  133. return cluster2
  134. }
  135. function getinfoWindow() {
  136. return infoWindow
  137. }
  138. return {
  139. add: add,
  140. setPosition: setPosition,
  141. closeWindow: closeWindow,
  142. getItem: getItem,
  143. getinfoWindow: getinfoWindow,
  144. };
  145. }()
  146. this.initMap()
  147. },
  148. methods: {
  149. initMap() {
  150. AMapLoader.load({
  151. key: "08d130e013f96dcf4329cf7bac274c8e", // 申请好的Web端开发者Key,首次调用 load 时必填
  152. version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
  153. plugins: [
  154. 'AMap.MarkerCluster', 'AMap.Polyline', 'AMap.MoveAnimation', 'AMap.CircleEditor',
  155. 'AMap.Driving', 'AMap.AutoComplete', 'AMap.Scale','AMap.MouseTool', 'AMap.Geolocation','AMap.CitySearch',
  156. ], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
  157. Loca: { // 是否加载 Loca, 缺省不加载
  158. "version": '2.0.0' // Loca 版本,缺省 1.3.2
  159. },
  160. }).then((AMap) => {
  161. console.log('AMap Load','data')
  162. this.map = new AMap.Map(this.mapId, { //设置地图容器id
  163. viewMode: "3D", //是否为3D地图模式
  164. zoom: 3, //初始化地图级别
  165. center: [105.602725, 37.076636], //初始化地图中心点位置
  166. ...this.mapConfig
  167. });
  168. this.createProjects(this.toolMapKey, { points: this.pointsData, pointsDataConfig: this.pointsDataConfig })
  169. this.map.addControl(new AMap.Scale());
  170. if(this.enableCityPositon) {
  171. this.citySearch()
  172. }
  173. this.$emit('onload', this.map)
  174. }).catch(e => {
  175. console.log(e);
  176. })
  177. },
  178. createProjects(key, data, callBack) {
  179. if (!key) return
  180. const toolMapFn = {
  181. pulseLinkLayer: () => {//飞线
  182. createPulseLinkLayer(this.map, data, callBack);
  183. },
  184. markerCluster: () => {//创建聚合点
  185. if (!this.cluster) {
  186. this.cluster = createMarkerCluster(this.map, data, (e) => {
  187. const { clusterData, lnglat } = e
  188. if (clusterData.length > 1) {
  189. let {southWest,northEast} = getMaxMinBounds(clusterData.map((e) => [e.item.lng,e.item.lat]))
  190. let bounds = new AMap.Bounds(southWest, northEast)
  191. this.map.setBounds(bounds,true,[80, 80, 40, 40])
  192. }
  193. // console.log(e, 'eeeee')
  194. if (clusterData?.length > 1) return;
  195. this.$emit('handleMarkerClick',e)
  196. // this.createInfoWindow.closeWindow()
  197. // this.createInfoWindow.add({ ...e, offset: [0, -14], componentsMapKey: 'markerCluster' })//创建窗体
  198. });
  199. } else {
  200. let { points, prePoints } = data
  201. this.cluster.setData(points)
  202. /*移动窗体 */
  203. /* let activeItem = this.createInfoWindow.getItem()
  204. if (activeItem) {
  205. let filterItem = points.filter((item) => {
  206. return item.item.id == activeItem[0].item.id
  207. })
  208. if (!isEmpty(filterItem)) {
  209. this.createInfoWindow.setPosition([filterItem[0].item.lng, filterItem[0].item.lat])
  210. } else {
  211. this.createInfoWindow.closeWindow()
  212. }
  213. } */
  214. let newPoint = points.map(e => e.item)
  215. let oldPoint = prePoints.map(e => e.item)
  216. let {adds,deletes} = listCrudChange(newPoint,oldPoint)
  217. console.log(adds,'addsaddsadds')
  218. if( adds.length>=2) {
  219. let {southWest,northEast} = getMaxMinBounds(adds.map((e) => [e.lng,e.lat]))
  220. let bounds = new AMap.Bounds(southWest, northEast)
  221. this.$nextTick(() => {
  222. this.map.setBounds(bounds,true,[80, 80,40, 40])
  223. })
  224. console.log(bounds,'boundsbounds',this.map)
  225. } else if (adds.length == 1) {
  226. let item = adds[0]
  227. this.map.setZoomAndCenter(18, [item.lng,item.lat], true);
  228. }
  229. }
  230. },
  231. moveAnimation: () => {//创建轨迹回放
  232. let index = 0;
  233. if (this.createInfoWindow.getinfoWindow()) {
  234. this.createInfoWindow.closeWindow();
  235. }
  236. this.createMoveAlongFn = createMoveAlong(this.map, data, (key, e) => {
  237. if (key === 'moving') {
  238. index = e.index
  239. this.$emit('moving', e)
  240. /* if (this.createInfoWindow.getinfoWindow()) {
  241. if (this.isMoveAlongWindow) {
  242. this.createInfoWindow.setPosition([e.target.getPosition().lng, e.target.getPosition().lat])
  243. if (this.$refs.componentViewRef) {
  244. this.$refs.componentViewRef.callBack(e, this.createInfoWindow.closeWindow)
  245. }
  246. }
  247. } */
  248. this.$emit('handleMarkerClick',e, key)
  249. }
  250. if (key === 'moveTo') {
  251. /* if (this.createInfoWindow.getinfoWindow()) {
  252. if (this.isMoveAlongWindow) {
  253. this.createInfoWindow.setPosition(e.lnglat)
  254. if (this.$refs.componentViewRef) {
  255. this.$refs.componentViewRef.callBack(e, this.createInfoWindow.closeWindow)
  256. }
  257. }
  258. } */
  259. this.$emit('handleMarkerClick',e, key)
  260. }
  261. if (key === 'click') {
  262. if(e.resetIdx>index){
  263. e.index = e.resetIdx
  264. }else{
  265. e.index = index
  266. }
  267. this.$emit('handleMarkerClick',e, key)
  268. /* this.isMoveAlongWindow = true
  269. if (!this.isCreateInfoWindow) return;
  270. this.createInfoWindow.add({ index, lnglat: e.target.getPosition(), offset: [0, -23], componentsMapKey: 'moveAnimation' })//创建窗体 */
  271. }
  272. })
  273. },
  274. createMarker: () => { //创建标注点
  275. this.allmarkerPro = createMarker(this.map, data, (key, e) => { //返回目前地图上所有Mark实例
  276. if (this.createInfoWindow.getinfoWindow()) {
  277. this.createInfoWindow.closeWindow();
  278. }
  279. console.log('创建标注点', e)
  280. let lng = Number(e.lng02)
  281. let lat = Number(e.lat02)
  282. this.isMoveAlongWindow = false
  283. if (e.hiddenInfoWindow) return
  284. this.createInfoWindow.add({ lnglat: { lng, lat }, offset: [0, 0], componentsMapKey: e.type, info: e })//创建窗体
  285. })
  286. },
  287. removeAllMarkers: () => {//删除所有创建的标注点
  288. if (this.allmarkerPro) {
  289. this.allmarkerPro.removeAllMarkers()
  290. }
  291. },
  292. createPolyline: () => {//创建展示路线
  293. createPolylineShow(this.map, data)
  294. }
  295. }
  296. toolMapFn[key]();
  297. },
  298. citySearch() {
  299. //实例化城市查询类
  300. let that = this;
  301. var citysearch = new AMap.CitySearch();
  302. //自动获取用户IP,返回当前城市
  303. citysearch.getLocalCity(function(status, result) {
  304. if (status === 'complete' && result.info === 'OK') {
  305. if (result && result.city && result.bounds) {
  306. var citybounds = result.bounds;
  307. //地图显示当前城市
  308. that.map.setBounds(citybounds,true);
  309. }
  310. } else {
  311. }
  312. });
  313. },
  314. setisMoveAlongWindow(e) {
  315. isMoveAlongWindow = e
  316. },
  317. /**创建路线 */
  318. createPolyline(data, option) {
  319. let polyline
  320. polyline = new AMap.Polyline({
  321. map: this.map,
  322. path: data,
  323. showDir: true,
  324. strokeStyle: "solid",
  325. strokeColor: "#027AFF", //线颜色
  326. strokeWeight: 6, //线宽
  327. strokeOpacity: 1,
  328. ...option,
  329. });
  330. return polyline
  331. },
  332. /**移除覆盖物 */
  333. removeOverlays(Overlays) {
  334. this.map.remove(Overlays)
  335. },
  336. /**创建围栏 */
  337. createPolygon(data, option) {
  338. let Polygon
  339. Polygon = new AMap.Polygon({
  340. map: this.map,
  341. path: data,
  342. strokeStyle: "solid",
  343. fillColor: '#00b0ff',
  344. strokeColor: '#80d8ff',
  345. strokeWeight: 2, //轮廓线宽度
  346. ...option,
  347. });
  348. return Polygon
  349. },
  350. /**创建圆 */
  351. createCircle(center, option) {
  352. let Circle
  353. Circle = new AMap.Circle({
  354. center: center,
  355. radius: 50, //半径
  356. borderWeight: 3,
  357. strokeColor: "#FF33FF",
  358. strokeOpacity: 1,
  359. strokeWeight: 6,
  360. strokeOpacity: 0.2,
  361. fillOpacity: 0.4,
  362. fillColor: '#1791fc',
  363. ...option,
  364. });
  365. this.map.add(Circle)
  366. return Circle
  367. },
  368. /**批量创建标记点 */
  369. batchCreateMarkers(data, option) {
  370. let Markers = []
  371. data.forEach(item => {
  372. Markers.push(
  373. new AMap.Marker({
  374. position: item.position,
  375. content: item.content,
  376. offset: item.offset,
  377. title: item.title,
  378. ...option
  379. })
  380. )
  381. });
  382. this.map.add(Markers)
  383. return Markers
  384. },
  385. // 坐标集合适配
  386. setBoundsPolygon(bounds,immediately){
  387. let polygon = new AMap.Polygon({
  388. path: bounds,
  389. });
  390. let mybounds = polygon.getBounds();
  391. this.map.setBounds(mybounds,immediately);
  392. },
  393. /**视觉适配 */
  394. setFitView(...args) {
  395. this.map.setFitView(...args);
  396. },
  397. /**设置中心点 */
  398. setCenter(...args) {
  399. this.map.setCenter(...args);
  400. },
  401. /**地图缩放至指定级别并以指定点为地图显示中心点 */
  402. setZoomAndCenter(...args) {
  403. this.map.setZoomAndCenter(...args);
  404. },
  405. /* 设置卫星图 */
  406. setStelliteLayer(isShow = true) {
  407. if (isShow) {
  408. if (!this.satelliteLayer) {
  409. this.satelliteLayer = new AMap.TileLayer.Satellite();
  410. this.satelliteLayer.setMap(this.map);
  411. } else {
  412. this.satelliteLayer.show();
  413. }
  414. } else {
  415. if (this.satelliteLayer) this.satelliteLayer.hide();
  416. }
  417. },
  418. }
  419. }
  420. </script>
  421. <style scoped lang="scss">
  422. // 地图
  423. ::v-deep {
  424. .amap-logo {
  425. display: none !important;
  426. }
  427. .amap-copyright {
  428. display: none !important;
  429. }
  430. }
  431. .container-box {
  432. width: 100%;
  433. height: 100%;
  434. position: relative;
  435. .showspeed-box {
  436. border-radius: 6px;
  437. overflow: hidden;
  438. position: absolute;
  439. right: 12px;
  440. top: 11px;
  441. height: 33px;
  442. color: #fff;
  443. display: flex;
  444. align-items: center;
  445. text-align: center;
  446. height: 33px;
  447. line-height: 33px;
  448. font-size: 12px;
  449. .flex-al {
  450. display: flex;
  451. align-items: center;
  452. justify-content: center;
  453. }
  454. .checkbox {
  455. width: 84px;
  456. background: linear-gradient(180deg, #3296FF 0%, #027AFF 100%);
  457. border-radius: 6px 0px 0px 6px;
  458. :deep(.el-checkbox__label) {
  459. color: #fff;
  460. }
  461. :deep(.el-checkbox) {
  462. height: 33px;
  463. line-height: 33px;
  464. display: flex;
  465. align-items: center;
  466. justify-content: center;
  467. }
  468. }
  469. .speed-60 {
  470. width: 30px;
  471. background: #16B92A;
  472. }
  473. .speed-80 {
  474. width: 45px;
  475. background: #0000FE;
  476. }
  477. .speed-100 {
  478. width: 53px;
  479. background: #F98202;
  480. }
  481. .speed-m100 {
  482. width: 71px;
  483. background: #FE0100;
  484. border-radius: 0px 6px 6px 0px;
  485. }
  486. }
  487. .mapToolBox {
  488. position: absolute;
  489. display: flex;
  490. max-width: 500px;
  491. overflow: auto;
  492. top: 10px;
  493. left: 30px;
  494. }
  495. }
  496. .container {
  497. padding: 0px;
  498. margin: 0px;
  499. width: 100%;
  500. height: 100%;
  501. }
  502. </style>