微信小程序地图获取地点信息(打卡签到功能为例)--7-26

时间:2024-04-17 15:15:39

微信小程序地图获取地点信息(打卡签到功能为例)

解决方案:利用微信小程序的地图组件获取到用户的地理位置信息(经纬度),再通过腾讯地图 SDK 获取到对应的地理位置(即地名)

效果图

  • 地图已打马赛克

前提步骤

首先需要了解的

代码部分

配置性代码

app.js 部分关键代码

  • key 需要换成你自己申请的
//app.js
// 引入SDK核心类
var QQMapWX = require(\'/utils/qqmap-wx-jssdk.min.js\')

App({
  globalData: {
    // ..其他全局变量..
    patrolForm: null,
      
    // 实例化API核心类
    qqmapsdk: new QQMapWX({
      key: \'这个key是你申请下来的key\' // 必填
    }),
      
    // ..其他全局变量..
  },
  // 其他代码
})

app.json 部分关键代码(permission那个对象)

{
  "pages":[
    "pages/location_check_in/location_check_in"
  ],
  "window":{
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#3db0fc",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle": "white"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json",
  "permission": {
    "scope.userLocation": {
      "desc": "您的位置信息将用于小程序签到功能"
    }
  }
}

https://apis.map.qq.com 添加到小程序后台-开发-开发设置-服务器域名中

  • 这个操作需要小程序管理员进到后台去配置
  • 我们只需要在开发工具右上角 详情-本地设置 勾上 不校验合法域名........ 即可继续开发

功能性代码

页面代码:location_check_in.wxml

<!--location_check_in/location_check_in.wxml-->
<view class="wholeContinaer">
	<map id="myMap" markers="{{markers}}" style="width:100%;height:100vh;" longitude="{{poi.longitude}}" latitude="{{poi.latitude}}" scale=\'16\' show-location>
	</map>

	<view class="checkInPanel">
		<!-- <view class="checkInBtn" bindtap="checkIn" style="background:url(\'../../images/zcxj/checkInBj.png\') center / contain no-repeat"> -->
		<view class="checkInBtn {{canClick ? \'\' : \'disableClick\'}}" bindtap="checkIn">
			<view class="checkInTitle">签到</view>
			<view class="checkInTime">{{time}}</view>
		</view>
		<!-- <view class="whitePositionPanel" style="background:url(\'../../images/zcxj/jxk.png\') center / contain no-repeat"> -->
		<view class="whitePositionPanel">
			<view class="positonTextRow">
				<image class="positionIcon" src="../../images/zcxj/positionIcon.png"></image>
				<view class="positionFont">{{addressName}}</view>
			</view>
		</view>
		<view class="rePosition" bindtap="rePosition">
			<image class="positionIcon" src="../../images/zcxj/rePosition.png"></image>
			<view class="positionFont">重新定位</view>
		</view>
	</view>
</view>

样式代码:location_check_in.wxss

/* location_check_in/location_check_in.wxss */
Page{
  background-color: #f2f2f2;
}
.wholeContinaer{
  position: relative;
  height: 100vh;
}

.mapContianer{
  background-color: turquoise;
  width: 100vw;
  height: 100vh;
}

.checkInPanel{
  position: absolute;
  width: 705rpx;
  height: 520rpx;
  bottom: 25rpx;
  left: 25rpx;
  /* background-color: #ffffff; */
}
.checkInBtn{
  width: 280rpx;
  height: 280rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  position: absolute;
  left: calc(50% - 140rpx);
  z-index: 12;

  border-radius: 50%;
  background-image: linear-gradient(180deg, 
      #00a0e9 0%, 
      #0095e9 73%, 
      #0089e9 100%);
  box-shadow: 0 0 10rpx 0rpx #0089e9;
  /* margin: 0 auto; */
}
.disableClick{
  pointer-events: none;
}
.checkInTitle{
  font-size: 36rpx;
  line-height: 34rpx;
  color: #ffffff;
}
.checkInTime{
  font-size: 24rpx;
  line-height: 34rpx;
  color: #8cd7fe;
  margin-top: 10rpx;
}

.whitePositionPanel{
  width: 100%;
  height: 412rpx;
  position: absolute;
  bottom: 0rpx;

  background-color: #ffffff;
  border-radius: 10rpx;
  
  display: flex;
  align-items: center;
  justify-content: center;
}

.positonTextRow{
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 80rpx;
}
.positonTextRow .positionIcon{
  width: 20rpx;
  height: 26rpx;
  margin-right: 10rpx;
}
.positonTextRow .positionFont{
  font-size: 22rpx;
  line-height: 29rpx;
  color: #9c9c9c;
}

.rePosition{
  width: 130rpx;
  height: 35rpx;
  background-color: #ffffff;
  border-radius: 10rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  top: 60rpx;
}
.rePosition .positionIcon{
  width: 24rpx;
  height: 24rpx;
  margin-right: 10rpx;
}
.rePosition .positionFont{
  font-size: 18rpx;
  color: #333333;
  line-height: 35rpx;
}

逻辑代码:location_check_in.js

  • 逆地址解析(坐标位置描述)函数官方文档:reverseGeocoder(options:Object)
  • realyCheckIn 函数为业务逻辑代码,此前已经获取到了地理位置,缺少了部分前置业务代码,会导致代码报错,改成你自己的就好
// location_check_in/location_check_in.js
const util = require(\'../../utils/util\')
const app = getApp()
const urlList = require("../../utils/api.js")  // 根据实际项目自己配置

// 实例化API核心类
const qqmapsdk = app.globalData.qqmapsdk

Page({

  /**
   * 页面的初始数据
   */
  data: {
    markers: \'\',
    poi: {
      latitude: \'\',
      longitude: \'\'
    },
    addressName: \'\',
    time: \'\',
    timer: \'\',
    timer2: \'\',  // 用来每个一段时间自动刷新一次定位
    canClick: true
  },
 
  getAddress(e) {
    var that = this;
    qqmapsdk.reverseGeocoder({
      //位置坐标,默认获取当前位置,非必须参数
      /**
       * 
        location: {
          latitude: 39.984060,
          longitude: 116.307520
        },
      */
      // 成功后的回调
      success: function(res) {
        // console.log(res);
        that.setData({
          addressName: res.result.address
        })
        var res = res.result;
        var mks = [];
        //当get_poi为0时或者为不填默认值时,检索目标位置,按需使用
        mks.push({ // 获取返回结果,放到mks数组中
          title: res.address,
          id: 0,
          latitude: res.location.lat,
          longitude: res.location.lng,
          iconPath: \'../../images/zcxj/myPosition.png\', // 图标路径
          width: 21,
          height: 28,
          // callout: { //在markers上展示地址名称,根据需求是否需要
          //   content: res.address,
          //   color: \'#000\',
          //   display: \'ALWAYS\'
          // }
        });
        that.setData({ // 设置markers属性和地图位置poi,将结果在地图展示
          markers: mks,
          poi: {
            latitude: res.location.lat,
            longitude: res.location.lng
          }
        });
      },
      fail: function(error) {
        console.error(error);
      },
      complete: function(res) {
        console.log(res);
      }
    })
  },
  getTime: function () {
    let that = this
    let time = that.data.time
    that.setData({
      timer: setInterval(function () {
        time = util.formatTime(new Date())
        that.setData({
          time: time.substr(-8)
        });
        if (time == 0) {
          // 页面跳转后,要把定时器清空掉,免得浪费性能
          clearInterval(that.data.timer)
        }
      }, 1000)
    })
  },
  rePosition: function () {
    console.log(\'用户点了重新定位\')
    this.getAddress()
  },
  checkIn: function () {
    this.setData({
      canClick: false
    })
    console.log(\'用户点击了签到\')

    
    var that = this
    var nowTime = util.formatTime(new Date())
    wx.showModal({
      title: \'请确认打卡信息\',
      // content: \'请确认待整改项已整改完毕!\',
      content: `地点:${this.data.addressName}\n时间:${nowTime}`,  // 开发者工具上没有换行,真机调试时会有的
      confirmText: \'确认\',
      success (res) {
        if (res.confirm) {
          console.log(\'用户点击确定\')
          // 调起签到接口
          that.realyCheckIn()
          
        } else if (res.cancel) {
          console.log(\'用户点击取消\')
          that.setData({
            canClick: true
          })
        }
      }
    })
  },
  realyCheckIn: function() {
    var that = this
    var patrolForm = app.globalData.patrolForm  // 其他需要一并提交过去的业务数据

    console.log(app.globalData)
    // debugger
    // 要在这里给 patrolForm 补充其他的参数
    patrolForm.checkaddress = this.data.addressName
    patrolForm.searchtime = util.formatTime(new Date())
    // 应该先判断用户有没有登录,没登录就授权登录
    patrolForm.searchuser = app.globalData.user ? app.globalData.user.UserName : app.globalData.userInfo.nickName
    console.log("传给后台的 searchuser:", patrolForm.searchuser)
    // 拼接:"经度,纬度"
    patrolForm.latandlon = this.data.poi.longitude + "," + this.data.poi.latitude
    

    console.log(patrolForm)
    console.log("↑ 签到提交的post参数")

    var tmpNumber = 0
    wx.request({
      url: urlList.submitCheckInInfo,
      data: patrolForm,
      method: "POST",
      header: {
        \'content-type\': \'application/x-www-form-urlencoded\'
      },
      success: function (res) {
        console.log(res)
        if(res.data.IsSuccess) {
          console.log(res.data.IsSuccess, typeof(res.data.IsSuccess))
          console.log("请求成功")
          var patrolId = res.data.ReturnData[0].id
          // // 看怎么取到返回的id
          // debugger

          if (patrolForm.img_arr1.length > 0) {
            for (var i = 0; i < patrolForm.img_arr1.length; i++){
              tmpNumber = i
              wx.uploadFile({
                // 图片上传的接口地址
                url: urlList.submitCheckInPhoto + "?patrolid=" + patrolId,
                filePath: patrolForm.img_arr1[i],
                name: \'content\',
                // formData: {
                //   // 这里面可以携带一些参数一并传过去
                //   patrolId: patrolId
                // },
                // header: {
                //   Authorization: token
                // },
                success: function (res) {
                  console.log(res)
                },
                fail: function (res) {
                  that.setData({
                    canClick: true
                  })
                },
                complete: function () {
                  // 因为上传图片是异步操作,所以会导致这里的 i 会取不到,故需要用个作用域更大点的变量来标识,否则 if 里面的代码不会执行
                  if(tmpNumber === patrolForm.img_arr1.length - 1) {
                    // 有图片就等图片上传完了再返回首页
                    wx.showToast({
                      title: \'巡查签到成功!\',
                      icon: \'success\',
                      duration: 2000,
                      complete: function(){
                        wx.navigateBack({
                          delta: 2  // 回退两层页面
                        })
                      }
                    })
                  }
                }
              })
            }
          } else{
            wx.showToast({
              title: \'巡查签到成功!\',
              icon: \'success\',
              duration: 2000,
              complete: function(){
                wx.navigateBack({
                  delta: 2
                })
              }
            })
          }
        }
      },
      fail: function(res) {
        that.setData({
          canClick: true
        })
      }
    })

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    var that = this
    that.getTime()
    that.getAddress()
    
    that.setData({
      canClick: true, // 允许用户点击,防止多次提交
      timer2: setInterval(function () {
        that.getAddress()
      }, 20000)  // 每20秒刷新一次定位
    })
  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    clearInterval(this.data.timer)
    clearInterval(this.data.timer2)
    console.log("定时器已被清除")
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})

demo 下载

gitee地址:https://gitee.com/Suwanbin/positionCheckIn

如果对你有帮助,还请帮忙点个 star!