页面效果展示
集成腾讯地图SDK
腾讯位置服务为微信小程序提供了基础的标点能力、线和圆的绘制接口等地图组件和位置展示、地图选点等地图API位置服务能力支持,使得开发者可以自由地实现自己的微信小程序产品。 在此基础上,腾讯位置服务微信小程序JavaScript SDK是专为小程序开发者提供的LBS数据服务工具包,可以在小程序中调用腾讯位置服务的POI检索、关键词输入提示、地址解析、逆地址解析、行政区划和距离计算等数据服务,让您的小程序更强大!
文档地址:微信小程序JavaScript SDK
使用步骤说明:
1.申请开发者密钥(key):申请密钥
2.开通webserviceAPI服务
控制台 ->应用管理 -> 我的应用 ->添加key-> 勾选WebServiceAPI -> 保存
(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)
3.下载微信小程序JavaScriptSDK
微信小程序JavaScriptSDK v1.1 JavaScriptSDK v1.2 ,这里推荐下载1.2版本,将下载好的SDK放在对应文件夹中,去引用它(即 qqmap-wx-jssdk.min.js
文件)引用到你小程序项目中。
4.安全域名设置
在小程序管理后台 -> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com
- 这个操作需要小程序管理员进到后台去配置
- 本地环境开发只需设置 勾上 不校验合法域名 即可
5.小程序核心代码示例
// 引入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: 'DreamCoders',success: function (res) {console.log(res);},fail: function (res) {console.log(res);},complete: function (res) {console.log(res);}});}
})
微信小程序代码
wx.getLocation(Object object)
以 Promise 风格 调用:支持
用户授权:需要 scope.userLocation
小程序插件:支持,需要小程序基础库版本不低于 1.9.6
微信 Windows 版:支持
微信 Mac 版:支持
功能描述
获取当前的地理位置、速度。当用户离开小程序后,此接口无法调用。开启高精度定位,接口耗时会增加,可指定 highAccuracyExpireTime 作为超时时间。地图相关使用的坐标格式应为 gcj02。 高频率调用会导致耗电,如有需要可使用持续定位接口 wx.onLocationChange
。 基础库 2.17.0
版本起 wx.getLocation
增加调用频率限制,相关公告。
使用方法
自 2022 年 7 月 14 日后发布的小程序,若使用该接口,需要在 app.json 中进行声明,否则将无法正常使用该接口,2022年7月14日前发布的小程序不受影响。具体规则见公告
申请开通
暂只针对如下类目的小程序开放,需要先通过类目审核,再在小程序管理后台,「开发」-「开发管理」-「接口设置」中自助开通该接口权限。 接口权限申请入口将于2022年3月11日开始内测,于3月31日全量上线。并从4月18日开始,在代码审核环节将检测该接口是否已完成开通,如未开通,将在代码提审环节进行拦截。
微信小程序获取定位关键方法 getLocation。因此需要在 app.json 中进行声明,后期小程序上线还需要单独申请getLocation 接口权限。
app.json 部分关键代码
{"pages": ["pages/index/index","pages/sign/sign"],"window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": "#fff","navigationBarTitleText": "Weixin","navigationBarTextStyle": "black"},"tabBar": {"custom": false,"backgroundColor": "#fefefe","color": "#999999","selectedColor": "#1C9D9D","list": [{"pagePath": "pages/index/index","text": "首页","iconPath": "/images/home.png","selectedIconPath": "/images/home_cur.png"},{"pagePath": "pages/sign/sign","text": "打卡","iconPath": "/images/day.png","selectedIconPath": "/images/day_cur.png"},{"pagePath": "pages/index/index","text": "我的","iconPath": "/images/my.png","selectedIconPath": "/images/my_cur.png"}]},"permission": {"scope.userLocation": {"desc": "您的位置信息将用于小程序考勤签到功能"}},"requiredPrivateInfos":["getLocation"],"style": "v2","sitemapLocation": "sitemap.json","lazyCodeLoading": "requiredComponents"
}
sign.wxml代码
部分UI代码参考苏苏就是小苏苏
<!-- 部分UI代码参考苏苏就是小苏苏 -->
<view class="index"><!-- 用户信息 --><view class="head "><view class="head_box flex-row" style="justify-content:left"><view class="user_ava"><open-data type="userAvatarUrl"></open-data></view><view><view class="user_name">DreamCoders <text>{{tip}}</text></view><view class="user_add">新的一天开始了,加油哦~</view></view><view class="mealBtn" bindtap="ToMealTap"><image src="/images/meal.png"></image><view class="mealText">{{is_meal==2 ? '已订':'订餐'}}</view></view></view></view><view class="contentBox"><!-- 打卡记录 --><view class="signRecord"><view class="signInfo">上班打卡<text class="text-green">{{record[0].times ? record[0].times : '未打卡'}}</text><view class="sign_address"><view class="">{{record[0].address ? record[0].address : '暂无打卡地址'}}</view></view></view><view class="signInfo">下班打卡<text class="text-green">{{record[1].times ? record[1].times : '未打卡'}}</text><view class="sign_address"><view class="">{{record[1].address ? record[1].address : '暂无打卡地址'}}</view></view></view></view><view class="dateInfo "><text>{{nowDate}} {{nowDay}}</text></view><!-- 打卡按钮 --><view class="c_clock flex-column"><view class="clock_time flex-column j_c {{status==1?'c1':''}} {{is_out==2 ? 'outArea' : ''}}" catchtap="signTap"><text>{{signType>0 ? "下班打卡" : "上班打卡"}}</text><text>{{now_time}}</text></view><view class="clock_time_over flex-column j_c {{status==1?'c2':''}}" catchtap="clockInStart"><text>已打卡</text><text>{{now_time_stop}}</text></view></view><!-- 打卡地址 --><view class="clock_address "><image src="/images/add0.png" class="add_icon" /><text>{{current_address}}</text></view><view class="refresh" catchtap="refreshAdd">刷新位置</view></view>
</view>
sign.js代码
具体业务逻辑根据实际情况改写
let qqMapSdk= require("../../utils/qqmap.js");
let util = require('../../utils/util.js')Page({/*** 页面的初始数据*/data: {signType:0,//0上班打卡 1下班打卡is_out:2,//1办公地点打卡 2外勤打卡is_meal:1,//1未定餐 2已订餐now_time: '',//当前时间nowDate:'',//当前年月日nowDay:'',//星期几tip:'',//提示 上午好、下午好current_address: '',//当前定位地址status: 0, //0未打卡 1已打卡latlng:[],//经纬度now_time_stop: '', //已打卡时间area:{},//考勤点多个record:[],//打卡记录},onLoad: function (options) {this.getCurrentTime();this.setData({now_time: this.getTime(),nowDate: util.formatTime(new Date()),nowDay: util.formatDay(new Date()),tip: util.formatSole(),})},onShow: function () {this.getLocation();this.setData({status:0,current_address:'',})},signTap() {var that = this;if (!that.data.current_address) {return wx.showToast({title: '未获取当前定位',icon: 'error'})}var list = that.data.record.concat({'times':that.data.now_time,'address':that.data.current_address});wx.vibrateLong();//手机震动提示that.getSignRecord();that.setData({status: 1, //已打卡record:list,now_time_stop: that.data.now_time,})console.log(list);console.log(that.data.record);wx.showToast({title: '打卡成功',icon: 'none'})},getCurrentTime: function () {var time = setInterval(() => {this.setData({now_time: this.getTime()})}, 1000)},getTime() {let dateTime = '';let hh = new Date().getHours()let mf = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() :new Date().getMinutes()let ss = new Date().getSeconds() < 10 ? '0' + new Date().getSeconds() :new Date().getSeconds()dateTime = hh + ':' + mf + ':' + ss;return dateTime;},// 请求获取定位授权getUserAuth: function () {return new Promise((resolve, reject) => {wx.authorize({scope: 'scope.userLocation'}).then(() => {resolve()}).catch(() => {let that = this;wx.getSetting({success: (res) => {if (res.authSetting['scope.userLocation'] != undefined && res.authSetting['scope.userLocation'] != true) {wx.showModal({title: '请求授权当前位置',content: '需要获取您的地理位置,请确认授权',success: function (res) {if (res.cancel) {wx.showToast({title: '拒绝授权',icon: 'none',duration: 1000})} else if (res.confirm) {wx.openSetting({success: function (dataAu) {if (dataAu.authSetting["scope.userLocation"] == true) {//再次授权,调用wx.getLocation的APIthat.getLocation();} else {wx.showToast({title: '授权失败',icon: 'none',duration: 1000})}}})}}})} else if (res.authSetting['scope.userLocation'] == undefined) {that.getLocation();} else {that.getLocation();}}})})})},getLocation: function () {const that = this// 实例化腾讯地图API核心类const QQMapWX = new qqMapSdk({key: '你申请的KEY'// KEY必填});//获取当前位置wx.getLocation({type: 'gcj02',success: function(res) {that.latitude = res.latitudethat.longitude = res.longitudeQQMapWX.reverseGeocoder({location: {latitude: res.latitude,longitude: res.longitude},success: function(res) {let address = res.result.address + res.result.formatted_addresses.recommend;that.getSignRecord();that.setData({current_address:address,latlng:[res.result.location.lat,res.result.location.lng]})},fail: function(res) {this.getUserAuth()wx.showToast({title: '获取定位失败,请打开手机定位,重新进入!',icon: 'none'});}})},})},// 刷新定位refreshAdd() {this.getLocation(),this.getSignRecord()},//处理打卡记录及判断打卡位置是否办公地点打卡getSignRecord: function () {var that = this;console.log(that.data.latlng);var distance = that.getDistance(that.data.latlng[0],that.data.latlng[1],31.370450,121.228252);if(distance < 200000000000000){that.setData({is_out:1,})}},//经纬度距离计算
getDistance:function (lat1, lng1, lat2, lng2, unit = false) {var radLat1 = lat1 * Math.PI / 180.0var radLat2 = lat2 * Math.PI / 180.0var a = radLat1 - radLat2var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0var 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 // EARTH_RADIUS;s = Math.round(s * 10000) / 10000 //输出为公里if (0) { //是否返回带单位if (s < 1) { //如果距离小于1km返回ms = s.toFixed(3)s = s * 1000 + "m"} else {s = s.toFixed(2)s = s + "km"}} else {s = s.toFixed(3)s = s * 1000}return s
},//订餐操作
ToMealTap:function (e) {wx.showToast({title: '订餐成功',icon: 'none'})this.setData({is_meal: 2,})
}})
sign.json 代码
{"usingComponents": {},"navigationBarTitleText": "考勤打卡"
}
sign.wxss 代码
page {height: calc(100% - 10px)
}.index {margin-top: 10px;background: #fff;min-height: 100%;
}.head {padding-bottom: 10rpx;border-bottom: 2rpx solid #E5E5E5;
}.head_box {padding: 26rpx 28rpx 8px;width: 750rpx;box-sizing: border-box;
}.user_ava {width: 116rpx;height: 116rpx;overflow: hidden;border-radius: 25%;margin-right: 32rpx;
}.user_name {font-size: 32rpx;font-weight: 600;color: #333333;margin-bottom: 18rpx;
}.user_name text {font-size: 24rpx;color: #999999;font-weight: 400;margin-left: 40rpx;
}.user_add {font-size: 28rpx;color: #3380F3;
}.contentBox {padding: 44rpx 28rpx;
}.signRecord{display: flex;flex-flow: row nowrap;justify-content: space-between;margin-top: 15px;
}.dateInfo{text-align: center;position: relative;top: 50px;font-size: 35rpx;
}.c_title {font-size: 28rpx;color: #666666;margin-bottom: 26rpx;
}.c_section .c_item {position: relative;font-size: 30rpx;font-weight: 600;color: #333333;padding-left: 40rpx;margin-bottom: 110rpx;
}.c_section text {color: #307CED;text-overflow: ellipsis;overflow: hidden;width: 80%;white-space: nowrap;
}.c_section .c_item::before {content: '';position: absolute;width: 18rpx;height: 18rpx;border: 2rpx solid #999999;left: 0;top: 50%;margin-top: -9rpx;border-radius: 50%;
}.c_section {position: relative;
}.c_section .c_item::after {content: '';position: absolute;width: 2rpx;height: 178rpx;background: #E6E6E6;left: 10rpx;top: 34rpx;
}.c_section view:last-child::after {display: none;
}.start_lo {position: absolute;top: 30px;left: -5px;
}.start_end {position: absolute;bottom: -108px;left: 20px;
}.c_clock {margin: 180rpx auto 0;width: 350rpx;height: 380rpx;perspective: 1500;-webkit-perspective: 1500;-moz-perspective: 1500;
}.clock_time {width: 350rpx;height: 350rpx;margin-bottom: 30rpx;position: absolute;transition: all 1s;backface-visibility: hidden;
}.clock_time::after {content: '';top: 0;left: 0;width: 350rpx;height: 350rpx;border-radius: 50%;position: absolute;z-index: 9;background: rgba(48, 124, 237, 0.08);animation: scale 1s infinite alternate-reverse;
}/* 已打卡 */
.clock_time_over {width: 350rpx;height: 350rpx;margin-bottom: 30rpx;border-radius: 50%;background: rgba(48, 124, 237, 0.08);position: absolute;transition: all 1s;backface-visibility: hidden;transform: rotateY(-180deg);
}.clock_time_over::after {position: absolute;z-index: 11;content: '';width: 320rpx;height: 320rpx;background: #C6CED9;border-radius: 50%;top: 50%;left: 50%;transform: translate(-50%, -50%);
}.clock_time_over text {position: relative;z-index: 13;color: #FFFFFF;
}.clock_time_over text:first-child {font-size: 36rpx;margin-bottom: 14rpx;
}.clock_time_over text:last-child {font-size: 28rpx;
}@keyframes scale {0% {transform: scale(1.1);}100% {transform: scale(1);}
}.clock_time::before {position: absolute;z-index: 11;content: '';width: 320rpx;height: 320rpx;background: rgb(48, 124, 237, 0.79);border-radius: 50%;top: 50%;left: 50%;transform: translate(-50%, -50%);
}.clock_time text {position: relative;z-index: 13;color: #FFFFFF;
}.clock_time text:first-child {font-size: 36rpx;margin-bottom: 14rpx;
}.clock_time text:last-child {font-size: 45rpx;
}.clock_address {text-align: center;font-size: 30rpx;color: #333333;width: 80%;margin: 20px auto;overflow:hidden; text-overflow:ellipsis;white-space:nowrap;
}.clock_address text {vertical-align: middle;
}.add_icon {width: 28rpx;height: 36rpx;margin-right: 16rpx;vertical-align: middle;
}.refresh {margin-top: 25px;color: #307CED;display: flex;align-items: center;justify-content: center;
}.now_location {font-size: 24rpx;color: #333333 !important;
}.upload_box {width: 260rpx;height: 180rpx;background: #F5F5F8;border-radius: 5rpx;
}.upload_box text {font-size: 20rpx;color: #999 !important;font-weight: 100;
}.camera_icon {width: 42rpx;height: 44rpx;margin-bottom: 10rpx;
}.clock_img {width: 100%;height: 100%;
}.del_icon {width: 32rpx;height: 32rpx;position: absolute;right: -4px;top: -11rpx;
}.ative::before {background: #307cedc9;border: 2rpx solid #307cedc9 !important;
}.c1 {transform: rotateY(180deg)
}.c1::after {animation: none !important;
}.c2 {transform: rotateY(0deg)
}.mealBtn{position: absolute;right: 15px;
}
.mealBtn image{width: 27px;height: 27px;
}
.mealText{font-size: 12px;color: #999999;
}.outArea::before{background: #f44336 !important;
}
.signInfo{width: 48%;height: 65px;background: #f1f1f1;padding: 10px;border-radius: 5px;
}
.signInfo text{float: inline-end;
}
.sign_address{display: flex;margin-top: 5px;
}.sign_address view{white-space: nowrap;text-overflow: ellipsis;overflow: hidden;font-size: 14px;margin-top: 1px !important;color: #5f5a5a;
}.text-green{color: green;
}
util.js 代码
function formatTime(date) {var year = date.getFullYear()var month = date.getMonth() + 1var day = date.getDate()return year + "年" + month + "月" + day + "日";
}
const formatDay = dates => {let _day = new Array('星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六');let date = new Date(dates);date.setDate(date.getDate());let day = date.getDay();return _day[day];
}
const formatSole = () => {let timeNow = new Date();let hours = timeNow.getHours();let text = ``;if (hours >= 0 && hours <= 6) {text = `深夜了,不要熬夜太久哟`;} else if (hours > 6 && hours <= 8) {text = `早上好`;} else if (hours > 8 && hours <= 10) {text = `上午好`;} else if (hours > 10 && hours <= 13) {text = `中午好`;} else if (hours > 13 && hours <= 17) {text = `下午好`;} else if (hours > 17 && hours <= 23) {text = `晚上好`;}return text;
}
module.exports = {formatTime: formatTime,formatDay: formatDay,formatSole: formatSole
}
源码地址:
https://gitee.com/iGaoWei/miniWxDemo