uniapp 实现打卡功能

uniapp实现打卡功能

记录一下,已备不时之需

实现思路(核心):

1.页面加载调用 uni.getLocation接口 获取当前位置(经纬度)
2.调用uni.chooseLocation接口选择打卡地点,然后计算两个经纬度相差的距离,距离在设定的检查范围内,则视为打卡成功。
3.保存打卡信息。
<template>
	<view class="container">
		<privacy :showPrivacy="true" />
		<view class="head">
			<view class="head-left">拜访对象</view>
			<view class="head-right">
				<u-search placeholder="请选择拜访对象" v-model="searchName" actionText="选择" :animation="false"
					@custom='searchClick' @search='searchClick'></u-search>
				<!-- 				<image class="right" :src="$previewImage('uniapp/business/static/right2.png')" mode=""></image> -->
			</view>
		</view>
		<view class="body">

			<view class="imgView" @click="checkIn">
				<view class="daka-text">{{countdown}}</view>
				<view class="daka-tips">请在规定时间内完成打卡</view>
				<image class="dakaImg" :src="$previewImage('uniapp/business/static/daka.png')" mode="aspectFit"></image>
			</view>
			<view class="addr">
				<image class="addr-img" :src="$previewImage('uniapp/business/static/ads.png')" mode="aspectFit">
					<view class="addr-text" @click="getChooseLocation">{{name}}</view>
			</view>

		</view>
		<view class="foot">
			<view class="common_btn">
				<u-button color="#ff3f2a" shape="circle" text="返回" @click="goBack"></u-button>
			</view>
			<u-picker :show="pickerShow" :columns="columns" @cancel="pickerShow = false" @confirm="confirm"
				keyName="name"></u-picker>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				totalSeconds: 180, // 假设倒计时60秒
				countdown: '打卡', // 初始倒计时时间
				timer: null, // 计时器
				pickerShow: false,
				columns: [],
				checkDistance: 1000, //签到范围距离(米)
				// 当前经纬度
				locationLatitude: null,
				locationLongitude: null,
				// 选择的经纬度
				chooseLatitude: null,
				chooseLongitude: null,
				address: null,
				name: "请选择地址",
				checkStatus: true, // 打卡按钮状态
				btnText: "打卡",
				searchName: "", // 搜索数据
				// 选择的拜访对象
				objectId: null,
				objectName: null,
				objectType: null,
			}
		},
		onReady() {

		},
		onShow() {
			// 启动计时器
			this.startCountdown();
		},
		onReachBottom() {

		},
		onLoad(option) {
			wx.getPrivacySetting({
				success: res => {
					console.log(
							res
							) // 返回结果为: res = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' }
					if (res.needAuthorization) {
						// 需要弹出隐私协议
						this.setData({
							showPrivacy: true
						})
					} else {
						// 用户已经同意过隐私协议,所以不需要再弹出隐私协议,也能调用隐私接口
					}
				},
				fail: () => {},
				complete: () => {}
			})
			this.getLocation();
		},
		onUnload(e) {

		},
		methods: {
			// 加载选项
			loadingColumns() {
				let that = this;
				this.$request.request({
					url: "/crmClockIn/getVisitObject",
					data: {
						userId: uni.getStorageSync('user').id,
						name: this.searchName
					},
					method: "POST"
				}).then(res => {
					if (res.errorCode == 0) {
						that.columns = [res.data];
						that.pickerShow = true;
					}
				})
			},
			// 回调参数为包含columnIndex、value、values
			confirm(e) {
				let value = e.value[0];
				if (value) {
					this.objectId = value.id;
					this.objectName = value.name;
					this.objectType = value.type;
					this.searchName = value.name;
				}
				this.pickerShow = false;
			},
			// 计算经纬度距离
			getDistance(lat1, lng1, lat2, lng2) {
				lat1 = lat1 || 0;
				lng1 = lng1 || 0;
				lat2 = lat2 || 0;
				lng2 = lng2 || 0;

				var rad1 = lat1 * Math.PI / 180.0;
				var rad2 = lat2 * Math.PI / 180.0;
				var a = rad1 - rad2;
				var b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
				var r = 6378137;
				// 计算出来的是米数
				var distance = r * 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(rad1) * Math.cos(rad2) *
					Math.pow(Math.sin(b / 2), 2)));
				// 返回计算好的距离(m单位)
				return distance;
			},
			getLocation() {
				console.log("默认当前位置");
				let that = this;
				uni.getLocation({
					type: 'gcj02',
					highAccuracy: true,
					success(res) {
						console.log("getLocation响应", res);
						// 纬度
						that.locationLatitude = res.latitude
						// 经度
						that.locationLongitude = res.longitude
					},
					fail: (err) => {
						console.log("接口调用失败", err);
					},
					complete: (res) => {}
				})
			},
			getChooseLocation() {
				console.log("选择地址");
				let that = this;
				this.name = "请选择地址";
				uni.chooseLocation({
					success(res) {
						console.log(res);
						if (res.errMsg == "chooseLocation:ok") {
							if (res.name == null || res.name == '') {
								uni.showModal({
									title: '提示',
									content: "请选择地址",
									showCancel: false,
									success: function(res) {
										if (res.confirm) {
											// 执行确认后的操作
										} else {
											// 执行取消后的操作
										}
									}
								})
								return;
							}
							// 判断选取的经纬度和当前经纬度的距离
							let distance = that.getDistance(res.latitude, res.longitude, that.locationLatitude,
								that.locationLongitude);
							console.log(that.checkDistance, "打卡范围");
							console.log(distance, "实际距离");
							if (distance > that.checkDistance) {
								uni.showModal({
									title: '提示',
									content: "选择地址距离当前位置过远,无法打卡,请重新选择",
									showCancel: false,
									success: function(res) {
										if (res.confirm) {
											// 执行确认后的操作
										} else {
											// 执行取消后的操作
										}
									}
								})
								return;
							}
							that.chooseLatitude = res.latitude; // 经度
							that.chooseLongitude = res.longitude; // 纬度
							that.address = res.address; // 地址
							that.name = res.name; // 名称
						} else {
							console.log("调用失败!")
						}
					},
					fail: function() {}
				})
			},
			getSetting() {
				uni.getSetting({
					success: function(res) {
						var statu = res.authSetting;
						if (!statu['scope.userLocation']) {
							uni.showModal({
								title: '是否授权当前位置',
								content: '需要获取您的地理位置,请确认授权,否则地图功能将无法使用',
								success: function(tip) {
									if (tip.confirm) {
										uni.openSetting({
											success: function(data) {
												if (data.authSetting[
														"scope.userLocation"] ===
													true) {
													uni.showToast({
														title: '授权成功',
														icon: 'success',
														duration: 1000
													})
												} else {
													uni.showToast({
														title: '授权失败',
														icon: 'success',
														duration: 1000
													})
												}
											}
										})
									}
								}
							})
						}
					},
					fail: function(res) {
						uni.showToast({
							title: '调用授权窗口失败',
							icon: 'success',
							duration: 1000
						})
					}
				})
			},
			check(){
				// 打卡按钮状态检测
				if (!this.checkStatus) {
					return false;
				}
				// 判断是否选择拜访对象
				if (!this.objectName) {
					uni.showModal({
						title: '提示',
						content: "请选择拜访对象!",
						showCancel: false,
						success: function(res) {
							if (res.confirm) {
								// 执行确认后的操作
							} else {
								// 执行取消后的操作
							}
						}
					})
					return false;
				}
				if (!this.chooseLatitude || !this.chooseLongitude ||!this.address || !this.name) {
					uni.showModal({
						title: '提示',
						content: "请选择地址!",
						showCancel: false,
						success: function(res) {
							if (res.confirm) {
								// 执行确认后的操作
							} else {
								// 执行取消后的操作
							}
						}
					})
					return false;
				}
				return true;
			},
			checkIn() {
				let flag = this.check();
				console.log("打卡校验",flag);
				if(!flag){
					console.log("打卡校验未通过!");
					return;
				}
				
				this.$request.request({
					url: "/crmClockIn/save",
					data: {
						user_id: uni.getStorageSync('user').id,
						name: this.name,
						address: this.address,
						latitude: this.chooseLatitude,
						longitude: this.chooseLongitude,
						business_type: this.objectType,
						business_id: this.objectId,
						business_name:this.objectName
					},
					method: "POST"
				}).then(res => {
					uni.showModal({
						title: '提示',
						content: "打卡成功!",
						showCancel: false,
						success: function(res) {
							if (res.confirm) {
								uni.navigateBack();
							} else {
								uni.navigateBack();
							}
						}
					})
				})
			},
			searchClick(e) {
				this.loadingColumns();
			},
			// 计时器
			startCountdown() {
				this.clearCountdown(); // 清除之前的计时器
				this.timer = setInterval(() => {
					if (this.totalSeconds <= 0) {
						this.checkStatus = false;
						this.clearCountdown();
						this.countdown = "不可打卡";
						this.goBack();
						// 倒计时结束的操作
					} else {
						this.totalSeconds -= 1;
						this.countdown = this.formatTime(this.totalSeconds);
					}
				}, 1000);
			},
			clearCountdown() {
				if (this.timer) {
					clearInterval(this.timer);
					this.timer = null;
				}
			},
			formatTime(seconds) {
				let m = Math.floor(seconds / 60); // 分钟
				let s = seconds % 60; // 秒
				let sum = m * 60 + s;
				return sum + "s"; // 格式化输出
			},
			goBack() {
				uni.navigateBack();
			},
		}
	}
</script>
<style lang="scss">
	.container,
	page {
		background-color: #fff;
		height: 100%;
	}

	.container {
		display: flex;
		flex-direction: column;
		justify-content: space-between;
	}

	.head {
		display: flex;
		flex-direction: row;
		justify-content: space-between;
		padding: 10px 20rpx;
		align-items: center;
	}

	.imgView {
		display: flex;
		align-items: center;
		justify-content: center;
		margin-bottom: 40rpx;
	}

	.body {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		width: 100%;
		height: 100%;
		flex: 1;
	}

	.dakaImg {
		height: 400rpx;
		width: 400rpx;
	}

	.common_btn {
		width: 80%;
		margin: 0 auto;
	}

	.foot {
		padding-bottom: 10%;
	}

	.right {
		width: 35px;
		height: 35px;
	}

	.daka-text {
		position: absolute;
		color: #fff;
		font-size: 50rpx;
		font-weight: bold;

	}

	.daka-tips {
		position: absolute;
		margin-top: 100rpx;
		font-size: 20rpx;
		color: #ffffff7a;
	}

	.addr {
		display: flex;
		color: #999999;
		padding: 0px 40rpx;
	}

	.addr-img {
		height: 40rpx;
		width: 40rpx;
		margin-right: 10rpx;
	}

	.head-right {
		display: flex;
		align-items: center;
		font-size: 26rpx;
		color: #BDBDBD;
	}

	.head-left::before {
		content: "*";
		color: red;
		margin-right: 4px;
	}

	.addr-text {
		color: #409eff;
	}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值