关于微信小程序根据定位实现打卡功能的工作总结
关于微信小程序根据定位实现打卡功能的工作总结
最近工作的一小项任务是在微信小程序实现一个类似企业微信上下班打卡的功能,前端主要的工作量则在于计算位置距离
1.添加店铺的经纬度
首先我们要添加店铺的经纬度
添加经纬度,我们可以使用腾讯的提供的一个小程序地图选点插件,首先登录微信公众号小程序后台,在“设置”栏目里的“第三方设置”的底部有一个“插件管理”,在那里可以选择添加插件
在添加插件的弹框中搜索“地图选点”
然后选择添加“腾讯位置服务地图选点”小程序插件
然后点开插件的详情,里面有开发文档,有详细关于这个地图选点的使用说明
一切配置完成后,在小程序里打开地图选点插件会出现如下界面
至此我们完成了店铺的经纬度添加的关键步骤
2.微信小程序里获取位置定位
wx.getLocation(Object object)
wx.getLocation({
type: 'wgs84',
success (res) {
const latitude = res.latitude
const longitude = res.longitude
const speed = res.speed
const accuracy = res.accuracy
}
})
这个微信小程序提供的api获取到的只是经纬度,如果我们还要知道当前位置的具体信息,例如,XX省,XX市,XX街道的话,则还需要利用腾讯位置服务提供的sdk执行解析
3.注册腾讯位置服务账号、配置信息、下载SDK
首先我们要前往腾讯位置服务官网注册一个腾讯位置服务的账号
控制台 --> 我的应用 --> 创建应用 -->填写名称以及分类
添加key -->勾选 WebServiceAPI -->勾选微信小程序 -->填写你的小程序 APPID --> 复制Key(这个很重要小程序要用)
头部导航 --> 开发文档 --> 微信小程序 --> 微信小程序JavaScript SDK
然后里面有详细关于微信小程序位置解析的相关操作和代码示例
怕图片看得不太清,主要的我还是摘录罗列一下
-
申请开发者密钥(key):申请密钥
-
开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用 ->添加key-> 勾选WebServiceAPI -> 保存
(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)
-
下载微信小程序JavaScriptSDK,微信小程序JavaScriptSDK v1.1 JavaScriptSDK v1.2
-
安全域名设置,在小程序管理后台 -> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加apis.map.qq.com
-
小程序示例
// 引入SDK核心类,js文件根据自己业务,位置可自行放置
var QQMapWX = require('../../libs/qqmap-wx-jssdk.js');
var qqmapsdk;
Page({
onLoad: function () {
// 实例化API核心类
qqmapsdk = new QQMapWX({
key: '申请的key'
});
},
onShow: function () {
// 调用接口
qqmapsdk.search({
keyword: '酒店',
success: function (res) {
console.log(res);
},
fail: function (res) {
console.log(res);
},
complete: function (res) {
console.log(res);
}
});
}
})
4.经纬度解析关键代码实现
globalData = {
getLocation: function () {
return new Promise((resolve, reject) => {
// 实例化API核心类
const qqmapsdk = new QQMapWX({
key: 'xxxxx',
});
wx.getLocation({
type: 'gcj02', //返回可以用于wx.openLocation的经纬度
success(res) {
const latitude = res.latitude;
const longitude = res.longitude;
qqmapsdk.reverseGeocoder({
location: {
latitude,
longitude,
},
success(res) {
const locationData = {
...res.result.address_component,
latitude,
longitude,
};
// 此处使用了redux的数据仓库状态管理
store.dispatch({
type: action.SET_CURRENT_LOACTION,
currentLocation: locationData,
});
resolve(locationData);
},
fail: function (error) {
console.error(error);
reject('经纬度解析失败');
},
complete: function (res) {
console.log(res);
},
});
},
fail: function (error) {
console.error(error);
reject(
failAuthDenyLocationEnum.FAIL_AUTH_DENY_LOCATION
);
},
});
});
},
};
正常经纬度解析结果:
5.得到两个点的经纬度然后进行距离计算
此为百度得来的代码 。
//计算距离,参数分别为第一点的纬度,经度;第二点的纬度,经度
getDistance(lat1,lng1,lat2,lng2){
//进行经纬度转换为距离的计算
function rad(d){
return d * Math.PI / 180.0;//经纬度转换成三角函数中度分表形式。
}
const radLat1 = rad(lat1);
const radLat2 = rad(lat2);
const a = radLat1 - radLat2;
const b = rad(lng1) - rad(lng2);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
s = s *6378.137 ;
console.log('s', s)
// 输出为公里
s = Math.round(s * 10000) / 10000;
// 输出为米
// s = Math.round(s * 10000000);
// s = s.toFixed(4);
return s;
}
6. 是否在打卡范围之内
countDistance() {
if(!this.shopLat || !this.shopLng){
this.locationInfo = '店铺经纬度设置错误,无法进行打卡'
this.disableClockIn = true
this.$apply();
return
}
if(!this.userLat || !this.userLng){
this.locationInfo = '用户位置信息错误'
this.disableClockIn = true
this.$apply();
return
}
this.distance = this.getDistance(this.userLat, this.userLng, this.shopLat, this.shopLng)
if(this.distance <= this.distanceClockIn){
this.locationInfo = '您已在打卡范围内'
this.disableClockIn = false
}
if(this.distance > this.distanceClockIn){
this.locationInfo = '您不在打卡范围内'
this.disableClockIn = true
}
this.$apply();
}
7.实时监控位置变化
wx.getSetting({
success(res) {
if (!res.authSetting['scope.userLocation']) {
_this.locationInfo = '您已拒绝授权位置信息'
_this.failAuthDenyLocation = true
_this.$apply()
} else {
// 每10秒计算一次距离 暂且使用wx.getLocation,使用wx.onLocationChange监听获取位置信息变化存在问题
_this.timer = setInterval(() => {
_this.getLocation().then(() => {
_this.countDistance()
console.log('timer-distance', _this.distance)
}).catch(err => console.log(err))
}, 10000);
}
}
})
// 页面卸载时清除计时器
onUnload() {
clearInterval(this.timer)
}
至此关于微信小程序根据定位实现打卡功能的关键步骤都完成了
8.业务流程代码
最后附上此打卡功能的业务流程代码,我使用的是wepy
import wepy from 'wepy';
import { connect } from 'wepy-redux';
import util from '../../utils/util';
import api from '../../utils/api';
import { failAuthDenyLocationEnum } from '../../utils/constant';
@connect({
userInfo(state) {
return state.user.userInfo;
},
currentLocation(state) {
return state.user.currentLocation;
},
okUser(state) {
return state.user.ok;
},
})
export default class ClerkClockIn extends wepy.page {
config = {
navigationBarTitleText: '店员结班',
navigationBarBackgroundColor: '#EA1717',
navigationBarTextStyle: 'white',
};
components = {
};
mixins = [];
data = {
client: {},
key: '',
nowTime: '',
timer: null,
shopLat: '',
shopLng: '',
userLat: '',
userLng: '',
distance: '',
locationInfo: '',
disableClockIn: true,
distanceClockIn: 3, // 打卡距离 1 表示一公里
failAuthDenyLocation: false, // 是否拒绝授权位置信息
};
computed = {};
methods = {
clerkClockIn() {
if(!this.disableClockIn){
if(this.clerkPrisonLogData) {
api.shop.clerkPrisonLogOut({shopId: this.shopId}).then(r => {
if(r.status === 'OK'){
util.toast('打卡成功')
setTimeout(() => {
this.query()
}, 500)
}
}).catch(err => console.log(err))
}else{
api.shop.clerkPrisonLogIn({shopId: this.shopId}).then(r => {
if(r.status === 'OK'){
util.toast('打卡成功')
setTimeout(() => {
this.query()
}, 500)
}
}).catch(err => console.log(err))
}
}
},
//再次获取授权
openConfirm: function () {
wx.showModal({
content: '检测到您没打开此小程序的定位权限,是否去设置打开?',
confirmText: "确认",
cancelText: "取消",
success: function (res) {
console.log(res);
//点击“确认”时打开设置页面
if (res.confirm) {
console.log('用户点击确认')
wx.openSetting({
success: (res) => {
}
})
} else {
console.log('用户点击取消')
}
}
})
},
};
watch = {}
//计算距离,参数分别为第一点的纬度,经度;第二点的纬度,经度
getDistance(lat1,lng1,lat2,lng2){
//进行经纬度转换为距离的计算
function rad(d){
return d * Math.PI / 180.0;//经纬度转换成三角函数中度分表形式。
}
const radLat1 = rad(lat1);
const radLat2 = rad(lat2);
const a = radLat1 - radLat2;
const b = rad(lng1) - rad(lng2);
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
s = s *6378.137 ;
console.log('s', s)
// 输出为公里
s = Math.round(s * 10000) / 10000;
// 输出为米
// s = Math.round(s * 10000000);
// s = s.toFixed(4);
return s;
}
// 是否在打卡范围之内
countDistance() {
if(!this.shopLat || !this.shopLng){
this.locationInfo = '店铺经纬度设置错误,无法进行打卡'
this.disableClockIn = true
this.$apply();
return
}
if(!this.userLat || !this.userLng){
this.locationInfo = '用户位置信息错误'
this.disableClockIn = true
this.$apply();
return
}
this.distance = this.getDistance(this.userLat, this.userLng, this.shopLat, this.shopLng)
if(this.distance <= this.distanceClockIn){
this.locationInfo = '您已在打卡范围内'
this.disableClockIn = false
}
if(this.distance > this.distanceClockIn){
this.locationInfo = '您不在打卡范围内'
this.disableClockIn = true
}
this.$apply();
}
getLocation() {
return new Promise((resolve, reject) => {
this.$parent.globalData.getLocation().then(({latitude, longitude}) => {
if(latitude && longitude){
this.userLat = latitude
this.userLng = longitude
this.failAuthDenyLocation = false
this.$apply()
resolve()
}
}).catch(err => {
console.log(err)
if(err === failAuthDenyLocationEnum.FAIL_AUTH_DENY_LOCATION){
}
reject(err)
});
})
}
// 实时监控位置变化
onShow() {
this.getLocation().then(() => {
this.countDistance()
}).catch(err => console.log(err))
const _this = this
wx.getSetting({
success(res) {
if (!res.authSetting['scope.userLocation']) {
_this.locationInfo = '您已拒绝授权位置信息'
_this.failAuthDenyLocation = true
_this.$apply()
} else {
// 每10秒计算一次距离 暂且使用wx.getLocation,使用wx.onLocationChange监听获取位置信息变化存在问题
_this.timer = setInterval(() => {
_this.getLocation().then(() => {
_this.countDistance()
console.log('timer-distance', _this.distance)
}).catch(err => console.log(err))
}, 10000);
}
}
})
this.$apply();
}
onReady() {
api.common.queryClientInfo().then((r) => {
this.client = r;
this.$apply();
});
}
//
onLoad(options) {
console.log('currentLocation', this.currentLocation)
this.shopId = options.shopId ? Number(options.shopId) : ''
this.shopLat = options.shopLat ? Number(options.shopLat) : ''
this.shopLng = options.shopLng ? Number(options.shopLng) : ''
this.userLat = this.currentLocation.latitude
this.userLng = this.currentLocation.longitude
this.$apply();
}
onUnload() {
clearInterval(this.timer)
}
onShareAppMessage(rest) {}
}