jufubao-mall 2.0.97 → 2.0.98-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jufubao-mall",
3
- "version": "2.0.97",
3
+ "version": "2.0.98-beta2",
4
4
  "private": false,
5
5
  "description": "聚福宝商城业务组件插件包",
6
6
  "main": "index.js",
@@ -107,6 +107,7 @@
107
107
  import { baseIsEmpty, } from "@/utils/xd.base";
108
108
  import CusShops from "@/components/CusShops/CusShops.vue";
109
109
  import XdMore from "@/components/XdMore/XdMore.vue";
110
+ import getCoordinatesMixins from "@/mixins/getCoordinatesMixins";
110
111
  let $vm = null;
111
112
 
112
113
  export default {
@@ -118,7 +119,7 @@
118
119
  XdQueryFilter,
119
120
  XdMore
120
121
  },
121
- mixins: [componentsMixins,extsMixins,JfbMallNewShopListMixin],
122
+ mixins: [componentsMixins, extsMixins, JfbMallNewShopListMixin, getCoordinatesMixins],
122
123
  data() {
123
124
  return {
124
125
  //系统变量
@@ -191,6 +192,10 @@
191
192
  hdSelfPath: "",//到店自取
192
193
  travelfPath:'',//旅游购票
193
194
 
195
+ //设置当前定位
196
+ onLoadDone:false, //onload加载完成
197
+ curLocaltionDone: false, //当前定位完成
198
+ curLocaltioInfo: null, //当前定位信息
194
199
  }
195
200
  },
196
201
  watch: {
@@ -210,6 +215,9 @@
210
215
  async showQuery(val){
211
216
  if(val === 'Y') this.searchFilterList = await this.getSearchFilterList();
212
217
  },
218
+ watchCurLocaltion(val){
219
+ if(val === true) this.getList();
220
+ }
213
221
  },
214
222
  filters:{
215
223
  filterLabels(val){
@@ -229,6 +237,13 @@
229
237
  stateCity: state => state.cityLocation.city,
230
238
  stateLocation: state => state.cityLocation.location || {}
231
239
  }),
240
+ //当前定位是否完成
241
+ watchCurLocaltion(){
242
+ return this.onLoadDone === true
243
+ && this.curLocaltionDone === true
244
+ && this.curLocaltioInfo !== null;
245
+ },
246
+
232
247
  fixTopComp(){
233
248
  return {
234
249
  top: this.layoutInfo.top + 'rpx'
@@ -324,6 +339,26 @@
324
339
  this.isFixed = !this.isPreview
325
340
  this.init(this.container);
326
341
  $vm= this;
342
+
343
+ //进入页面获取定位信息
344
+ //预览模式
345
+ if(this.$configProject['isPreview']) {
346
+ this.curLocaltionDone = true;
347
+ this.curLocaltioInfo = {};
348
+ }
349
+ //非预览模型
350
+ else {
351
+ this.getCoordinates()
352
+ .then(res=>{
353
+ this.curLocaltioInfo = res;
354
+ this.curLocaltionDone = true;
355
+ })
356
+ .catch(err=>{
357
+ this.curLocaltioInfo = this.stateLocation;
358
+ this.curLocaltionDone = true;
359
+ })
360
+ }
361
+
327
362
  },
328
363
  methods: {
329
364
  /**
@@ -482,9 +517,9 @@
482
517
  getSFP('brand_ids',this.searchFilterParams['brand']);
483
518
  getSFP('area_codes',this.searchFilterParams['area']);
484
519
  if(this.stateCity) getSFP('city_code',this.stateCity.city_code);
485
- if(this.stateLocation && (this.stateLocation.longitude && this.stateLocation.latitude)){
486
- params['longitude'] = this.stateLocation.longitude;
487
- params['latitude'] = this.stateLocation.latitude;
520
+ if(this.curLocaltioInfo && (this.curLocaltioInfo.longitude && this.curLocaltioInfo.latitude)){
521
+ params['longitude'] = this.curLocaltioInfo.longitude;
522
+ params['latitude'] = this.curLocaltioInfo.latitude;
488
523
  }
489
524
  return params
490
525
  },
@@ -663,7 +698,7 @@
663
698
  reject(err);
664
699
  })
665
700
  })
666
-
701
+
667
702
  },
668
703
 
669
704
  handleTabs(type){
@@ -776,7 +811,8 @@
776
811
  if(consumeMode && consumeMode.options){
777
812
  this.consumeModeOfPanelAndUrl = this.getIntersection(consumeMode.options, this.consumeModeOfPanelAndUrl);
778
813
  }
779
- this.getList();
814
+
815
+ this.onLoadDone = true;
780
816
  },
781
817
  onJfbReachBottom(options) {
782
818
  if(this.loading) return;
@@ -130,6 +130,7 @@
130
130
  import XdNoData from "@/components/XdNoData/XdNoData.vue"
131
131
  import CusShops from "@/components/CusShops/CusShops.vue"
132
132
  import XdMore from "@/components/XdMore/XdMore.vue";
133
+ import getCoordinatesMixins from "@/mixins/getCoordinatesMixins";
133
134
  let $vm = null;
134
135
  export default {
135
136
  // #ifdef MP-WEIXIN
@@ -143,9 +144,7 @@
143
144
  CusShops,
144
145
  XdMore,
145
146
  },
146
- mixins: [
147
- componentsMixins, extsMixins, JfbMallResourceShopListMixin
148
- ],
147
+ mixins: [componentsMixins, extsMixins, JfbMallResourceShopListMixin,getCoordinatesMixins],
149
148
  data() {
150
149
  return {
151
150
  closeMask:true,
@@ -155,7 +154,7 @@
155
154
  cityList: [],
156
155
  consumeModeList:[],
157
156
  brandGroupNum: 8,
158
-
157
+
159
158
  showQueryModal: false,
160
159
  curQueryTab: "",
161
160
  showQueryFilter: false,
@@ -245,6 +244,11 @@
245
244
  popId:null,
246
245
  footerColor:'#ddd',
247
246
  searchFilterList:[],
247
+
248
+ //设置当前定位
249
+ onLoadDone:false, //onload加载完成
250
+ curLocaltionDone: false, //当前定位完成
251
+ curLocaltioInfo: null, //当前定位信息
248
252
  }
249
253
  },
250
254
  filters:{
@@ -264,6 +268,14 @@
264
268
  stateCity: state => state.cityLocation.city,
265
269
  stateLocation: state => state.cityLocation.location || {},
266
270
  }),
271
+
272
+ //当前定位是否完成
273
+ watchCurLocaltion(){
274
+ return this.onLoadDone === true
275
+ && this.curLocaltionDone === true
276
+ && this.curLocaltioInfo !== null;
277
+ },
278
+
267
279
  //把brandList每8个分为一组
268
280
  groupBrandList(){
269
281
  let arr = [];
@@ -321,7 +333,7 @@
321
333
  console.log(this.filterMode,this.filterMode==['filter'],this.filterMode.includes('filter'),['filter'].includes('filter'),'ffffffff')
322
334
  if(this.filterMode.includes('filter')){
323
335
  tabs.push({value: 'filter', label: '筛选', icon: 'iconshaixuanbiaoji_mian',size: 24});
324
- }
336
+ }
325
337
  return tabs
326
338
  },
327
339
  bodyStyle(){
@@ -360,6 +372,9 @@
360
372
  this.onJfbLoad(this.options);
361
373
  }
362
374
  },
375
+ watchCurLocaltion(val){
376
+ if(val === true) this.getList();
377
+ },
363
378
  menuGreater:{
364
379
  handler(n,o){
365
380
  if(n&&n!==o){
@@ -369,13 +384,13 @@
369
384
  top: this.menuWrapTop/this.$rpxNum+this.bodyPadding.top+'rpx',
370
385
  position: 'absolute',
371
386
  // boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
372
- transition: 'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),box-shadow 0.3s ease',
387
+ transition: 'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),box-shadow 0.3s ease',
373
388
  }
374
389
  }else{
375
390
  this.menuWrapStyle = {
376
391
  top: this.layoutInfo.top +'rpx',
377
392
  position: 'fixed',
378
- transition: 'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),box-shadow 0.3s ease',
393
+ transition: 'transform 0.3s cubic-bezier(0.25, 0.1, 0.25, 1),box-shadow 0.3s ease',
379
394
  }
380
395
  }
381
396
  }
@@ -421,10 +436,10 @@
421
436
  showQueryModal:{
422
437
  handler(n,o){
423
438
  if(n){
424
- this.setHasPopStatus({popId: this.popId, action: 'add'});
439
+ this.setHasPopStatus({popId: this.popId, action: 'add'});
425
440
  }else{
426
441
  this.setHasPopStatus({popId: this.popId, action: 'del'});
427
- }
442
+ }
428
443
  },
429
444
  immediate:true,
430
445
  deep:true,
@@ -449,12 +464,25 @@
449
464
  this.popId = this.$xdUniHelper.randomChar(20);
450
465
  this.init(this.container);
451
466
  $vm= this;
452
- //todo
453
- },
454
- mounted(){
455
- console.log(this.layoutInfo,'this.layoutInfo')
456
- // window.addEventListener('scroll', this.watchScroll);
457
-
467
+
468
+ //进入页面获取定位信息
469
+ //预览模式
470
+ if(this.$configProject['isPreview']) {
471
+ this.curLocaltionDone = true;
472
+ this.curLocaltioInfo = {};
473
+ }
474
+ //非预览模型
475
+ else {
476
+ this.getCoordinates()
477
+ .then(res=>{
478
+ this.curLocaltioInfo = res;
479
+ this.curLocaltionDone = true;
480
+ })
481
+ .catch(err=>{
482
+ this.curLocaltioInfo = this.stateLocation;
483
+ this.curLocaltionDone = true;
484
+ })
485
+ }
458
486
  },
459
487
  methods: {
460
488
  ...mapActions(['getCustomList','setHasPopStatus']),
@@ -509,13 +537,14 @@
509
537
  delete options['shop_category_ids'];
510
538
  }
511
539
  this.options = this.$xdUniHelper.cloneDeep(options);
512
- console.log('event.onJfbLoad', this.stateCity, this.stateLocation)
540
+
513
541
  await Promise.all([
514
542
  this.p_getSearchFilter(),
515
543
  this.p_getCityList()
516
544
  ])
545
+
517
546
  this.$nextTick(() => {
518
- this.getList();
547
+ this.onLoadDone = true;
519
548
  })
520
549
  },
521
550
  /**
@@ -575,16 +604,16 @@
575
604
  //描边
576
605
  this.cardItemBorder = gCPVal(container, 'cardItemBorder', [{color: '#f8f8f8',width: 2,style: 'solid'}],{isBorder: true});
577
606
  // this.cardItemRadius = getContainerPropsValue(container, 'content.cardItemRadius', 16);
578
-
607
+
579
608
  this.shopInfoPath = gCPVal(container, 'shopInfoPath', {value: ""}).value;
580
609
  this.scanPath = gCPVal(container, 'scanPath', {value: ""}).value;
581
610
  this.exchangePath = gCPVal(container, 'exchangePath', {value: ""}).value;
582
611
  this.takeGoodsPath = gCPVal(container, 'takeGoodsPath', {value: ""}).value;
583
612
  this.buyTicketPath = gCPVal(container, 'buyTicketPath', {value: ""}).value;
584
613
  this.codePath = gCPVal(container, 'codePath', {value: ""}).value;
585
-
614
+
586
615
  this.isAutoJump = gCPVal(container, 'isAutoJump', []);
587
-
616
+
588
617
  //图片 描边
589
618
  this.cardImageBorder = gCPVal(container, 'cardImageBorder', [{color: '#f8f8f8',width: '2',style: 'solid'}],{isBorder: true});
590
619
  console.log(this.cardImageBorder,'cardImageBorder')
@@ -651,7 +680,7 @@
651
680
  //内容区边距
652
681
  this.bodyPadding = gCPVal(container,'bodyPadding', [0], {sKey:'bodyPaddingStatus',fields:['bodyPadding'],isPMR: true,});
653
682
  console.log(this.bodyPadding,'this.bodyPadding')
654
-
683
+
655
684
  this.listPadding = gCPVal(container,'listPadding', [((shopStyle===sValue)?{ bottom:0,left:16, right:16,top:16}:{top:2,left:0,right:0,bottom:0}),20], {sKey:'listPaddingStatus',fields:['listPadding'],cusFields: ['shopStyle','margin',shopStyle],isPMR: true,});
656
685
  console.log(this.listPadding,'listPadding')
657
686
  // this.cardItemSpace = getContainerPropsValue(container, 'content.cardItemSpace', 20);
@@ -785,7 +814,6 @@
785
814
  },
786
815
  getList(){
787
816
  let data = this.getParams();
788
- console.log(data,this.namespace,'dddddd')
789
817
  data = {
790
818
  ...data,
791
819
  page_token: this.page_token,
@@ -829,13 +857,6 @@
829
857
  }
830
858
  this.page_token = res.next_page_token;
831
859
  this.hasNextPage = res.next_page_token !== "";
832
-
833
- // if(this.page_token == 1){
834
- // uni.pageScrollTo({
835
- // scrollTop: 0,
836
- // duration: 0
837
- // })
838
- // }
839
860
  }).catch(err=>{
840
861
  this.loading = false;
841
862
  })
@@ -866,11 +887,11 @@
866
887
  getSFP('area_codes',this.searchFilterParams['area']);
867
888
  getSFP('namespaces',this.searchFilterParams['business_line']);
868
889
  if(this.stateCity) getSFP('city_code',this.stateCity.city_code);
869
- if(this.stateLocation && (this.stateLocation.longitude && this.stateLocation.latitude)){
870
- params['longitude'] = this.stateLocation.longitude;
871
- params['latitude'] = this.stateLocation.latitude;
890
+ if(this.curLocaltioInfo && (this.curLocaltioInfo.longitude && this.curLocaltioInfo.latitude)){
891
+ params['longitude'] = this.curLocaltioInfo.longitude;
892
+ params['latitude'] = this.curLocaltioInfo.latitude;
872
893
  }
873
- console.log(params,'params' )
894
+
874
895
  return params
875
896
  },
876
897
  getFilter(cb, options){
@@ -1067,7 +1088,7 @@
1067
1088
  this.consume_mode = consume_mode;
1068
1089
  // }
1069
1090
  this.curQueryTab = '';
1070
-
1091
+
1071
1092
  this.getList();
1072
1093
  },
1073
1094
  onJfbScroll(options) {
@@ -1288,7 +1309,7 @@
1288
1309
  z-index: 999;
1289
1310
  position: fixed;
1290
1311
  width: 100%;
1291
-
1312
+
1292
1313
  .menu_list{
1293
1314
  display: flex;
1294
1315
  box-sizing: border-box;
@@ -1342,7 +1363,7 @@
1342
1363
  overflow: hidden;
1343
1364
  text-overflow: ellipsis;
1344
1365
  font-size: 24rpx;
1345
-
1366
+
1346
1367
  &.active{
1347
1368
  background-color: #DEEDFF;
1348
1369
  border: 1px solid #1989FA;
@@ -1361,7 +1382,7 @@
1361
1382
  margin: 10rpx;
1362
1383
  width: 160rpx;
1363
1384
  &.active{
1364
-
1385
+
1365
1386
  .brand_name{
1366
1387
  color: var(--main-color);
1367
1388
  }
@@ -169,6 +169,7 @@
169
169
  import {
170
170
  baseIsEmpty,
171
171
  } from "@/utils/xd.base";
172
+ import getCoordinatesMixins from "@/mixins/getCoordinatesMixins";
172
173
 
173
174
  export default {
174
175
  name: "JfbMallShopList",
@@ -177,7 +178,7 @@
177
178
  XdButton,
178
179
  XdQueryFilter,
179
180
  },
180
- mixins: [componentsMixins,extsMixins,JfbMallShopListMixin],
181
+ mixins: [componentsMixins, extsMixins, JfbMallShopListMixin,getCoordinatesMixins],
181
182
  data() {
182
183
  return {
183
184
 
@@ -237,6 +238,12 @@
237
238
  delivery:'',
238
239
  scrollTop: 0,
239
240
  scrollHeight: 0,
241
+
242
+
243
+ //设置当前定位
244
+ onLoadDone:false, //onload加载完成
245
+ curLocaltionDone: false, //当前定位完成
246
+ curLocaltioInfo: null, //当前定位信息
240
247
  }
241
248
  },
242
249
  watch: {
@@ -254,6 +261,10 @@
254
261
  }
255
262
  },
256
263
 
264
+ watchCurLocaltion(val){
265
+ if(val === true) this.getList();
266
+ }
267
+
257
268
  },
258
269
  computed: {
259
270
  ...mapState({
@@ -263,6 +274,13 @@
263
274
  }),
264
275
  ...styleForm.getComputedItem(),
265
276
 
277
+ //当前定位是否完成
278
+ watchCurLocaltion(){
279
+ return this.onLoadDone === true
280
+ && this.curLocaltionDone === true
281
+ && this.curLocaltioInfo !== null;
282
+ },
283
+
266
284
  outPadding(){
267
285
  let str = `${this.checkValue(this.padding.top, 0)}rpx`;
268
286
  str = `${str} ${this.checkValue(this.padding.right, 0)}rpx`;
@@ -300,11 +318,31 @@
300
318
  created() {
301
319
  this.isPreview = this.$configProject.isPreview;
302
320
  this.init(this.container);
321
+
322
+ //进入页面获取定位信息
323
+ //预览模式
324
+ if(this.$configProject['isPreview']) {
325
+ this.curLocaltionDone = true;
326
+ this.curLocaltioInfo = {};
327
+ }
328
+ //非预览模型
329
+ else {
330
+ this.getCoordinates()
331
+ .then(res=>{
332
+ this.curLocaltioInfo = res;
333
+ this.curLocaltionDone = true;
334
+ })
335
+ .catch(err=>{
336
+ this.curLocaltioInfo = this.stateLocation;
337
+ this.curLocaltionDone = true;
338
+ })
339
+ }
303
340
  },
304
341
  methods: {
305
342
  handleError(item){
306
343
  item.errorStatus = true;
307
344
  },
345
+
308
346
  toNavigationApp(item){
309
347
  if(item.latitude && item.longitude) {
310
348
  let address = ``
@@ -330,7 +368,6 @@
330
368
  },
331
369
 
332
370
  onJfbLoad(options) {
333
- console.log(options,'ppppp');
334
371
  //设置业务请求代码
335
372
  this.setNameSpace(options);
336
373
  if(!baseIsEmpty(options)){
@@ -338,11 +375,9 @@
338
375
  }
339
376
  this.region_limit_tpl_id = options.region_limit_tpl_id || "";
340
377
  this.brand_id = options.brand_id || options.id;
341
- // this.brand_ids = options.brand_id;
342
- setTimeout(()=>{
343
- this.getList();
344
- },100)
345
378
  this.getSearchFilterList();
379
+
380
+ this.onLoadDone = true;
346
381
  },
347
382
  /**
348
383
  * @description 监听事件变化
@@ -448,25 +483,31 @@
448
483
  })
449
484
  },
450
485
  getList(){
451
- console.log(this.brand_id,'brand_id')
486
+
452
487
  this.loading = true;
453
488
  this.$xdShowLoading({});
489
+
490
+ let params = {
491
+ consume_mode: this.consume_mode,
492
+ brand_type: this.brand_type,
493
+ ...this.options,
494
+ brand_id: this.brand_id,
495
+ city_code: this.stateCity.city_code,
496
+ keyword: this.keyword,
497
+ page_size: this.page_size,
498
+ page_token: this.page_token,
499
+ region_limit_tpl_id: this.region_limit_tpl_id,
500
+ namespace: this.xnamespace,
501
+ }
502
+
503
+ if(this.curLocaltioInfo && this.curLocaltioInfo.latitude && this.curLocaltioInfo.longitude) {
504
+ params['latitude'] = this.curLocaltioInfo.latitude;
505
+ params['longitude'] = this.curLocaltioInfo.longitude;
506
+ }
507
+
454
508
  jfbRootExec("getShopList", {
455
509
  vm: this,
456
- data: {
457
- consume_mode: this.consume_mode,
458
- brand_type: this.brand_type,
459
- ...this.options,
460
- brand_id: this.brand_id,
461
- city_code: this.stateCity.city_code,
462
- latitude: this.stateLocation.latitude,
463
- longitude: this.stateLocation.longitude,
464
- keyword: this.keyword,
465
- page_size: this.page_size,
466
- page_token: this.page_token,
467
- region_limit_tpl_id: this.region_limit_tpl_id,
468
- namespace: this.xnamespace,
469
- }
510
+ data: params
470
511
  }).then(res => {
471
512
  this.loaded = true;
472
513
  this.loading = false;
@@ -478,7 +519,7 @@
478
519
  errorStatus: false,
479
520
  }
480
521
  });
481
- if(this.page_token == 1){
522
+ if(Number(this.page_token) === 1){
482
523
  this.shopList = list;
483
524
  }else{
484
525
  this.shopList = this.shopList.concat(list);
@@ -673,7 +673,7 @@
673
673
 
674
674
  getParams(){
675
675
  let latitude = '', longitude = '';
676
- console.log('handleRegionChange',this.stateLocation, this.changeLocation);
676
+
677
677
  if(this.stateLocation && !this.$xdUniHelper.isEmpty(this.stateLocation)) {
678
678
  if(this.stateLocation.longitude) longitude = this.stateLocation.longitude + '';
679
679
  if(this.stateLocation.latitude) latitude = this.stateLocation.latitude + '';
@@ -0,0 +1,442 @@
1
+ 'use strict';
2
+ //#ifdef H5
3
+ import {mapActions} from "vuex";
4
+ //#endif
5
+ import {isWechat} from "@/utils/xd.base";
6
+ import storage from "@/common/storage";
7
+
8
+ export default {
9
+ data(){
10
+ return {
11
+ lastSuccessCoords:null,
12
+
13
+ //#ifdef H5
14
+ jwxCoordinatesSDK:null,
15
+ //#endif
16
+
17
+ // #ifdef MP-WEIXIN
18
+ mpLocationFnCoords:'getFuzzyLocation',
19
+ // #endif
20
+
21
+ //保存上一次定位信息(正式缓冲6分钟,测试机环境0.5小时)
22
+ locationStorageKeyCoords: "xd_three_map",
23
+ //永久保存上一次定位信息
24
+ locationLongStorageKeyCoords: 'xd_three_map_long',
25
+ }
26
+ },
27
+ created() {
28
+ // #ifdef MP-WEIXIN
29
+ //判断小程序是否使用精准定位
30
+ if(this.$configProject['privacy_interface_list']) {
31
+ this.$configProject['privacy_interface_list'].map(item=>{
32
+ if(item['api_name'] === 'wx.getLocation') {
33
+ this.mpLocationFnCoords = 'getLocation';
34
+ }
35
+ })
36
+ }
37
+ // #endif
38
+ },
39
+ methods:{
40
+ /**
41
+ * @description 获取位置信息
42
+ * @return {Promise<unknown>}
43
+ */
44
+ getCoordinates() {
45
+ return new Promise((resolve, reject) => {
46
+ //#ifdef H5
47
+ if (isWechat()) {
48
+ this.getH5WXLocation().then(res => {
49
+ res = {...res, pf: 'H5-WX'}
50
+ resolve(res);
51
+ }).catch(err => {
52
+ //微信定位失败,降级到百度定位/原生定位
53
+ this.getH5FallbackLocation(err, resolve, reject);
54
+ });
55
+ return;
56
+ }
57
+
58
+ //百度定位
59
+ if(this.$settings.baiduAk) {
60
+ this.getBaiduLocation().then(res => {
61
+ res = {...res, pf: 'H5-BD'}
62
+ resolve(res);
63
+ }).catch(err => {
64
+ reject(err);
65
+ });
66
+ return;
67
+ }
68
+
69
+ //H5原生定位兜底
70
+ if(window.navigator.geolocation) {
71
+ this.getH5NativeLocation().then(res => {
72
+ res = {...res, pf: 'H5-PC'}
73
+ resolve(res);
74
+ }).catch(err => {
75
+ reject(err);
76
+ });
77
+ return;
78
+ }
79
+ //所有定位方式均不可用,直接拒绝避免 Promise 悬挂
80
+ reject('当前环境不支持定位');
81
+ // #endif
82
+
83
+ // #ifdef MP-WEIXIN
84
+ this.getWxLocation().then(res => {
85
+ res = {...res, pf: 'MP-WX'}
86
+ resolve(res);
87
+ }).catch(err => {
88
+ reject(err);
89
+ });
90
+ // #endif
91
+ });
92
+
93
+ },
94
+
95
+ /**
96
+ * @description 火星转百度坐标
97
+ * @param lng
98
+ * @param lat
99
+ * @returns {{latitude: *, longitude: *}}
100
+ */
101
+ gcj02ToBaiduLatAndLng(lng, lat) {
102
+ const X_PI = 3.14159265358979324 * 3000.0 / 180.0;
103
+ const PI = 3.1415926535897932384626;
104
+ const A = 6378245.0;
105
+ const EE = 0.00669342162296594323;
106
+
107
+ lat = +lat;
108
+ lng = +lng;
109
+ let z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
110
+ let theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI);
111
+ let bd_lng = z * Math.cos(theta) + 0.0065;
112
+ let bd_lat = z * Math.sin(theta) + 0.006;
113
+ return {longitude: bd_lng, latitude:bd_lat}
114
+ },
115
+
116
+ /**
117
+ * @description 通用定位执行器:超时 + 防重复 + 成功缓存 + 失败兜底
118
+ * @param locateFn {Function} 发起定位的函数,接收 {type, success, fail, cancel}
119
+ * @param resolve {Function}
120
+ * @param reject {Function}
121
+ * @param isTargetCoords {Boolean} 是否已是目标坐标系(BD-09),true则跳过 gcj02→bd09 转换
122
+ */
123
+ locateWithFallback(locateFn, resolve, reject, isTargetCoords = false){
124
+ let done = false;
125
+ let timer = null;
126
+
127
+ //统一收尾:成功保存坐标,失败/超时兜底返回上次成功坐标
128
+ const finish = (isSuccess, val) => {
129
+ if (done) return;
130
+ done = true;
131
+ clearTimeout(timer);
132
+ if (isSuccess) {
133
+ this.lastSuccessCoords = val;
134
+ resolve(val);
135
+ } else if (this.lastSuccessCoords) {
136
+ resolve(this.lastSuccessCoords);
137
+ } else {
138
+ //首次定位失败:从本地存储的历史定位兜底
139
+ const cached = this.getCachedCoords();
140
+ if (cached) {
141
+ this.lastSuccessCoords = cached;
142
+ resolve(cached);
143
+ } else {
144
+ reject(val);
145
+ }
146
+ }
147
+ };
148
+
149
+ //5秒超时设置
150
+ timer = setTimeout(() => {
151
+ finish(false, '定位超时');
152
+ }, 5000);
153
+
154
+ //发起定位
155
+ locateFn({
156
+ type: 'gcj02',
157
+ success: (res) => {
158
+ const lng = Number(res.longitude);
159
+ const lat = Number(res.latitude);
160
+ //坐标有效性校验,避免 NaN 进入缓存
161
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) {
162
+ finish(false, '定位坐标无效');
163
+ return;
164
+ }
165
+ const coords = isTargetCoords
166
+ ? {longitude: lng, latitude: lat}
167
+ : this.gcj02ToBaiduLatAndLng(lng, lat);
168
+ coords.accuracy = res.accuracy !== undefined ? res.accuracy : null;
169
+ finish(true, coords);
170
+ },
171
+ fail: () => finish(false, '定位失败'),
172
+ cancel: () => finish(false, '定位取消')
173
+ });
174
+ },
175
+
176
+ /**
177
+ * @description 从本地存储读取历史定位坐标(BD-09)兜底
178
+ * @returns {{longitude:number, latitude:number, accuracy:null, fromCache:boolean}|null}
179
+ */
180
+ getCachedCoords(){
181
+ try {
182
+ //与 newLocaltionMixins.getLocationData() 一致:2小时定位缓存优先,永久缓存兜底
183
+ const cached = storage.get(this.locationStorageKeyCoords)
184
+ || storage.get(this.locationLongStorageKeyCoords);
185
+ const lng = Number(cached && cached.longitude);
186
+ const lat = Number(cached && cached.latitude);
187
+ if (Number.isFinite(lng) && Number.isFinite(lat)) {
188
+ return {longitude: lng, latitude: lat, accuracy: null, fromCache: true};
189
+ }
190
+ } catch (e) {}
191
+ return null;
192
+ },
193
+
194
+
195
+ //#ifdef H5
196
+ ...mapActions(['getH5WxAuthorize']),
197
+
198
+ /**
199
+ * @description 初始化微信定位信息
200
+ * @return {Promise<unknown>}
201
+ */
202
+ initH5WXLocation(){
203
+ return new Promise((resolve, reject) => {
204
+ //已经初始化
205
+ if(this.jwxCoordinatesSDK !== null){
206
+ resolve(this.jwxCoordinatesSDK );
207
+ return
208
+ }
209
+ //未初始化,10s授权超时兜底,避免 Promise 悬挂
210
+ let authorizeTimer = setTimeout(() => {
211
+ reject('微信授权超时');
212
+ }, 10000);
213
+ this.getH5WxAuthorize({jsApiList: ['openLocation', 'getLocation']})
214
+ .then(({jwxSDK}) => {
215
+ clearTimeout(authorizeTimer);
216
+ this.jwxCoordinatesSDK = jwxSDK;
217
+ resolve(jwxSDK)
218
+ })
219
+ .catch(res => {
220
+ clearTimeout(authorizeTimer);
221
+ reject('微信授权失败')
222
+ });
223
+ });
224
+ },
225
+
226
+ /**
227
+ * @description 微信环境获取位置
228
+ */
229
+ getH5WXLocation(){
230
+ return new Promise((resolve, reject) => {
231
+ this.initH5WXLocation()
232
+ .then(jwxSDK => {
233
+ this.locateWithFallback((opts) => jwxSDK.getLocation(opts), resolve, reject);
234
+ })
235
+ .catch(err => reject(err));
236
+ });
237
+ },
238
+
239
+ /**
240
+ * @description 微信H5定位失败后的降级定位(百度 → 原生)
241
+ * @param originErr 原始错误信息
242
+ * @param resolve {Function}
243
+ * @param reject {Function}
244
+ */
245
+ getH5FallbackLocation(originErr, resolve, reject){
246
+ if(this.$settings.baiduAk) {
247
+ this.getBaiduLocation()
248
+ .then(resolve)
249
+ .catch(() => {
250
+ if(window.navigator.geolocation) {
251
+ this.getH5NativeLocation().then(resolve).catch(() => reject(originErr || '定位失败'));
252
+ } else {
253
+ reject(originErr || '定位失败');
254
+ }
255
+ });
256
+ } else if(window.navigator.geolocation) {
257
+ this.getH5NativeLocation().then(resolve).catch(() => reject(originErr || '定位失败'));
258
+ } else {
259
+ reject(originErr || '定位失败');
260
+ }
261
+ },
262
+
263
+ /**
264
+ * @description 加载百度地图JS SDK(v3.0 经典版)
265
+ * @description 已加载直接复用;加载中复用缓存 Promise,避免重复创建 script 下载 SDK
266
+ * @returns {Promise<BMap>}
267
+ */
268
+ loadBaiduMapSDK(){
269
+ //已加载完成,直接复用
270
+ if (window.BMap && !window.BMap._preloader) {
271
+ return Promise.resolve(window.BMap);
272
+ }
273
+ //加载中,复用同一个 Promise
274
+ if (window.BMap && window.BMap._preloader) {
275
+ return window.BMap._preloader;
276
+ }
277
+ const ak = this.$settings.baiduAk;
278
+ if (!ak) {
279
+ return Promise.reject('百度AK未配置');
280
+ }
281
+ //未加载:创建加载流程,缓存 Promise 供并发调用复用
282
+ window.BMap = window.BMap || {};
283
+ window.BMap._preloader = new Promise((resolve, reject) => {
284
+ //10s加载超时兜底,避免回调不触发导致 Promise 悬挂
285
+ let sdkTimer = setTimeout(() => {
286
+ delete window._initBaiduMap;
287
+ window.BMap._preloader = null;
288
+ window.BMap = null;
289
+ reject('百度地图SDK加载超时');
290
+ }, 10000);
291
+
292
+ //百度地图SDK加载回调
293
+ window._initBaiduMap = () => {
294
+ clearTimeout(sdkTimer);
295
+ window.BMap._preloader = null;
296
+ delete window._initBaiduMap;
297
+ resolve(window.BMap);
298
+ };
299
+
300
+ //创建script标签加载百度地图SDK
301
+ const script = document.createElement('script');
302
+ script.src = `https://api.map.baidu.com/api?v=3.0&ak=${ak}&callback=_initBaiduMap`;
303
+ script.onerror = () => {
304
+ clearTimeout(sdkTimer);
305
+ window.BMap._preloader = null;
306
+ window.BMap = null;
307
+ delete window._initBaiduMap;
308
+ reject('百度地图SDK加载失败');
309
+ };
310
+ document.head.appendChild(script);
311
+ });
312
+ return window.BMap._preloader;
313
+ },
314
+
315
+ /**
316
+ * @description 百度定位(BMap.Geolocation,返回 BD-09 坐标)
317
+ */
318
+ getBaiduLocation(){
319
+ return new Promise((resolve, reject) => {
320
+ this.loadBaiduMapSDK()
321
+ .then(BMap => {
322
+ //百度返回 bd09,isTargetCoords=true 跳过 gcj02→bd09 转换
323
+ this.locateWithFallback((opts) => {
324
+ const geolocation = new BMap.Geolocation();
325
+ geolocation.getCurrentPosition(
326
+ (location) => opts.success({
327
+ longitude: location.point.lng,
328
+ latitude: location.point.lat,
329
+ accuracy: location.accuracy
330
+ }),
331
+ (err) => opts.fail && opts.fail(err)
332
+ );
333
+ }, resolve, reject, true);
334
+ })
335
+ .catch(err => reject(err));
336
+ });
337
+ },
338
+
339
+ /**
340
+ * @description 获取H5原生位置(navigator.geolocation,WGS-84 → GCJ-02 → BD-09)
341
+ */
342
+ getH5NativeLocation(){
343
+ return new Promise((resolve, reject) => {
344
+ this.locateWithFallback((opts) => {
345
+ window.navigator.geolocation.getCurrentPosition(
346
+ (pos) => {
347
+ //WGS-84 转 GCJ-02,交由执行器再转 BD-09
348
+ const gcj02 = this.wgs84ToGcj02LatAndLng(pos.coords.longitude, pos.coords.latitude);
349
+ gcj02.accuracy = pos.coords.accuracy !== undefined ? pos.coords.accuracy : null;
350
+ opts.success(gcj02);
351
+ },
352
+ (err) => opts.fail && opts.fail(err),
353
+ { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
354
+ );
355
+ }, resolve, reject);
356
+ });
357
+ },
358
+
359
+ /**
360
+ * @description WGS-84 转 GCJ-02(火星坐标)
361
+ * @param lng {Number} 经度
362
+ * @param lat {Number} 纬度
363
+ * @returns {{longitude: *, latitude: *}}
364
+ */
365
+ wgs84ToGcj02LatAndLng(lng, lat) {
366
+ const PI = 3.1415926535897932384626;
367
+ const a = 6378245.0;
368
+ const ee = 0.00669342162296594323;
369
+
370
+ //中国境外无需偏移,直接返回
371
+ if (lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271) {
372
+ return {longitude: lng, latitude: lat};
373
+ }
374
+
375
+ const transformLat = (x, y) => {
376
+ let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
377
+ ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
378
+ ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
379
+ ret += (160.0 * Math.sin(y / 12.0 * PI) + 320.0 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
380
+ return ret;
381
+ };
382
+
383
+ const transformLng = (x, y) => {
384
+ let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
385
+ ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
386
+ ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
387
+ ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
388
+ return ret;
389
+ };
390
+
391
+ let dLat = transformLat(lng - 105.0, lat - 35.0);
392
+ let dLng = transformLng(lng - 105.0, lat - 35.0);
393
+ const radLat = lat / 180.0 * PI;
394
+ let magic = Math.sin(radLat);
395
+ magic = 1 - ee * magic * magic;
396
+ const sqrtMagic = Math.sqrt(magic);
397
+ dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
398
+ dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
399
+ return {longitude: lng + dLng, latitude: lat + dLat};
400
+ },
401
+ //#endif
402
+
403
+ // #ifdef MP-WEIXIN
404
+ /**
405
+ * @description 小程序获取位置
406
+ * @description 根据 mpLocationFnCoords 选择调用 getFuzzyLocation(模糊,无需授权) 或 getLocation(精确,需授权)
407
+ */
408
+ getWxLocation(){
409
+ return new Promise((resolve, reject) => {
410
+ const locationFn = this.mpLocationFnCoords || 'getFuzzyLocation';
411
+
412
+ const doLocate = () => {
413
+ this.locateWithFallback((opts) => uni[locationFn](opts), resolve, reject);
414
+ };
415
+
416
+ //精确 getLocation 需要 scope.userLocation 授权
417
+ if (locationFn === 'getLocation') {
418
+ uni.getSetting({
419
+ success: (res) => {
420
+ if (res.authSetting['scope.userLocation']) {
421
+ doLocate();
422
+ } else {
423
+ uni.authorize({
424
+ scope: 'scope.userLocation',
425
+ success: doLocate,
426
+ fail: () => reject('定位权限被拒绝')
427
+ });
428
+ }
429
+ },
430
+ fail: () => reject('获取授权设置失败')
431
+ });
432
+ }
433
+ else {
434
+ //getFuzzyLocation 模糊定位,无需授权
435
+ doLocate();
436
+ }
437
+ });
438
+ },
439
+ // #endif
440
+
441
+ }
442
+ }