liupishui-pickerpolygons.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. <template>
  2. <view class="box-picker_position">
  3. <view class="map_wp">
  4. <map id="map_20221212" class="picker-map" scale="12" min-scale="12" @regionchange="handleRegionchange" :latitude="position.latitude" :longitude="position.longitude" show-location='true' :polygons="polygons"></map>
  5. <view :class="['picker_map_location', animateLocation?'animated':'']"></view>
  6. </view>
  7. <view class="list-picker_position">
  8. <view class="hd">
  9. <input type="text" v-model="searchKey" class="input_text" placeholder="请输入搜索地点">
  10. <button class="btn-search" type="default" @click="searchPosition"><view class="picker-search"></view>搜索</button>
  11. </view>
  12. <view class="bd">
  13. <view v-for="(item,index) in searchlist" @click="changeSelectItem(index)" :class="['item',item.useable?'':'disabled']" :key="index">
  14. <view class="item-l">
  15. <view class="picker-address"></view>
  16. </view>
  17. <view class="item-c">
  18. <view class="title">{{item.title}}</view>
  19. <view class="address">{{item.address}}</view>
  20. </view>
  21. <view class="item-r">
  22. <view v-if="item.select" class="picker-checked"></view>
  23. </view>
  24. </view>
  25. </view>
  26. <view class="ft">
  27. <button @click="confirmSelect" :class="['btn-selected', canConfirm?'':'disabled']">确定选点</button>
  28. </view>
  29. </view>
  30. </view>
  31. </template>
  32. <script>
  33. var QQMapWX = require('./qqmap-wx-jssdk.js');
  34. var qqmapsdk,handleRegionchangeTimmer;
  35. export default {
  36. props:{
  37. polygons:{
  38. type:Array,
  39. default:[
  40. {
  41. points:[
  42. {
  43. latitude:'36.829066',
  44. longitude:'118.025761',
  45. },
  46. {
  47. latitude:'36.825012',
  48. longitude:'118.066702',
  49. },
  50. {
  51. latitude:'36.81161',
  52. longitude:'118.042681'
  53. },
  54. {
  55. latitude:'36.801964',
  56. longitude:'118.034875',
  57. },
  58. ],
  59. strokeWidth:1,
  60. strokeColor:'#ff000066',
  61. fillColor:'#ff000016'
  62. },
  63. {
  64. points:[
  65. {
  66. latitude:'36.817369',
  67. longitude:'118.001311',
  68. },
  69. {
  70. latitude:'36.808682',
  71. longitude:'118.010281',
  72. },{
  73. latitude:'36.805555',
  74. longitude:'117.996537',
  75. },
  76. ],
  77. strokeWidth:1,
  78. strokeColor:'#ff000066',
  79. fillColor:'#ff000016'
  80. },
  81. ]
  82. },
  83. qqmapsdkKey:{
  84. type:String,
  85. default:''
  86. },
  87. base_url:{
  88. type:String,
  89. default:''
  90. },
  91. sig:{
  92. type:String,
  93. default:''
  94. }
  95. },
  96. data() {
  97. return {
  98. canConfirm: false,
  99. animateLocation:false,
  100. isChangeSelectItem:false,
  101. searchKey:'',
  102. position:{
  103. latitude:'36.811995',
  104. longitude:'118.05539'
  105. },
  106. searchlist:[]
  107. };
  108. },
  109. mounted(){
  110. if(this.qqmapsdkKey==''){
  111. console.error('需要腾讯地图开发Key,申请地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview')
  112. return;
  113. }
  114. qqmapsdk = new QQMapWX({
  115. key:this.qqmapsdkKey,
  116. BASE_URL:this.base_url
  117. });
  118. let that = this;
  119. let needRegetLocation = true;
  120. if(uni.getStorageSync('mapPickerPosition')){
  121. let data = JSON.parse(uni.getStorageSync('mapPickerPosition'));
  122. if(new Date().getTime() < data.expireTime){
  123. needRegetLocation = false;
  124. that.position.latitude = data.position.latitude;
  125. that.position.longitude = data.position.longitude;
  126. that.init();
  127. }
  128. }
  129. if(needRegetLocation){
  130. uni.getLocation({
  131. cacheTimeout:1,
  132. accuracy:'best',
  133. type:'gcj02',
  134. isHighAccuracy:true,
  135. success:function(e){
  136. console.log(e);
  137. that.position.latitude = e.latitude;
  138. that.position.longitude = e.longitude;
  139. uni.setStorageSync('mapPickerPosition',JSON.stringify({
  140. expireTime: new Date().getTime() - 0 + 60*1000,
  141. position:{
  142. latitude: e.latitude,
  143. longitude: e.longitude
  144. }
  145. }))
  146. that.init();
  147. },
  148. fail:function(e){
  149. console.log(e);
  150. }
  151. })
  152. }
  153. // uni.onLocationChange(function(e){
  154. // console.log(11111,e);
  155. // that.position.latitude = e.latitude;
  156. // that.position.longitude = e.longitude;
  157. // })
  158. },
  159. methods:{
  160. init(){
  161. this.animateLocation = true;
  162. handleRegionchangeTimmer = setTimeout(()=>{
  163. this.renderList();
  164. },400)
  165. },
  166. renderList(){
  167. let that = this;
  168. let mapContext = uni.createMapContext("map_20221212", that);
  169. mapContext.getCenterLocation({
  170. success:function(rst){
  171. let options = {
  172. location:rst.latitude+','+rst.longitude,
  173. get_poi:1,
  174. poi_options: 'radius=1000',
  175. sig:that.sig,
  176. success:function(res){
  177. //console.log('xxxxx',res.result.pois)
  178. let isSelect = false;
  179. if(res.message == "query ok"){
  180. //that.$data.searchlist = [];
  181. that.$data.searchlist = [...res.result.pois];
  182. for(let i = 0;i<that.$data.searchlist.length;i++){
  183. that.$data.searchlist.useable = false;
  184. that.$data.searchlist.select = false;
  185. for(let j = 0;j < that.polygons.length;j++){
  186. if(that.isPointInPolygon(that.$data.searchlist[i].location.lat,that.$data.searchlist[i].location.lng,that.polygons[j].points)){
  187. that.$data.searchlist[i].useable = true;
  188. if(!isSelect){
  189. that.$data.searchlist[i].select = true;
  190. isSelect = true;
  191. }
  192. }
  193. }
  194. }
  195. setTimeout(()=>{
  196. that.animateLocation = false;
  197. },200)
  198. }
  199. if(isSelect){
  200. that.canConfirm = true;
  201. }else{
  202. that.canConfirm = false;
  203. }
  204. },
  205. fail:function(error){
  206. console.log(error);
  207. }
  208. };
  209. if(typeof(window)==='undefined'){
  210. delete options.sig;
  211. }
  212. if(options.sig===''){
  213. delete options.sig;
  214. }
  215. qqmapsdk.reverseGeocoder(options);
  216. }
  217. });
  218. },
  219. searchPosition(){
  220. if(this.searchKey.replace(/\s+/,'')==''){
  221. return;
  222. }
  223. let that = this;
  224. let searchOptions = {
  225. keyword:this.searchKey,
  226. location:{
  227. latitude:this.position.latitude,
  228. longitude:this.position.longitude
  229. },
  230. sig:that.sig,
  231. success:function(rst){
  232. let isSelect = false;
  233. if(rst.message == "query ok"){
  234. //that.$data.searchlist = [];
  235. that.$data.searchlist = [...rst.data];
  236. for(let i = 0;i<that.$data.searchlist.length;i++){
  237. that.$data.searchlist.useable = false;
  238. that.$data.searchlist.select = false;
  239. for(let j = 0;j < that.polygons.length;j++){
  240. if(that.isPointInPolygon(that.$data.searchlist[i].location.lat,that.$data.searchlist[i].location.lng,that.polygons[j].points)){
  241. that.$data.searchlist[i].useable = true;
  242. if(!isSelect){
  243. that.$data.searchlist[i].select = true;
  244. that.position.latitude = that.$data.searchlist[i].location.lat;
  245. that.position.longitude = that.$data.searchlist[i].location.lng;
  246. isSelect = true;
  247. }
  248. }
  249. }
  250. }
  251. }
  252. if(isSelect){
  253. that.canConfirm = true;
  254. }else{
  255. that.canConfirm = false;
  256. }
  257. }
  258. };
  259. if(typeof(window)==='undefined'){
  260. delete searchOptions.sig;
  261. }
  262. if(searchOptions.sig===''){
  263. delete searchOptions.sig;
  264. }
  265. qqmapsdk.search(searchOptions);
  266. },
  267. changeSelectItem(index){
  268. if(this.$data.searchlist[index].useable){
  269. for(let i = 0;i<this.$data.searchlist.length;i++){
  270. let temp = {};
  271. for(var key in this.$data.searchlist[i]){
  272. temp[key] = this.$data.searchlist[i][key];
  273. }
  274. if(i===index){
  275. temp.select = true;
  276. this.isChangeSelectItem = true;
  277. this.position.latitude = temp.location.lat;
  278. this.position.longitude = temp.location.lng;
  279. }else{
  280. temp.select = false;
  281. }
  282. this.$set(this.$data.searchlist,i,temp);
  283. }
  284. }
  285. },
  286. handleRegionchange(e){
  287. console.log(e);
  288. this.animateLocation = false;
  289. if(e.type=='end'){
  290. if(this.isChangeSelectItem){
  291. setTimeout(()=>{
  292. this.isChangeSelectItem = false;
  293. },100)
  294. return;
  295. }else{
  296. this.animateLocation = true;
  297. if(handleRegionchangeTimmer){
  298. clearTimeout(handleRegionchangeTimmer);
  299. }
  300. handleRegionchangeTimmer = setTimeout(()=>{
  301. this.renderList();
  302. },400)
  303. }
  304. }
  305. },
  306. isPointInPolygon(aLat, aLon, pointList){
  307. /*
  308. :param aLon: double 经度
  309. :param aLat: double 纬度
  310. :param pointList: list [{latitude: 22.22, longitude: 113.113}...] 多边形点的顺序需根据顺时针或逆时针,不能乱
  311. */
  312. var iSum = 0
  313. var iCount = pointList.length
  314. if(iCount < 3) {
  315. return false
  316. }
  317. // 待判断的点(x, y) 为已知值
  318. var y = aLat
  319. var x = aLon
  320. for(var i = 0; i < iCount; i++) {
  321. var y1 = pointList[i].latitude
  322. var x1 = pointList[i].longitude
  323. if(i == iCount - 1) {
  324. var y2 = pointList[0].latitude
  325. var x2 = pointList[0].longitude
  326. } else {
  327. var y2 = pointList[i + 1].latitude
  328. var x2 = pointList[i + 1].longitude
  329. }
  330. // 当前边的 2 个端点分别为 已知值(x1, y1), (x2, y2)
  331. if (((y >= y1) && (y < y2)) || ((y >= y2) && (y < y1))) {
  332. // y 界于 y1 和 y2 之间
  333. // 假设过待判断点(x, y)的水平直线和当前边的交点为(x_intersect, y_intersect),有y_intersect = y
  334. // 则有(2个相似三角形,公用顶角,宽/宽 = 高/高):|x1 - x2| / |x1 - x_intersect| = |y1 - y2| / |y1 - y|
  335. if (Math.abs(y1 - y2) > 0) {
  336. var x_intersect = x1 - ((x1 - x2) * (y1 - y)) / (y1 - y2);
  337. if(x_intersect < x) {
  338. iSum += 1
  339. }
  340. }
  341. }
  342. }
  343. if(iSum % 2 != 0) { //true就是在
  344. return true
  345. }else { //false就是不在
  346. return false
  347. }
  348. },
  349. confirmSelect(){
  350. if(this.canConfirm){
  351. this.searchlist.forEach(val=>{
  352. if(val.select){
  353. val.polygonIndex = [];
  354. this.polygons.forEach((polygon,index)=>{
  355. if(this.isPointInPolygon(val.location.lat,val.location.lng,polygon.points)){
  356. val.polygonIndex.push(index);
  357. }
  358. })
  359. uni.setStorageSync('polygonLocationPicker',JSON.stringify(val));
  360. this.$emit('selected',JSON.stringify(val));
  361. }
  362. })
  363. }
  364. }
  365. }
  366. }
  367. </script>
  368. <style scoped lang="scss">
  369. @keyframes bounceInDown {
  370. from,
  371. 20%,
  372. 53%,
  373. to {
  374. animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  375. transform: translate3d(0, 0, 0);
  376. }
  377. 40%,
  378. 43% {
  379. animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
  380. transform: translate3d(0, -30rpx, 0) scaleY(1.1);
  381. }
  382. 70% {
  383. animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06);
  384. transform: translate3d(0, -15rpx, 0) scaleY(1.05);
  385. }
  386. 80% {
  387. transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1);
  388. transform: translate3d(0, 0, 0) scaleY(0.95);
  389. }
  390. 90% {
  391. transform: translate3d(0, -4rpx, 0) scaleY(1.02);
  392. }
  393. }
  394. .box-picker_position{
  395. height: 100%;
  396. width: 100%;
  397. background: #fff;
  398. display: flex;
  399. flex-flow: column;
  400. position: relative;
  401. overflow: hidden;
  402. box-sizing: border-box;
  403. }
  404. .map_wp{
  405. position: relative;
  406. width: 750rpx;
  407. height: 400rpx;
  408. }
  409. .picker_map_location{
  410. width: 60rpx;
  411. height: 60rpx;
  412. background-image: url("data:image/svg+xml,%3Csvg t='1670988557938' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3498' width='200' height='200'%3E%3Cpath d='M511.968 0c-207.84 0-376.96 169.12-376.96 376.992 0 54.208 11.104 105.984 32.96 153.888 94.24 206.24 274.976 424 328.128 485.824 3.968 4.608 9.792 7.296 15.904 7.296s11.904-2.656 15.904-7.296c53.12-61.824 233.856-279.552 328.128-485.824 21.888-47.904 32.96-99.648 32.96-153.888-0.032-207.872-169.152-376.992-376.992-376.992zM511.968 572.8c-107.968 0-195.808-87.84-195.808-195.808s87.84-195.84 195.808-195.84 195.808 87.84 195.808 195.84c0 107.968-87.84 195.808-195.808 195.808z' fill='%23fa3c23' p-id='3499'%3E%3C/path%3E%3C/svg%3E");
  413. background-size: cover;
  414. background-position: center bottom;
  415. position: absolute;
  416. left: 50%;
  417. top: 50%;
  418. margin-left: -30rpx;
  419. margin-top: -60rpx;
  420. z-index: 999;
  421. transform-origin: center bottom;
  422. }
  423. .picker_map_location.animated{
  424. animation: bounceInDown .6s linear infinite;
  425. }
  426. .picker-map{
  427. width: 750rpx;
  428. height: 400rpx;
  429. }
  430. .list-picker_position{
  431. position: relative;
  432. flex: 1;
  433. display: flex;
  434. flex-flow: column;
  435. overflow: hidden;
  436. .hd{
  437. padding: 20rpx 20rpx 10rpx;
  438. display: flex;
  439. .input_text{
  440. flex: 1;
  441. box-sizing: border-box;
  442. background: #f2f2f2;
  443. border-radius: 6rpx;
  444. height: 60rpx;
  445. font-size: 28rpx;
  446. padding-left: 1em;
  447. }
  448. }
  449. .bd{
  450. padding: 20rpx;
  451. box-sizing: border-box;
  452. flex: 1;
  453. overflow-y: scroll;
  454. .item{
  455. display: flex;
  456. padding: 15rpx 10rpx;
  457. border-bottom: 1px solid #ccc;
  458. line-height: 1.92;
  459. align-items: stretch;
  460. .title{
  461. font-size: 32rpx;
  462. }
  463. .address{
  464. font-size: 24rpx;
  465. color: #999;
  466. line-height: 1.5;
  467. }
  468. }
  469. .item-l{
  470. padding: 10rpx 6rpx 0 0;
  471. }
  472. .item-c{
  473. flex: 1;
  474. }
  475. .item-r{
  476. width: 60rpx;
  477. display: flex;
  478. align-items: center;
  479. align-content: center;
  480. }
  481. .item.disabled{
  482. opacity: .5;
  483. }
  484. }
  485. }
  486. .btn-search{
  487. background: #007AFF;
  488. font-size: 24rpx;
  489. color: #fff;
  490. display: flex;
  491. padding: 0;
  492. align-items: center;
  493. justify-content: center;
  494. width: 120rpx;
  495. }
  496. .picker-search{
  497. background-image: url("data:image/svg+xml,%3Csvg t='1670900132396' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='2679' width='200' height='200'%3E%3Cpath d='M685.6 660.336l155.152 155.168a16 16 0 0 1 0 22.624l-11.312 11.328a16 16 0 0 1-22.624 0l-158.528-158.544a289.792 289.792 0 0 1-165.152 51.36C322.336 742.256 192 611.904 192 451.12 192 290.336 322.336 160 483.136 160c160.784 0 291.12 130.336 291.12 291.136 0 82.112-33.984 156.272-88.672 209.2z m-202.464 33.92c134.272 0 243.12-108.848 243.12-243.12C726.256 316.848 617.408 208 483.136 208 348.848 208 240 316.848 240 451.136c0 134.272 108.848 243.12 243.136 243.12z' p-id='2680' fill='%23dbdbdb'%3E%3C/path%3E%3C/svg%3E"); background-size: cover;
  498. background-size: cover;
  499. width:36rpx;
  500. height: 36rpx;
  501. width: 36rpx;
  502. display: block;
  503. margin-right: 5rpx;
  504. }
  505. .picker-checked{
  506. background-image: url("data:image/svg+xml,%3Csvg t='1670909673260' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3763' width='200' height='200'%3E%3Cpath d='M417.185185 768c-9.481481 0-18.962963-3.792593-26.548148-11.377778l-246.518518-246.518518c-15.17037-15.17037-15.17037-37.925926 0-53.096297 15.17037-15.17037 37.925926-15.17037 53.096296 0L417.185185 676.977778l409.6-409.6c15.17037-15.17037 37.925926-15.17037 53.096296 0 15.17037 15.17037 15.17037 37.925926 0 53.096296l-436.148148 436.148148c-7.585185 7.585185-17.066667 11.377778-26.548148 11.377778z' p-id='3764' fill='%231e63ed'%3E%3C/path%3E%3C/svg%3E");
  507. background-size: cover;
  508. width: 46rpx;
  509. height: 46rpx;
  510. display: block;
  511. }
  512. .picker-address{
  513. background-image: url("data:image/svg+xml,%3Csvg t='1670910118471' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4833' width='200' height='200'%3E%3Cpath d='M508.313 1018.666c0 0-379.51-422.921-379.51-632.516 0-209.606 169.914-379.51 379.51-379.51s379.511 169.903 379.51 379.51c0 209.596-379.51 632.516-379.51 632.516zM508.313 55.295c-182.719 0-330.854 150.305-330.854 335.72s330.854 559.534 330.854 559.534 330.854-374.117 330.854-559.534c0-185.415-148.135-335.72-330.854-335.72zM508.312 512.654c-87.336 0-158.129-70.793-158.129-158.129s70.793-158.129 158.129-158.129 158.129 70.793 158.129 158.129c0 87.328-70.793 158.129-158.129 158.129zM508.313 240.185c-64.488 0-116.772 52.285-116.772 116.772s52.285 116.772 116.772 116.772c64.498 0 116.772-52.285 116.772-116.772 0-64.488-52.275-116.772-116.772-116.772z' fill='%23666666' p-id='4834'%3E%3C/path%3E%3C/svg%3E");
  514. background-size: cover;
  515. width: 36rpx;
  516. height: 36rpx;
  517. display: block;
  518. }
  519. .btn-selected{
  520. background: #007AFF;
  521. height: 80rpx;
  522. line-height: 80rpx;
  523. font-size: 32rpx;
  524. color: #fff;
  525. border: none;
  526. border-radius: 0;
  527. }
  528. .btn-selected.disabled{
  529. background: #f2f2f2;
  530. color: #ccc;
  531. }
  532. </style>