教你用Uniapp實現(xiàn)微信小程序的GPS定位打卡
寫在開頭
哈嘍,隔了幾天沒寫文章,小編又回來了(?ω?)。最近接了一個校園的需求,主要功能是希望學生每天進行定位打卡,幫助班導確認學生是否在校的情況。

上面圖片是大致的交互過程,定位打卡是個比較常見的功能了,只是很多時候都是在 APP 上完成的,這次需求方是希望專門做個小程序來使用,當然,整個小程序還有其他很多功能模塊,本章我們先來分享一下定位打卡功能,前端具體需要做哪些事情。
開通相關(guān)API權(quán)限
首先,因為這次定位打卡功能使用的是 GPS 來定位的,這就需要獲取用戶的地理位置信息。在小程序中,要獲取用戶的地理位置,微信官方提供了部分 API ,但是這些 API 有權(quán)限要求,我們需要先登陸 小程序后臺 去申請。
登陸后,按路徑「開發(fā)」-「開發(fā)管理」-「接口設置」中找到相關(guān) API ,填寫你使用 API 的理由,提交申請即可。

本次的功能小編一共會使用到了以下兩個 API :
- wx.chooseLocation:用于打開微信小程序自帶的地圖,能選擇一個位置,獲取目標位置的經(jīng)緯度。
- wx.getLocation:用于獲取用戶當前所在的地理位置信息,主要為了拿到經(jīng)緯度;不過,這個
API有點難申請通過,小編也是申請了三次才過的,真是挺麻煩-.-,好像一般小程序主體是政府、學?;蛘叽笃髽I(yè)等機構(gòu)就比較容易通過(●—●)。
API 權(quán)限申請好了后,我們就能進入正題了,開始正式的編碼工作。
項目初始化
項目小編直接使用 uniapp 的 HBuilderX 工具創(chuàng)建的,并使用了@dcloudio/uni-ui 作為 UI 庫。

定位打卡功能的具體交互過程很簡單,先由管理人員選取學校的位置,獲取到學校經(jīng)緯度信息保存起來,然后學生每次打卡也會獲取經(jīng)緯度坐標,然后計算兩個經(jīng)緯度坐標的距離,就能推算出學生是否在校了。

API 配置聲明
項目初始化后,我們還需要進行一步很關(guān)鍵的配置聲明,在項目的根目錄下,找到 manifest.json 文件,進行如下配置:
{
...
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
},
"usingComponents": true,
"permission": {
"scope.userLocation": {
"desc": "測試-"
}
},
"requiredPrivateInfos": ["getLocation", "chooseLocation"]
},
...
}主要是 requiredPrivateInfos 字段的配置,至于為什么可以看看官方說明,傳送門 。
選取學校位置
那么,接下來進行我們的第一步,選取學校的位置,代碼比較簡單,直接來看:
<template>
<view>
<uni-section title="學校" type="line">
<uni-card title="選點">
<button @tap="chooseLocation">請在地圖中選擇學校的位置</button>
<view v-if="isChooseTarget" class="info">
<view>{{ schoolInfo.address }}</view>
<view>{{ `(${schoolInfo.latitude},${schoolInfo.longitude})` }}</view>
</view>
</uni-card>
</uni-section>
</view>
</template>
<script>
export default {
data() {
return {
schoolInfo: {
latitude: '',
longitude: '',
address: '',
},
}
},
computed: {
isChooseTarget() {
return this.schoolInfo.latitude && this.schoolInfo.longitude
},
},
methods: {
// 選點
chooseLocation() {
uni.chooseLocation({
success: res => {
this.schoolInfo.latitude = res.latitude;
this.schoolInfo.longitude = res.longitude;
this.schoolInfo.address = res.address;
}
});
},
}
}
</script>
<style>
.info{
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 20rpx;
}
</style>
獲取用戶的地理位置信息
搞定完學校的位置后,接下來就要來獲取學生的地理位置了,主要是使用 wx.chooseLocation 來獲取。
不過,因為這個 API 初次調(diào)用時,會有一個主動詢問動作。

如果你選擇允許授權(quán),那么后續(xù)你可以直接調(diào)用該 API,但是如果選擇拒絕,那么調(diào)用該 API 就會直接進入錯誤的回調(diào),并不會有再次主動詢問的動作。
那么我們要如何重新授權(quán)呢?總不能拒絕后就不能使用了吧?
也就因為這么一個行為,我們還會牽扯出好幾個權(quán)限相關(guān)的 API,才能完成整個權(quán)限的閉環(huán)操作。
- wx.getSetting:獲取用戶的當前設置。
- wx.openSetting:調(diào)起用戶的設置界面。
- wx.authorize:提前向用戶發(fā)起授權(quán)請求。
上面小編簡單注釋了每個 API 的作用,詳細信息還是要參考官方文檔為準。
然后,為了更好的組織代碼,小編把權(quán)限這塊相關(guān)的進行簡單的封裝,新建 /utils/location.js 文件:
/**
* 獲取是否授權(quán)了定位權(quán)限
* @param { Boolean } launchAuth: 是否發(fā)起授權(quán)請求, 初次有效
* @return { Boolean }
*/
export function getLocationAuth(launchAuth) {
return new Promise(resolve => {
uni.getSetting({
success: res => {
if(launchAuth && res.authSetting['scope.userLocation'] === undefined) {
return uni.authorize({
scope: 'scope.userLocation',
success: () => {
resolve(true);
},
fail: () => {
resolve(false);
}
})
}
resolve(res.authSetting['scope.userLocation']);
},
fail: err => {
console.err(err);
}
})
})
}具體的使用:
<template>
<view>
...
<uni-section v-if="isChooseTarget" title="學生" type="line">
<uni-card title="當前位置實時信息">
<template v-slot:title>
<uni-list>
<uni-list-item title="當前位置實時信息">
<template v-slot:footer v-if="isAuth === 0">
<text @tap="reGrantAuth" class="text">重新授權(quán)</text>
</template>
</uni-list-item>
</uni-list>
</template>
<view class="block">
<view class="title">經(jīng)緯度:</view>
<view class="value">
<text v-if="!loading">{{ jwText || '-' }}</text>
<view v-else class="loading">
<uni-icons type="spinner-cycle" size="20"/>
</view>
</view>
</view>
</uni-card>
</uni-section>
</view>
</template>
<script>
import { getLocationAuth } from '@/utils/location';
export default {
data() {
return {
...,
loading: false,
isAuth: -1, // -1: 未授權(quán) 0: 拒絕授權(quán) 1:已授權(quán)
studentInfo: {
latitude: '',
longitude: '',
},
}
},
computed: {
...,
jwText() {
const { latitude, longitude } = this.studentInfo;
if(latitude && longitude) return `(${latitude},${longitude})`;
return ''
},
},
async onLoad() {
if(!await getLocationAuth()) {
this.isAuth = 0;
}
},
methods: {
chooseLocation() {
uni.chooseLocation({
success: async res => {
...
// 判斷是否授權(quán)
const authRes = await getLocationAuth(true);
if(authRes) {
// 獲取用戶當前位置
this.getLocationInfo();
this.isAuth = 1;
}else {
this.isAuth = 0;
}
}
});
},
// 獲取當前位置信息
getLocationInfo() {
this.loading = true;
uni.getLocation({
type: 'gcj02',
success: ({ latitude, longitude }) => {
this.studentInfo.latitude = latitude;
this.studentInfo.longitude = longitude;
this.loading = false;
}
});
},
// 重新授權(quán)
reGrantAuth() {}
}
}
</script>
<style>
.info{
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-top: 20rpx;
}
.block{
margin-bottom: 20rpx;
}
.title{
color: #000;
font-weight: bold;
}
.value{
width: 100%;
min-height: 40rpx;
}
.text{
font-size: 24rpx;
color: #287DE1;
}
.loading {
width: 40rpx;
height: 40rpx;
transform: rotate(360deg);
animation: rotation 3s linear infinite;
}
@keyframes rotation{
0%{
transform: rotate(0deg);
}
100%{
transform: rotate(360deg);
}
}
</style>
上面我們成功獲取到用戶的當前位置信息,當然,如果用戶選擇了拒絕,我們也提供了重新授權(quán)的方式。
export default {
...,
methods: {
...,
// 重新授權(quán)
async reGrantAuth() {
const authRes = await getLocationAuth();
if(authRes) {
uni.showToast({
title: '已授權(quán)',
duration: 500,
icon: 'none'
});
}else {
wx.openSetting({
success: (res) => {
if(res.authSetting['scope.userLocation']) {
this.getLocationInfo();
this.isAuth = 1;
}
},
})
}
},
}
}
經(jīng)緯度轉(zhuǎn)化成具體地址
上面我們已經(jīng)拿到了學生用戶的當前經(jīng)緯度坐標了,本來我們接下來只要計算兩個經(jīng)緯度坐標之間的距離就能完成功能了,奈何需求方還想要學生具體位置的中文信息,這就比較麻煩了,唉,但是麻煩也得做,否則沒飯吃呀-.-。
這個需求本質(zhì)就是讓我們把經(jīng)緯度轉(zhuǎn)成具體地址,這里需要使用額外的插件來處理,方式有很多,小編選擇 騰訊的位置服務。
我們直接按照他官網(wǎng)的介紹操作即可。

具體使用:
<template>
<view>
...
<uni-section v-if="isChooseTarget" title="學生" type="line">
<uni-card title="當前位置實時信息">
...
<view class="block">
<view class="title">詳細地址:</view>
<view class="value">
<text v-if="!loading">{{ studentInfo.address || '-' }}</text>
<view v-else class="loading">
<uni-icons type="spinner-cycle" size="20"/>
</view>
</view>
</view>
</uni-card>
</uni-section>
</view>
</template>
<script>
import { getLocationAuth } from '@/utils/location';
const QQMapWX = require('@/utils/qqmap-wx-jssdk.min.js');
export default {
data() {
return {
...
studentInfo: {
latitude: '',
longitude: '',
address: '',
},
mapInstance: null,
}
},
computed: { ... },
async onLoad() {
this.mapInstance = new QQMapWX({
key: '你的密鑰',
});
if(!await getLocationAuth()) {
this.isAuth = 0;
}
},
methods: {
chooseLocation() { ... },
getLocationInfo() {
this.loading = true;
uni.getLocation({
type: 'gcj02',
success: ({ latitude, longitude }) => {
this.studentInfo.latitude = latitude;
this.studentInfo.longitude = longitude;
// 經(jīng)緯度轉(zhuǎn)成具體地址
this.mapInstance.reverseGeocoder({
location: { latitude, longitude },
success: res => {
console.log(res)
this.studentInfo.address = res.result.formatted_addresses.recommend;
this.loading = false;
}
});
}
});
},
// 重新授權(quán)
async reGrantAuth() { ... },
}
}
</script>
計算兩個經(jīng)緯度坐標之間的距離
具體位置也搞定后,就剩下最終的功能,計算兩個經(jīng)緯度坐標之間的距離,這聽起來好像很難,但實際很簡單,網(wǎng)上有大把現(xiàn)成的方法,我們直接抄個來耍就行了。
/**
* 根據(jù)經(jīng)緯度獲取兩點距離
* @param la1 第一個坐標點的緯度 如:24.445676
* @param lo1 第一個坐標點的經(jīng)度 如:118.082745
* @param la2 第二個坐標點的緯度
* @param lo2 第二個坐標點的經(jīng)度
* @return { Object } { km: 千米/公里, m: 米 }
* @tips 注意經(jīng)度和緯度參數(shù)別傳反了, 一般經(jīng)度為0~180、緯度為0~90
*/
export function calcDistanceLL(la1, lo1, la2, lo2) {
let La1 = la1 * Math.PI / 180.0;
let La2 = la2 * Math.PI / 180.0;
let La3 = La1 - La2;
let Lb3 = lo1 * Math.PI / 180.0 - lo2 * Math.PI / 180.0;
let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(La3 / 2), 2) + Math.cos(La1) * Math.cos(La2) * Math.pow(Math.sin(
Lb3 / 2), 2)));
s = s * 6378.137;
s = Math.round(s * 10000) / 10000;
return {
km: s,
m: Math.round(s * 1000)
};
}具體使用:
<template>
<view>
...
<uni-section v-if="isChooseTarget" title="學生" type="line">
<uni-card title="當前位置實時信息">
...
<view class="block">
<view class="title">距離學校距離:</view>
<view class="value">
<text v-if="!loading">{{ distanceToText || '-' }}</text>
<view v-else class="loading">
<uni-icons type="spinner-cycle" size="20"/>
</view>
</view>
</view>
<view class="block">
<view class="title">是否可打卡:</view>
<view class="value">
<text v-if="studentInfo.distance > 500 || studentInfo.distance === ''">否</text>
<view @click="punchClock" v-else class="button yd-flex-h-hC-vC">打卡</view>
</view>
</view>
</uni-card>
</uni-section>
</view>
</template>
<script>
import { getLocationAuth, calcDistanceLL } from '@/utils/location';
const QQMapWX = require('@/utils//qqmap-wx-jssdk.min.js');
export default {
data() {
return {
...
studentInfo: {
latitude: '',
longitude: '',
address: '',
distance: '',
},
mapInstance: null,
}
},
computed: {
distanceToText() {
if(this.mainInfo.distance !== '') {
return `${this.mainInfo.distance} 米`;
}
return '';
},
},
async onLoad() {
this.mapInstance = new QQMapWX({
key: '你的密鑰',
});
if(!await getLocationAuth()) {
this.isAuth = 0;
}
},
methods: {
punchClock() {
uni.showToast({
title: '打卡成功',
duration: 500,
});
},
chooseLocation() { ... },
getLocationInfo() {
this.loading = true;
uni.getLocation({
type: 'gcj02',
success: ({ latitude, longitude }) => {
this.studentInfo.latitude = latitude;
this.studentInfo.longitude = longitude;
// 經(jīng)緯度轉(zhuǎn)成具體地址
this.mapInstance.reverseGeocoder({
location: { latitude, longitude },
success: res => {
this.studentInfo.address = res.result.formatted_addresses.recommend;
// 計算兩個經(jīng)緯度之間的距離
const distance = calcDistanceLL(
this.schoolInfo.latitude,
this.schoolInfo.longitude,
latitude,
longitude,
);
this.studentInfo.distance = distance.m;
this.loading = false;
}
});
}
});
},
// 重新授權(quán)
async reGrantAuth() { ... },
}
}
</script>
<style>
...
.button{
height: 60rpx;
color: #fff;
line-height: 1;
background-color: #287DE1;
border-radius: 4rpx;
font-size: 20rpx;
width: 30%;
margin: auto;
}
</style>

至此,本篇文章就寫完啦,撒花撒花。
總結(jié)
到此這篇關(guān)于用Uniapp實現(xiàn)微信小程序的GPS定位打卡的文章就介紹到這了,更多相關(guān)Uniapp實現(xiàn)小程序GPS定位打卡內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序?qū)崿F(xiàn)滑動切換自定義頁碼的方法分析
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)滑動切換自定義頁碼的方法,結(jié)合實例形式分析了微信小程序頁碼動態(tài)切換相關(guān)實現(xiàn)技巧與注意事項,需要的朋友可以參考下2018-12-12
JavaScript之DOM_動力節(jié)點Java學院整理
由于HTML文檔被瀏覽器解析后就是一棵DOM樹,要改變HTML的結(jié)構(gòu),就需要通過JavaScript來操作DOM。始終記住DOM是一個樹形結(jié)構(gòu)。2017-07-07
JavaScript使用Math.Min返回兩個數(shù)中較小數(shù)的方法
這篇文章主要介紹了JavaScript使用Math.Min返回兩個數(shù)中較小數(shù)的方法,涉及javascript中Math.Min方法的使用技巧,非常具有實用價值,需要的朋友可以參考下2015-04-04

