微信小程序实现打卡功能

477 阅读4分钟

前言

因业务需求微信小程序使用wx.getLocation和腾讯地图联合实现打卡功能,并且记录一下

需要满足以下条件

  1. 一个已开通 wx.getLocation接口权限的微信小程序
  2. 注册一个腾讯地图开放平台,并创建一个key 申请key

申请 wxgetLocation 接口权限时,可事先将前端页面静态页面实现截图上传辅助审核更容易通过

Snipaste_2023-02-14_09-19-47.png

一、下载并引用sdk包

可以点击前面的 申请key 链接进入页面下载相应的包,并在页面中引入

/* 根据自己实际业务,随意放置位置*/
var QQMapWX = require("../../utils/qqmap-wx-jssdk/qqmap-wx-jssdk")
var qqmapwx;
Page({
   data:{
       targetLocation:[0,0]
   },
   onLoad(){
     qqmapwx = new QQMapWX({
     key: "申请的key"
   })
   qqmapwx.geocoder({
     address: "北京天安门",
     success: function (res) {
       _this.setData({
         targetLocation: res.result.location
       })
     }
   })
 
   }
})

wxml代码

<view>
    <map style="width:100%;height:500rpx;margin-top:20rpx;" 
        name="dk" 
        longitude="{{targetLocation.lng}}" 
        latitude="{{targetLocation.lat}}"
     >
    </map>
</view>

Snipaste_2023-02-14_09-47-39.png

二、标记打卡点

前面通过 qqmapwx.geocoder({address:'北京天安门'}) 获取到了北京天安门的坐标,我们就以天安门的坐标为打卡点

定义一个创建地图标记的方法

/*------------------------util.js---------------------------------*/
function randId(len = 32) {
  let date = (new Date()).valueOf()
  console.log(date)
  let txt = '1234567890qwertyuioplkjhgfdsazxcvbnm'
  let pwd = ''
  for (let index = 0; index < len; index++) {
    pwd += txt.charAt(Math.floor(Math.random() * txt.length))
  }
  return pwd
}
/*---------------------------------------------------------*/
const {
  randId
} = require("../../utils/util")
var QQMapWX = require("../../utils/qqmap-wx-jssdk/qqmap-wx-jssdk")
var qqmapwx;
let marker = []  // 在Page()前面定义一个变量用了存储标记点,在后面还要在存一个用户当前所在位置的点
Page({
  data: {
    targetLocation: [0, 0]
  },
  onLoad() {
    qqmapwx = new QQMapWX({
      key: "申请的key"
    })
    qqmapwx.geocoder({
      address: "北京天安门",
      success: function (res) {
        _this.setData({
          targetLocation: res.result.location
        })
        _this.addMark(res.result.location.lng, res.result.location.lat, "北京天安门")
      }
    })

  },
  addMark(lng, lat, name) {
    marker.push({
      id: randId(), //标记点 id
      width: 50,
      height: 50,
      longitude: lng,
      latitude: lat,
      label: {
        content: name,
        color: '#f00',
        textAlign: "center",
        fontSize: 12
      },
      callout: {
        content: '气泡',
        color: "#f00",
        fontSize: 30
      },

    })
    this.setData({
      markers: marker
    })
  }
})

wxml代码

 <view>
     <map style="width:100%;height:500rpx;margin-top:20rpx;" 
         name="dk" 
         longitude="{{targetLocation.lng}}" 
         latitude="{{targetLocation.lat}}"
         markers="{{markers}}"
      >
     </map>
 </view>

Snipaste_2023-02-14_10-16-06.png

3、获取用户当前位置

获取用户当前位置信息使用wx.getLocation()

注意:wx.getLocation 并不能直接使用,需要申请权限,并且在app.json里面配置

{
  "pages":[
    "pages/index/index"
  ],
  "requiredPrivateInfos": [
    "getLocation"
  ],
  "permission": {
    "scope.userLocation": {
      "desc": "获取位置信息弹框询问信息"
    }
  },
}

Snipaste_2023-02-14_10-29-51.png

wx.getLocation 获取用户当前位置

wx.getLocation详细介绍

微信开发工具的定位是采用的 IP定位,存在误差并且只支持 gcj02

   data:{
        targetLocation:[0,0]
        userInfo:{
            location:[0,0],
        }
    },
  getUserLocation() {
    const _this = this
    wx.getLocation({
      type: 'gcj02', // 腾讯地图的坐标系采用的gcj02 
      success: function (res) {
        console.log(res)
        _this.setData({
          ["userInfo.location"]: [res.latitude, res.longitude]
        })
      }
    })
  },

使用 qqmapwx.reverseGeocoder 将获取到的坐标转换为中文地址信息 ,并在地图上标记

reverseGeocoder相关介绍

  getUserLocation() {
    const _this = this
    wx.getLocation({
      type: 'gcj02', // 腾讯地图的坐标系采用的gcj02 
      success: function (res) {
        console.log(res)
        _this.setData({
          ["userInfo.location"]: [res.latitude, res.longitude]
        })
        _this.addMark(res.longitude, res.latitude, "我") // 标记
        qqmapwx.reverseGeocoder({
          location: {
            latitude: res.latitude,
            longitude: res.longitude
          },
          success(resp) {
            _this.setData({
              ["userInfo.address"]: resp.result.address,
            })
          }
        })
      }
    })
  },

wxml代码

  <view> 纬度:{{userInfo.location[0]}} ,经度:{{userInfo.location[1]}}</view>
  <view style="height: 60rpx;line-height:60rpx;"> 当前位置:{{userInfo.address}}</view>

Snipaste_2023-02-14_11-01-36.png

三、计算两点之间的距离

function rad(d) {
  return d * Math.PI / 180.0
}
// 计算两个坐标点的直线距离
function distanceOf(p1, p2) {
  let radLng1 = rad(p1.x)
  let radLng2 = rad(p2.x)
  let mdifference =radLng1 - radLng2
  let difference = rad(p1.y) - rad(p2.y)
  let distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(difference / 2), 2) +
    Math.cos(radLng1) * Math.cos(radLng2) * Math.pow(Math.sin(mdifference / 2), 2)));
  //	弧长等于弧度乘地球半径(单位为米)
  distance = distance * 6378.137;
  //	精确距离的数值
  distance = Math.abs(Math.round(distance * 10000) / 10);
  return distance
}

在业务js文件中引入

const {
  distanceOf,
  randId
} = require("../../utils/util")

/*---------------------------------------------------------*/
// distanceOf 方法调用位置根据业务需求,我这边就放在获取到用户位置后
  getUserLocation() {
    const _this = this
    wx.getLocation({
      type: 'gcj02', // 腾讯地图的坐标系采用的gcj02 
      success: function (res) {
        console.log(res)
        _this.setData({
          ["userInfo.location"]: [res.latitude, res.longitude]

        })
        _this.addMark(res.longitude, res.latitude, "我")
        const dis = distanceOf({
          "x": _this.data.targetLocation.lng,
          "y": _this.data.targetLocation.lat
        }, {
          "x": res.longitude,
          "y": res.latitude
        })
        qqmapwx.reverseGeocoder({
          location: {
            latitude: res.latitude,
            longitude: res.longitude
          },
          success(resp) {
            _this.setData({
              ["userInfo.address"]: resp.result.address,
              ["userInfo.dis"]: dis 
            })
          }
        })
      }
    })
  },

wxml

  <view>
   </button>
        <view> 纬度:{{userInfo.location[0]}} ,经度:{{userInfo.location[1]}}</view>
        <view style="height: 60rpx;line-height:60rpx;"> 当前位置:{{userInfo.address}}</view>
        <view>距离打卡点还有:{{userInfo.dis}}米</view>
  </view>
  <map style="width:100%;height:500rpx;margin-top:20rpx;" name="dk" longitude="{{targetLocation.lng}}" latitude="{{targetLocation.lat}}" markers="{{markers}}">
  </map>

微信开发工具

Snipaste_2023-02-14_11-42-51.png

小程序

微信图片_20230214114325.jpg

四、页面优化

页面简单的调整

wxml

<view class="content1">
  <view class="boxm">
    <view class="box" style="border-right-color: {{userInfo.jl< 100 ?'#0f0':'#f60' }};">
    </view>
    <view class="boxtext">
      <view> 距离:{{userInfo.dis}}米</view>
    </view>
  </view>
  <view class="dk-text">
    <button bindtap="bindClock" class='dk-btn'>{{userInfo.jl <100?'正常打卡':'外勤打卡'}} </button>
        <view style="height: 60rpx;line-height:60rpx;"> 当前位置:{{userInfo.address}}</view>
  </view>
  <map class="map" name="dk" longitude="{{targetLocation.lng}}" latitude="{{targetLocation.lat}}" markers="{{markers}}">
  </map>
</view>

全部wxss

.content1 {
  height: auto;
  background: white;
  margin-top: 24rpx;
  width: 702rpx;
  margin-left: 24rpx;
  border-radius: 25rpx;
  padding: 20rpx 0;
}

.contentBox1 {
  width: 702rpx;
  border: 1rpx solid white;
  display: flex;
}

.time {
  background: #F4F5F7;
  width: 315rpx;
  height: 120rpx;
  margin-top: 24rpx;
  margin-left: 24rpx;
  border-radius: 20rpx;
}

.boxm {
  width: 260rpx;
  height: 260rpx;
  position: relative;
  transform-origin: 50% 50%;
  margin: auto;
  text-align: center;
}

.box {
  width: 260rpx;
  height: 260rpx;
  border: 4px solid rgb(196, 192, 192);
  border-right: 4px solid #f60;
  border-radius: 50%;
  animation: turn 1s linear infinite;
}

.boxtext {
  position: absolute;
  top: 50%;
  left: 0;
  width: 300rpx;
  height: 300rpx;
  font-size: 12px;

}

.dk-text {
  margin: 30rpx 10rpx;
}

.map {
  width: 100%;
  height: 560rpx;
  margin-top: 20rpx;
}

@keyframes turn {
  100% {
    transform: rotate(-360deg)
  }
}

// index.js
const {
  distanceOf,
  randId
} = require("../../utils/util")
var QQMapWX = require("../../utils/qqmap-wx-jssdk/qqmap-wx-jssdk")
var qqmapwx;
let marker = []
Page({
  data: {
    targetLocation: [0, 0],
    markers: [],
    allowRange: 100,
    userInfo: {
      location: [],
      userAddress: "",
      dis: 0
    }
  },
  onLoad() {
    const _this = this
    qqmapwx = new QQMapWX({
      key: "key"
    })

    qqmapwx.geocoder({
      address: "北京天安门",
      success: function (res) {
        _this.setData({
          targetLocation: res.result.location
        })
        _this.addMark(res.result.location.lng, res.result.location.lat, "北京天安门")
        _this.getUserLocation()
      }
    })

  },
  getUserLocation() {
    const _this = this
    wx.getLocation({
      type: 'gcj02', // 腾讯地图的坐标系采用的gcj02 
      success: function (res) {
        console.log(res)
        _this.setData({
          ["userInfo.location"]: [res.latitude, res.longitude]

        })
        _this.addMark(res.longitude, res.latitude, "我")
        const dis = distanceOf({
          "x": _this.data.targetLocation.lng,
          "y": _this.data.targetLocation.lat
        }, {
          "x": res.longitude,
          "y": res.latitude
        })
        qqmapwx.reverseGeocoder({
          location: {
            latitude: res.latitude,
            longitude: res.longitude
          },
          success(resp) {
            _this.setData({
              ["userInfo.address"]: resp.result.address,
              ["userInfo.dis"]: dis
            })
          }
        })
      }
    })
  },
  addMark(lng, lat, name, ) {
    marker.push({
      id: randId(), //标记点 id
      width: 50,
      height: 50,
      longitude: lng,
      latitude: lat,
      label: {
        content: name,
        color: '#f00',
        textAlign: "center",
        fontSize: 12
      },
      callout: {
        content: '气泡',
        color: "#f00",
        fontSize: 30
      },

    })
    this.setData({
      markers: marker
    })
  },
  bindClock() {
    wx.showToast({
      title: '外勤打卡',
    })
  },
  fun() {}
})

结语

到此就结束了,首次写如有不对,请留下您的建议