tiandituMap.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. <template>
  2. <view id="mapDiv" class="mapDiv" :apikey="apiKey" :prop="option" :change:prop="Trenderjs.initTMap">
  3. <view class="tool-btn">
  4. <view class="position" @click.stop="Trenderjs.onPosition">
  5. <image style="width: 100%;height: 100%;" src="@/static/images/position.png" mode="aspectFit"></image>
  6. </view>
  7. <view class="position" @click.stop="$emit('openHint')">
  8. <image style="width: 100%;height: 100%;" src="@/static/images/hint.png" mode="aspectFit"></image>
  9. </view>
  10. </view>
  11. </view>
  12. </template>
  13. <script>
  14. import tools from './tools.js'
  15. import iconPath from '@/static/images/point.png'
  16. import { EventBus } from "@/utils/vueBus.js"
  17. export default {
  18. name: "tianditu",
  19. props: {
  20. apiKey: {
  21. type: String,
  22. require: true,
  23. default: ''
  24. },
  25. },
  26. data() {
  27. return {
  28. checkedDot: false,
  29. Tmap: null,
  30. option: {
  31. // dotPath:dotPath,
  32. type: '',
  33. apikey: '',
  34. lng: '',
  35. lat: '',
  36. png: iconPath
  37. }
  38. };
  39. },
  40. created() {
  41. console.log('===== ', this.option)
  42. },
  43. mounted() {
  44. },
  45. beforeDestroy() {
  46. // EventBus.$off('someEvent'); // 确保在组件销毁前移除事件监听
  47. },
  48. methods: {
  49. compliteonLoadTianDiTu() {
  50. this.$emit('onLoadTianDiTu')
  51. },
  52. initCharts(lng, lat, iconPng) {
  53. this.option = {
  54. ...this.option,
  55. apikey: this.apiKey,
  56. lng,
  57. lat,
  58. png: iconPng || this.option.png,
  59. type: 'open'
  60. }
  61. // setTimeout(() => {
  62. // this.Trenderjs.getLocation()
  63. // }, 5000)
  64. },
  65. upDataCharts(lng, lat) {
  66. this.option = {
  67. ...this.option,
  68. type: 'Icon',
  69. lng,
  70. lat,
  71. png: this.customIcon || this.option.png,
  72. type: 'update'
  73. }
  74. },
  75. async nextPoint(lnglat) {
  76. var that = this;
  77. let params = {
  78. postStr: JSON.stringify({
  79. lon: lnglat.lng,
  80. lat: lnglat.lat,
  81. ver: 1,
  82. }),
  83. type: 'geocode',
  84. tk: that.apiKey
  85. }
  86. let resData = await tools.createRequest('https://api.tianditu.gov.cn/geocoder', params, true)
  87. if (resData.status === '0') {
  88. const info = tools.formatterAdressLocation(resData.result, 1)
  89. this.option = {
  90. ...this.option,
  91. apikey: this.apiKey,
  92. lng: lnglat.lng,
  93. lat: lnglat.lat,
  94. png: this.customIcon || this.option.png,
  95. type: 'update'
  96. }
  97. this.$emit('onSelect', info)
  98. } else {
  99. tools.createMessage('数据异常', 1000, false, 'error')
  100. }
  101. },
  102. }
  103. }
  104. </script>
  105. <script module="Trenderjs" lang="renderjs">
  106. import { EventBus } from "@/utils/vueBus.js"
  107. import iconPath from '@/static/images/point.png'
  108. import dotPath from '@/static/images/dot.png'
  109. import locationPath from '@/static/images/location.png'
  110. import $confog from "@/config/index.js"
  111. import { mapGetters } from "vuex"
  112. const { type } = $confog
  113. var Tmap = null;
  114. const left = -(uni.upx2px(150));
  115. export default {
  116. props: {
  117. // 点位列表
  118. siteListArr: {
  119. type: Array,
  120. default: () => {
  121. }
  122. },
  123. searchDot: {
  124. type: Boolean,
  125. default: false
  126. },
  127. },
  128. data() {
  129. return {
  130. options: {},
  131. markerList: [],
  132. labelLsit: [],
  133. MyLngLat: {
  134. lng: '',
  135. lat: ''
  136. },
  137. // 中心点位
  138. centreDot: null,
  139. // 中心点位地址解析
  140. centreText: null,
  141. // 中心点信息
  142. centreMarker: '',
  143. // 我的点位信息
  144. MyMarker: null
  145. }
  146. },
  147. computed: {
  148. ...mapGetters(['useLocation'])
  149. },
  150. mounted() {
  151. },
  152. beforeDestroy() {
  153. this.removeMapEvent()
  154. },
  155. watch: {
  156. siteListArr: {
  157. handler(newL, oldL) {
  158. this.handleSiteList(newL)
  159. },
  160. deep: true,
  161. immediate: true
  162. },
  163. centreText: {
  164. handler(newL, oldL) {
  165. if (newL && newL !== oldL) {
  166. this.$emit("centre-text-callback", newL)
  167. }
  168. },
  169. deep: true
  170. },
  171. useLocation: {
  172. handler(newL, oldL) {
  173. if (this.MyMarker) this.setMyIcon()
  174. },
  175. deep: true
  176. }
  177. },
  178. methods: {
  179. handleSiteList(newArr) {
  180. this.clearIcon().then(async (res) => {
  181. const dotArr = []
  182. if (Tmap) await this.getMapCenter();
  183. const { Lng, Lat } = this.centreDot || {};
  184. let maxIcon = false;
  185. (newArr || []).forEach((el, index) => {
  186. el.maxIcon = false;
  187. // 通过搜索自动移动到收索返回的第一个点位
  188. if (index === 0 && this.searchDot) {
  189. this.SelectedDot(el, true, 12)
  190. }
  191. const { longitude, latitude, mapTypeIcon } = el;
  192. if (Lng == longitude && Lat == latitude) {
  193. el.maxIcon = true;
  194. maxIcon = true;
  195. };
  196. this.setIcon(el);
  197. });
  198. // 是否标记中心点
  199. if (!maxIcon) this.setCentre()
  200. this.setMyIcon()
  201. })
  202. // console.log('siteListArr = ', newL)
  203. // if (this.siteListArr && this.siteListArr.length) {
  204. // setTimeout(() => {
  205. // console.log("this.centreDot 1 = ", this.centreDot)
  206. // const { longitude, latitude, mapTypeIcon } = el;
  207. // }, 10);
  208. // }
  209. },
  210. getMapCenter() {
  211. return new Promise((resolve) => {
  212. const Fn = () => {
  213. if (Tmap) {
  214. const c = Tmap.getCenter();
  215. this.centreDot = {
  216. Lng: c.getLng(),
  217. Lat: c.getLat()
  218. };
  219. this.dotParse(c);
  220. resolve()
  221. } else {
  222. setTimeout(() => {
  223. Fn()
  224. }, 100);
  225. }
  226. };
  227. Fn()
  228. })
  229. },
  230. // 中心点位逆解析
  231. dotParse(v) {
  232. try {
  233. const geocode = new T.Geocoder();
  234. geocode.getLocation(v, (result) => {
  235. if (result.getStatus() == 0) {
  236. let t = '';
  237. const { poi } = result.getAddressComponent()
  238. try {
  239. if (poi && poi.indexOf('湖北省武汉市') === 0) {
  240. const lo = poi.split('湖北省武汉市')
  241. lo.forEach((el, index) => {
  242. if (index >= 1) {
  243. t = `${t}${el}`
  244. }
  245. })
  246. }
  247. } catch (error) {
  248. //TODO handle the exception
  249. }
  250. this.centreText = t || poi
  251. } else {
  252. this.centreText = null
  253. }
  254. });
  255. } catch (error) {
  256. this.centreText = null
  257. }
  258. },
  259. onPosition() {
  260. // const { longitude, latitude } = { longitude: 114.414431, latitude: 30.482926 };
  261. // this.SelectedDot({ longitude, latitude })
  262. // this.MyLngLat = {
  263. // lng: longitude,
  264. // lat: latitude
  265. // }
  266. // console.log("this.MyLngLat = " , this.MyLngLat)
  267. // this.setMyIcon()
  268. EventBus.$emit('TianDiTuSearch', (res) => {
  269. const { longitude, latitude } = res || {};
  270. if (longitude && latitude) {
  271. this.SelectedDot({ longitude, latitude })
  272. }
  273. // // this.SelectedDot({ longitude, latitude })
  274. // // this.MyLngLat = {
  275. // // lng: longitude,
  276. // // lat: latitude
  277. // // }
  278. // // this.clearIcon()
  279. })
  280. },
  281. addMapEvent() {
  282. //移除地图的移动停止事件
  283. this.getLocation()
  284. this.removeMapEvent()
  285. if (Tmap) {
  286. Tmap.addEventListener("moveend", this.MapMoveend);
  287. Tmap.addEventListener('zoomend', this.handleZoomEvent);
  288. }
  289. },
  290. removeMapEvent() {
  291. //移除地图的移动停止事件
  292. try {
  293. if (Tmap) {
  294. Tmap.removeEventListener("moveend", this.MapMoveend);
  295. Tmap.removeEventListener("zoomend", this.handleZoomEvent);
  296. }
  297. } catch (error) {
  298. //TODO handle the exception
  299. }
  300. },
  301. // 移动地图,获取中心点
  302. MapMoveend(e) {
  303. try {
  304. // if (type === 'H6') {
  305. // const v = e.target.getCenter()
  306. // // 自然缩放移动,刷新接口
  307. // const Lng = v.getLng();
  308. // const Lat = v.getLat();
  309. // this.$emit("moveMap", { Lng, Lat })
  310. // } else {
  311. // // 地图移动,判断是否缩放/移动地图,还是通过点位定位
  312. // if (!this.checkedDot) {
  313. // const v = e.target.getCenter()
  314. // // 自然缩放移动,刷新接口
  315. // const Lng = v.getLng();
  316. // const Lat = v.getLat();
  317. // this.$emit("moveMap", { Lng, Lat })
  318. // } else {
  319. // // 点位移动,做处理
  320. // this.$nextTick(() => {
  321. // this.checkedDot = false;
  322. // })
  323. // }
  324. // }
  325. // 地图移动,判断是否缩放/移动地图,还是通过点位定位
  326. if (!this.checkedDot) {
  327. const v = e.target.getCenter()
  328. // 自然缩放移动,刷新接口
  329. if (v) {
  330. const Lng = v.getLng();
  331. const Lat = v.getLat();
  332. this.$emit("moveMap", { Lng, Lat })
  333. }
  334. } else {
  335. // 点位移动,做处理
  336. this.$nextTick(() => {
  337. this.checkedDot = false;
  338. })
  339. }
  340. } catch (error) {
  341. //TODO handle the exception
  342. }
  343. },
  344. // 点击地图标注的点位
  345. clickMapSite({ type, target, lnglat, containerPoint }) {
  346. const Lng = lnglat.getLng();
  347. const Lat = lnglat.getLat();
  348. this.$emit('handleMapSite', { Lng, Lat })
  349. },
  350. // 天地图缩放事件
  351. handleZoomEvent(event) {
  352. if (!this.labelLsit || this.labelLsit.length === 0) return;
  353. this.labelLsit.forEach(el => {
  354. this.labelShowOrHide(el)
  355. })
  356. },
  357. // 标签的显示与隐藏
  358. labelShowOrHide(label) {
  359. if (!Tmap) return
  360. let currentZoom = Tmap.getZoom();
  361. if (currentZoom >= 16) {
  362. label.show()
  363. } else {
  364. label.hide()
  365. }
  366. },
  367. initTmap_() {
  368. try {
  369. Tmap = null;
  370. Tmap = new T.Map('mapDiv', {
  371. projection: 'EPSG:4326',
  372. });
  373. } catch (error) {
  374. console.log('Tmap error= ', error)
  375. //TODO handle the exception
  376. }
  377. },
  378. setViewport() {
  379. try {
  380. console.log('window.devicePixelRatio = ', window.devicePixelRatio)
  381. console.log('window.getResolution 1 = 1 ', Tmap, window.screen.width * window.devicePixelRatio)
  382. const w = window.screen.width * window.devicePixelRatio
  383. const meta = document.createElement('meta')
  384. meta.name = 'viewport'
  385. meta.content =
  386. `width=device-width,initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0, user-scalable=no`
  387. // meta.content = `initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no`
  388. // meta.media = `(device-height: 568px)`
  389. document.head.appendChild(meta)
  390. } catch (err) {
  391. console.log('---------- ', err)
  392. }
  393. },
  394. initTMap(newValue, oldValue, ownerInstance, instance) {
  395. this.options = newValue
  396. if (newValue.type === 'open' && newValue.apikey) {
  397. if (!window.T) {
  398. // this.setViewport()
  399. const script = document.createElement('script')
  400. // script.src = "https://tile0.tianditu.gov.cn/vts?t=vt&pk=ag1adgmgd&tk=92053ec7c4309bf20d822b28f150f33d&v=1.0"
  401. script.src = 'https://api.tianditu.gov.cn/api?v=4.0&tk=' + this.options.apikey
  402. script.onload = this.initChartsRender.bind(this)
  403. document.head.appendChild(script)
  404. } else {
  405. const {
  406. lng,
  407. lat
  408. } = this.options
  409. this.initTmap_()
  410. Tmap.centerAndZoom(new T.LngLat(lng, lat), 15);
  411. this.$ownerInstance.callMethod('nextPoint', {
  412. lng,
  413. lat
  414. })
  415. Tmap.addEventListener('click', (e) => {
  416. this.$ownerInstance.callMethod('nextPoint', e.lnglat)
  417. });
  418. }
  419. } else {
  420. // 选点,更新
  421. // const {
  422. // lng,
  423. // lat
  424. // } = newValue
  425. // this.upDataChartsRender(lng, lat)
  426. this.$emit("clickMap")
  427. }
  428. },
  429. initChartsRender() {
  430. this.$ownerInstance.callMethod('compliteonLoadTianDiTu')
  431. const {
  432. lng,
  433. lat
  434. } = this.options
  435. var that = this;
  436. this.initTmap_()
  437. Tmap.centerAndZoom(new T.LngLat(lng, lat), 15);
  438. this.$ownerInstance.callMethod('nextPoint', {
  439. lng,
  440. lat
  441. })
  442. Tmap.addEventListener('click', (e) => {
  443. this.$ownerInstance.callMethod('nextPoint', e.lnglat)
  444. });
  445. // Tmap.addEventListener("moveend", (e) => {
  446. // console.log('addEventListener = ' , e)
  447. // });
  448. this.addMapEvent()
  449. this.$emit("handleSearch")
  450. },
  451. // upDataChartsRender(lng, lat) {
  452. // if (!Tmap) return
  453. // this.setIcon(lng, lat, true)
  454. // Tmap.centerAndZoom(new T.LngLat(lng, lat), 15);
  455. // },
  456. // setIcon(lng, lat, isClear, iconU, info, max = false) {
  457. setIcon(row) {
  458. const { longitude, latitude, mapTypeIcon, locationName, maxIcon } = row
  459. // const w = uni.upx2px(54)
  460. // const h = uni.upx2px(54)
  461. const icon = new T.Icon({
  462. iconUrl: mapTypeIcon || this.options.png,
  463. iconSize: maxIcon ? new T.Point(60, 60) : new T.Point(45, 45),
  464. iconAnchor: new T.Point(15, 45)
  465. });
  466. const marker = new T.Marker(new T.LngLat(longitude, latitude), {
  467. icon
  468. });
  469. // 点位注册点击事件
  470. try {
  471. marker.removeEventListener("click", this.clickMapSite);
  472. } catch (error) {}
  473. marker.addEventListener("click", this.clickMapSite);
  474. // 缓存注册点击事件的点位
  475. this.markerList.push(marker)
  476. Tmap.addOverLay(marker);
  477. if (locationName) {
  478. try {
  479. var label = new T.Label({
  480. // text: this.setLable(locationName),
  481. text: locationName,
  482. position: marker.getLngLat(),
  483. offset: new T.Point(left, 30)
  484. });
  485. Tmap.addOverLay(label);
  486. label.setLngLat(marker.getLngLat());
  487. label.setBorderLine(0);
  488. // label.setBackgroundColor ('transparent');
  489. label.setFontSize(10);
  490. this.labelLsit.push(label)
  491. // 判断当前是否显示
  492. this.labelShowOrHide(label)
  493. } catch (error) {
  494. //TODO handle the exception
  495. console.log("info = error ", error)
  496. }
  497. }
  498. },
  499. // 移除点位,并注销点位绑定的点击事件
  500. clearIcon() {
  501. return new Promise((resolve, reject) => {
  502. try {
  503. (this.markerList || []).forEach(el => {
  504. try {
  505. el.removeEventListener("click", this.clickMapSite);
  506. } catch (error) {}
  507. });
  508. this.markerList = [];
  509. this.labelLsit = [];
  510. this.centreMarker = null;
  511. this.MyMarker = null;
  512. Tmap.clearOverLays();
  513. this.setMyIcon()
  514. } catch (error) {
  515. //TODO handle the exception
  516. } finally {
  517. resolve()
  518. }
  519. })
  520. },
  521. getLocation() {
  522. var lo = new T.Geolocation();
  523. console.log("天地图获取定位 = ", lo, lo.getStatus())
  524. try {
  525. lo.getCurrentPosition((res) => {
  526. console.log('获取定位', res)
  527. });
  528. } catch (error) {
  529. //TODO handle the exception
  530. console.log("天地图获取定位 = error ", error)
  531. }
  532. },
  533. SelectedDot(res, dot = false, level = 16) {
  534. this.checkedDot = dot
  535. const { longitude, latitude } = res;
  536. Tmap.panTo(new T.LngLat(longitude, latitude), level); // 移动到选中的点位
  537. setTimeout(function() {
  538. this.checkedDot = false
  539. }, 40);
  540. },
  541. setMaxIcon() {
  542. },
  543. setMyIcon() {
  544. const { longitude, latitude } = this.useLocation || {}
  545. if (!Tmap || !longitude || !latitude) return;
  546. // const w = uni.upx2px(54)
  547. // const h = uni.upx2px(54)
  548. const icon = new T.Icon({
  549. iconUrl: locationPath,
  550. iconSize: new T.Point(30, 30)
  551. });
  552. const marker = new T.Marker(new T.LngLat(longitude, latitude), {
  553. icon
  554. });
  555. Tmap.addOverLay(marker);
  556. try {
  557. marker.removeEventListener("click", () => {});
  558. } catch (error) {}
  559. marker.addEventListener("click", () => {});
  560. this.MyMarker = marker
  561. },
  562. setLable(locationName) {
  563. let el = '';
  564. for (let i = 0; i < locationName.length; i++) {
  565. if (!i || i % 10) {
  566. el += `${locationName[i]}`
  567. } else {
  568. el += `<br>${locationName[i]}`
  569. }
  570. };
  571. return `<p class='map-label'>${el}<p>`
  572. },
  573. // setTcon
  574. // centreDot
  575. // 设置中心点坐标
  576. setCentre() {
  577. if (type !== 'H6') return
  578. // siteListArr
  579. if (this.centreDot) {
  580. try {
  581. const { Lng, Lat } = this.centreDot || {};
  582. // const w = uni.upx2px(54)
  583. // const h = uni.upx2px(92)
  584. const icon = new T.Icon({
  585. iconUrl: dotPath,
  586. iconSize: new T.Point(27, 46)
  587. });
  588. const d_ = new T.Marker(new T.LngLat(Lng, Lat), {
  589. icon,
  590. zIndexOffset: 666
  591. });
  592. Tmap.addOverLay(d_);
  593. this.centreMarker = d_
  594. } catch (error) {
  595. //TODO handle the exception
  596. console.log('this.centreDot error ', error)
  597. }
  598. }
  599. }
  600. },
  601. }
  602. </script>
  603. <style lang="scss">
  604. .mapDiv {
  605. width: 100%;
  606. height: 100%;
  607. }
  608. .tool-btn {
  609. position: fixed;
  610. right: 30rpx;
  611. bottom: 45vh;
  612. z-index: 1000;
  613. }
  614. .position {
  615. width: 94rpx;
  616. height: 99rpx;
  617. background-color: #fff;
  618. padding: 10rpx;
  619. border-radius: 22rpx;
  620. padding: 23rpx 20rpx 22rpx 20rpx;
  621. .position-icon {
  622. width: 100%;
  623. height: 100%;
  624. path {
  625. fill: #3291F8;
  626. }
  627. }
  628. &+.position{
  629. margin-top: 20rpx;
  630. }
  631. }
  632. </style>