index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. <template>
  2. <view class="jnpf-location-map">
  3. <u-top-tips ref="uTips" />
  4. <view class="content">
  5. <view class="user-select u-flex-col">
  6. <view class="user-select-search">
  7. <u-search :placeholder="$t('common.searchText')" v-model="keyword" height="72" :show-action="false"
  8. bg-color="#f0f2f6" shape="square" @change='search'>
  9. </u-search>
  10. </view>
  11. </view>
  12. </view>
  13. <view class="header">
  14. <view class="map-container">
  15. <map class='map' id="maps" :latitude="location.latitude" :longitude="location.longitude"
  16. :circles="circles" :polygons="polygons" :scale='15' @regionchange="regionChange">
  17. <!-- #ifdef H5 -->
  18. <cover-image class="map-marker h5-map-marker" src="/static/image/mark.png" />
  19. <!-- #endif -->
  20. <!-- #ifndef H5-->
  21. <cover-image class="map-marker" src="/static/image/mark.png" />
  22. <!-- #endif -->
  23. <cover-view class="map-locate" @click="handleLocate">
  24. <cover-image v-if="!locateLoading" src="/static/image/locate.png" />
  25. <cover-image v-else class="map-locate-img" src="/static/image/waite.png" />
  26. </cover-view>
  27. </map>
  28. </view>
  29. </view>
  30. <view class="around-contain">
  31. <scroll-view style="height:100%" id="scroll-view-h" class="scroll-view2" :refresher-enabled="false"
  32. :refresher-threshold="50" :scroll-with-animation='true' @scrolltolower="handleScrollToLower"
  33. :scroll-y="true">
  34. <radio-group class="around-contain-item" v-for="(item,index) in list" :key="index" v-if="list.length"
  35. @change="onSelectValueChange(item,index)">
  36. <label class="u-radio-label">
  37. <radio class="u-radio" :value="item.id" :checked="item.id === selectId" />
  38. <view class="around-item-title-box">
  39. <view class="around-item-title u-line-1"> {{ item.name }}</view>
  40. <view class="around-item-sub-title u-line-1"> {{ item.address }}</view>
  41. </view>
  42. </label>
  43. </radio-group>
  44. <u-loading class="loading" mode="circle" size="44" v-if="loading" />
  45. <JnpfEmpty v-if="!loading&&!list.length"></JnpfEmpty>
  46. </scroll-view>
  47. </view>
  48. <view class="jnpf-bottom-actions">
  49. <u-button class="buttom-btn" @click="close()">{{$t('common.cancelText')}}</u-button>
  50. <u-button class="buttom-btn" type="primary" @click.stop="handleConfirm()">{{$t('common.okText')}}</u-button>
  51. </view>
  52. </view>
  53. </template>
  54. <script>
  55. import {
  56. getAroundList,
  57. getTextList
  58. } from '@/api/common.js'
  59. export default {
  60. data() {
  61. return {
  62. loading: false,
  63. tabWidth: 150,
  64. tabIndex: 0,
  65. keyword: '',
  66. location: {
  67. longitude: 116.404, // 经度
  68. latitude: 39.915, // 纬度
  69. },
  70. circles: [],
  71. list: [],
  72. pagination: {
  73. currentPage: 1,
  74. pageSize: 50
  75. },
  76. total: 0,
  77. currentLocation: {},
  78. selectId: '',
  79. selectItem: {},
  80. enableLocation: '',
  81. showPopup: false,
  82. locateLoading: false,
  83. polygons: [],
  84. enableLocationScope: false,
  85. adjustmentScope: 500,
  86. enableDesktopLocation: false,
  87. locationScope: [],
  88. emitKey: '',
  89. // #ifdef APP
  90. dragLoading: false
  91. // #endif
  92. };
  93. },
  94. onLoad(e) {
  95. const data = e.data ? JSON.parse(e.data) : {}
  96. this.enableLocationScope = data.enableLocationScope || false
  97. this.adjustmentScope = data.adjustmentScope || 500
  98. this.enableDesktopLocation = data.enableDesktopLocation || false
  99. this.locationScope = data.locationScope || []
  100. this.emitKey = data.emitKey
  101. this.init()
  102. },
  103. methods: {
  104. init() {
  105. this.circles = []
  106. this.polygons = []
  107. this.selectId = ''
  108. this.list = []
  109. this.locateLoading = false
  110. // #ifdef APP
  111. this.dragLoading = false
  112. // #endif
  113. this.getLocation()
  114. },
  115. getLocation() {
  116. this.loading = true;
  117. uni.getLocation({
  118. type: 'gcj02',
  119. isHighAccuracy: true,
  120. success: (res) => {
  121. this.location.longitude = res.longitude // 经度
  122. this.location.latitude = res.latitude // 纬度
  123. //查询附近位置
  124. this.getList()
  125. //添加可选区域圆形
  126. this.handelCircle();
  127. //添加微调区域圆形
  128. this.handleScopeCircle();
  129. },
  130. fail: (err) => {
  131. //查询附近位置
  132. this.getList()
  133. //添加可选区域圆形
  134. this.handelCircle();
  135. //添加微调区域圆形
  136. this.handleScopeCircle();
  137. }
  138. });
  139. },
  140. handleGetCenter() {
  141. this.mapContext = uni.createMapContext("maps", this);
  142. this.mapContext.getCenterLocation({
  143. type: 'gcj02',
  144. geocode: true,
  145. isHighAccuracy: true,
  146. altitude: true,
  147. success: (res) => {
  148. this.location.longitude = res.longitude
  149. this.location.latitude = res.latitude
  150. if (this.enableLocationScope) {
  151. const discount = this.jnpf.getDistance(this.currentLocation.latitude, this
  152. .currentLocation.longitude, this.location.latitude, this.location.longitude
  153. ) || 0;
  154. if (discount > (this.adjustmentScope || 500)) return this.$refs.uTips.show({
  155. title: '超出微调范围',
  156. type: 'warning',
  157. });
  158. }
  159. this.getList()
  160. }
  161. })
  162. },
  163. handelCircle() {
  164. if (!this.enableDesktopLocation || !this.locationScope.length) return;
  165. for (let i = 0; i < this.locationScope.length; i++) {
  166. const o = this.locationScope[i];
  167. if (!o.lng || !o.lat || !o.radius) continue;
  168. o.longitude = o.lng
  169. o.latitude = o.lat
  170. this.addCircle({
  171. ...o,
  172. color: '#54d65e99',
  173. fillColor: '#54d65e66',
  174. });
  175. }
  176. },
  177. handleScopeCircle() {
  178. if (!this.enableLocationScope) return;
  179. this.currentLocation = this.$u.deepClone(this.location);
  180. this.addCircle({
  181. ...this.location,
  182. radius: this.adjustmentScope || 500,
  183. color: '#1890ff99',
  184. fillColor: '#1890ff66'
  185. });
  186. },
  187. addCircle(o) {
  188. // #ifdef H5
  189. this.polygons.push({
  190. points: this.CreateSimpleCircle(o.latitude, o.longitude, o.radius, 100),
  191. strokeColor: o.color,
  192. fillColor: o.fillColor,
  193. strokeWidth: 1
  194. })
  195. // #endif
  196. // #ifndef H5
  197. this.circles.push({
  198. ...o,
  199. strokeWidth: 1,
  200. })
  201. // #endif
  202. },
  203. // #ifdef H5
  204. CreateSimpleCircle(lat, lng, radius, pointCount) {
  205. var km = radius / 1000;
  206. var a = km < 5 ? 0.01 : km < 50 ? 0.1 : km < 500 ? 1 : 10;
  207. var b = this.getCircleDistance(lng, lat, lng + a, lat);
  208. var c = this.getCircleDistance(lng, lat, lng, lat + a);
  209. var rb = radius / b * a;
  210. var rc = radius / c * a;
  211. var arr = [];
  212. var n = 0,
  213. step = 360.0 / pointCount,
  214. N = 360 - step / 2; //注意浮点数±0.000000001的差异
  215. for (var i = 0; n < N; i++, n += step) {
  216. var x = lng + rb * Math.cos(n * Math.PI / 180);
  217. var y = lat + rc * Math.sin(n * Math.PI / 180);
  218. arr[i] = {
  219. latitude: y,
  220. longitude: x
  221. }
  222. }
  223. arr.push({
  224. latitude: arr[0].latitude,
  225. longitude: arr[0].longitude
  226. });
  227. return arr;
  228. },
  229. getCircleDistance(lng1, lat1, lng2, lat2) {
  230. var d = Math.PI / 180;
  231. var f = lat1 * d,
  232. h = lat2 * d;
  233. var i = lng2 * d - lng1 * d;
  234. var e = (1 - Math.cos(h - f) + (1 - Math.cos(i)) * Math.cos(f) * Math.cos(h)) / 2;
  235. return 2 * 6378137 * Math.asin(Math.sqrt(e));
  236. },
  237. // #endif
  238. regionChange(e) {
  239. // #ifdef APP
  240. if (this.dragLoading) return (this.dragLoading = false);
  241. this.list = [];
  242. this.handleGetCenter()
  243. // #endif
  244. // #ifndef APP
  245. if (e.detail.causedBy == 'drag' && e.type == 'end') {
  246. this.list = [];
  247. this.handleGetCenter()
  248. }
  249. // #endif
  250. },
  251. handleScrollToLower() {
  252. if (this.pagination.pageSize * this.pagination.currentPage < this.total) {
  253. this.pagination.currentPage = this.pagination.currentPage + 1;
  254. this.getList()
  255. } else {
  256. this.$u.toast('没有更多信息啦!')
  257. }
  258. },
  259. getList() {
  260. this.loading = true;
  261. const query = {
  262. key: this.define.aMapWebKey,
  263. location: this.location.longitude + ',' + this.location.latitude,
  264. radius: -1,
  265. offset: this.pagination.pageSize,
  266. page: this.pagination.currentPage,
  267. };
  268. getAroundList(query).then(res => {
  269. this.handleResult(res)
  270. }).catch(() => {
  271. this.loading = false;
  272. })
  273. },
  274. handleResult(res) {
  275. this.loading = false;
  276. if (res.data.status == '1') {
  277. this.list = [...this.list, ...res.data.pois || []];
  278. this.total = Number(res.data.count || 0)
  279. } else {
  280. this.$u.toast(res.data.info)
  281. }
  282. },
  283. onSelectValueChange(item, index) {
  284. // #ifdef APP
  285. this.dragLoading = true
  286. // #endif
  287. this.selectStatus = true
  288. this.selectId = item.id
  289. this.selectItem = item
  290. const [longitude, latitude] = (item.location || '').split(',');
  291. this.location = {
  292. longitude,
  293. latitude
  294. };
  295. // #ifdef APP
  296. setTimeout(() => {
  297. this.dragLoading = false
  298. }, 800)
  299. // #endif
  300. },
  301. handleConfirm() {
  302. if (!this.selectId) return this.$u.toast('请选择地址')
  303. const data = this.selectItem
  304. const [lng, lat] = data.location.split(',');
  305. if (this.enableLocationScope) {
  306. const discount = this.jnpf.getDistance(this.currentLocation.latitude, this.currentLocation
  307. .longitude, lat,
  308. lng) || 0;
  309. if (discount > (this.adjustmentScope || 500)) return this.$refs.uTips.show({
  310. title: '超出微调范围',
  311. type: 'warning',
  312. });
  313. }
  314. //判断可选范围
  315. if (this.enableDesktopLocation && this.locationScope.length) {
  316. let list = [];
  317. for (let i = 0; i < this.locationScope.length; i++) {
  318. const o = this.locationScope[i];
  319. const discount = this.jnpf.getDistance(o.lat, o.lng, lat, lng) || 0;
  320. list.push(discount > o.radius);
  321. }
  322. if (list.every(o => o === true)) return this.$refs.uTips.show({
  323. title: '超出规定范围',
  324. type: 'warning',
  325. });
  326. }
  327. const address = data.address && data.address.length ? data.address : '';
  328. //台湾、北京、上海、重庆、深圳地址特殊处理
  329. let fullAddress = data.pname + data.cityname + data.adname + address + data.name;
  330. if (data.pname == data.cityname) fullAddress = data.pname + data.adname + address + data.name;
  331. if (data.pname == data.cityname && data.pname == data.adname) fullAddress = data.pname + address +
  332. data.name;
  333. this.innerValue = {
  334. pName: data.pname,
  335. cName: data.cityname,
  336. adName: data.adname,
  337. address,
  338. name: data.name,
  339. lng,
  340. lat,
  341. fullAddress,
  342. };
  343. uni.$emit(this.emitKey, JSON.stringify(this.innerValue))
  344. this.close();
  345. },
  346. close() {
  347. uni.navigateBack({
  348. delta: 1
  349. });
  350. },
  351. getDistance(lat1, lon1, lat2, lon2) {
  352. const toRadians = (degrees) => {
  353. return degrees * (Math.PI / 180);
  354. }
  355. const R = 6371;
  356. const dLat = toRadians(lat2 - lat1);
  357. const dLon = toRadians(lon2 - lon1);
  358. const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
  359. Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
  360. Math.sin(dLon / 2) * Math.sin(dLon / 2);
  361. const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  362. const distance = R * c;
  363. return distance * 1000;
  364. },
  365. search() {
  366. // 节流,避免输入过快多次请求
  367. this.searchTimer && clearTimeout(this.searchTimer)
  368. this.searchTimer = setTimeout(() => {
  369. this.list = [];
  370. this.pagination.currentPage = 1
  371. this.keyword ? this.handleSearch() : this.getList();
  372. }, 300)
  373. },
  374. handleSearch() {
  375. this.loading = true;
  376. const query = {
  377. key: this.define.aMapWebKey,
  378. keywords: this.keyword,
  379. radius: this.enableLocationScope ? this.adjustmentScope || 500 : -1,
  380. offset: this.pagination.pageSize,
  381. page: this.pagination.currentPage,
  382. };
  383. getTextList(query).then(res => {
  384. this.handleResult(res);
  385. });
  386. },
  387. handleLocate() {
  388. if (this.locateLoading) return
  389. this.locateLoading = true
  390. uni.getLocation({
  391. type: 'gcj02',
  392. isHighAccuracy: true,
  393. success: (res) => {
  394. this.locateLoading = false
  395. if (!res.longitude || !res.latitude) return
  396. this.mapContext = uni.createMapContext("maps", this);
  397. this.mapContext.moveToLocation({
  398. longitude: res.longitude,
  399. latitude: res.latitude,
  400. })
  401. },
  402. fail: (res) => {
  403. this.locateLoading = false
  404. this.$u.toast('获取定位失败')
  405. }
  406. })
  407. },
  408. }
  409. };
  410. </script>
  411. <style scoped lang="scss">
  412. .jnpf-location-map {
  413. /* #ifdef H5 */
  414. height: calc(100vh - 44px);
  415. /* #endif */
  416. /* #ifndef H5 */
  417. height: 100vh;
  418. /* #endif */
  419. display: flex;
  420. flex-direction: column;
  421. .header {
  422. .map-container {
  423. position: relative;
  424. padding: 0rpx 20rpx;
  425. .map {
  426. width: 100%;
  427. height: 600rpx;
  428. }
  429. .map-marker {
  430. width: 38rpx;
  431. height: 64rpx;
  432. position: absolute;
  433. top: 50%;
  434. left: 50%;
  435. transform: translate(-50%, calc(-50% - 20rpx));
  436. z-index: 9999;
  437. }
  438. .h5-map-marker {
  439. transform: translate(-50%, calc(-50% - 30rpx));
  440. }
  441. .map-locate {
  442. position: absolute;
  443. bottom: 10px;
  444. right: 10px;
  445. height: 24px;
  446. width: 24px;
  447. padding: 4px;
  448. background-color: #fff;
  449. border-radius: 50%;
  450. box-shadow: 0 0 5px silver;
  451. z-index: 999;
  452. .map-locate-img {
  453. -webkit-animation: rotate 2s linear infinite;
  454. }
  455. @keyframes rotate {
  456. 0% {
  457. -webkit-transform: rotate(0deg);
  458. }
  459. 25% {
  460. -webkit-transform: rotate(90deg);
  461. }
  462. 50% {
  463. -webkit-transform: rotate(180deg);
  464. }
  465. 75% {
  466. -webkit-transform: rotate(270deg);
  467. }
  468. 100% {
  469. -webkit-transform: rotate(1turn);
  470. }
  471. }
  472. }
  473. }
  474. }
  475. .content {
  476. width: 100%;
  477. .user-select {
  478. .user-select-search {
  479. padding: 0rpx 20rpx;
  480. margin: 20rpx 0;
  481. }
  482. }
  483. }
  484. .around-contain {
  485. flex: 1;
  486. width: 100%;
  487. overflow: hidden;
  488. .loading {
  489. display: flex;
  490. justify-content: center;
  491. margin: 250rpx auto 0;
  492. }
  493. .around-contain-item {
  494. display: flex;
  495. align-items: center;
  496. padding: 10rpx 0;
  497. height: 60px;
  498. line-height: 22px;
  499. border-bottom: 1px solid #f2f2f6;
  500. .u-radio-label {
  501. width: 100%;
  502. display: flex;
  503. align-items: center;
  504. // #ifdef MP
  505. :deep(.u-radio) {
  506. margin: 0 16rpx 0 20rpx;
  507. }
  508. // #endif
  509. // #ifndef MP
  510. :deep(.uni-radio-input) {
  511. margin: 0 16rpx 0 20rpx;
  512. }
  513. // #endif
  514. .around-item-title-box {
  515. flex: 1;
  516. min-width: 0;
  517. padding-right: 16rpx;
  518. .around-item-title {
  519. font-size: 30rpx;
  520. color: #171a1d;
  521. }
  522. .around-item-sub-title {
  523. font-size: 28rpx;
  524. color: #b9babb;
  525. padding-top: 8rpx;
  526. }
  527. }
  528. }
  529. }
  530. }
  531. }
  532. </style>