|  | @@ -0,0 +1,259 @@
 | 
	
		
			
				|  |  | +<template>
 | 
	
		
			
				|  |  | +	<view class="page" :style="{'min-height':h+'px', 'padding-top':mt+'px'}">
 | 
	
		
			
				|  |  | +		<cus-header title='外勤打卡'></cus-header>
 | 
	
		
			
				|  |  | +		<div class="map">
 | 
	
		
			
				|  |  | +			<map id="mapContainer" class="map" :latitude="latitude" :longitude="longitude" :markers="markers"
 | 
	
		
			
				|  |  | +				:scale="18" show-location></map>
 | 
	
		
			
				|  |  | +		</div>
 | 
	
		
			
				|  |  | +		<div class="box">
 | 
	
		
			
				|  |  | +			<div class="title">打卡位置</div>
 | 
	
		
			
				|  |  | +			<div class="place">{{address}}</div>
 | 
	
		
			
				|  |  | +			<div class="building">{{nearestBuilding}}附近</div>
 | 
	
		
			
				|  |  | +			<div class="title" style="margin-top: 64rpx;">外出签到拍照</div>
 | 
	
		
			
				|  |  | +			<div class="photo">
 | 
	
		
			
				|  |  | +				<u-upload width="180" height="180"
 | 
	
		
			
				|  |  | +					:fileList="fileList"
 | 
	
		
			
				|  |  | +				    @afterRead="afterRead"
 | 
	
		
			
				|  |  | +				    @delete="deletePic"
 | 
	
		
			
				|  |  | +				    :maxCount="1"
 | 
	
		
			
				|  |  | +					:previewFullImage="true"
 | 
	
		
			
				|  |  | +				></u-upload>
 | 
	
		
			
				|  |  | +			</div>
 | 
	
		
			
				|  |  | +			<div class="btn" @tap="confirm">确认外勤打卡</div>
 | 
	
		
			
				|  |  | +		</div>
 | 
	
		
			
				|  |  | +	</view>
 | 
	
		
			
				|  |  | +</template>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<script>
 | 
	
		
			
				|  |  | +	const baseApi = require('@/http/baseApi.js')
 | 
	
		
			
				|  |  | +	const QQMapWX = require('../static/lib/qqmap-wx-jssdk.min.js');
 | 
	
		
			
				|  |  | +	const qqmapsdk = new QQMapWX({
 | 
	
		
			
				|  |  | +		key: '2JCBZ-MTMWQ-4N55Z-2F3SP-W72A7-YGB7L'
 | 
	
		
			
				|  |  | +	});
 | 
	
		
			
				|  |  | +	export default {
 | 
	
		
			
				|  |  | +		data() {
 | 
	
		
			
				|  |  | +			return {
 | 
	
		
			
				|  |  | +				url:'',
 | 
	
		
			
				|  |  | +				outUserId:'',
 | 
	
		
			
				|  |  | +				isSW:'',
 | 
	
		
			
				|  |  | +				latitude: '',
 | 
	
		
			
				|  |  | +				longitude: '',
 | 
	
		
			
				|  |  | +				markers: [],
 | 
	
		
			
				|  |  | +				nearestBuilding: '正在定位...',
 | 
	
		
			
				|  |  | +				address: '正在获取地址...',
 | 
	
		
			
				|  |  | +				mapCtx: null,
 | 
	
		
			
				|  |  | +				fileList: []
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		onLoad(option) {
 | 
	
		
			
				|  |  | +			this.url = option.url;
 | 
	
		
			
				|  |  | +			this.outUserId = option.outUserId;
 | 
	
		
			
				|  |  | +			this.isSW = option.isSW;
 | 
	
		
			
				|  |  | +			// 初始化地图上下文
 | 
	
		
			
				|  |  | +			this.mapCtx = wx.createMapContext('mapContainer', this);
 | 
	
		
			
				|  |  | +			// 初始化定位
 | 
	
		
			
				|  |  | +			this.initLocation();
 | 
	
		
			
				|  |  | +		},
 | 
	
		
			
				|  |  | +		methods: {
 | 
	
		
			
				|  |  | +			deletePic(event) {
 | 
	
		
			
				|  |  | +				this.fileList.splice(event.index, 1);
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			// 新增图片
 | 
	
		
			
				|  |  | +			async afterRead(event) {
 | 
	
		
			
				|  |  | +				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
 | 
	
		
			
				|  |  | +				let lists = [].concat(event.file);
 | 
	
		
			
				|  |  | +				let fileListLen = this.fileList.length;
 | 
	
		
			
				|  |  | +				lists.map((item) => {
 | 
	
		
			
				|  |  | +					this.fileList.push({
 | 
	
		
			
				|  |  | +						...item,
 | 
	
		
			
				|  |  | +						status: "uploading",
 | 
	
		
			
				|  |  | +						message: "上传中",
 | 
	
		
			
				|  |  | +					});
 | 
	
		
			
				|  |  | +				});
 | 
	
		
			
				|  |  | +				for (let i = 0; i < lists.length; i++) {
 | 
	
		
			
				|  |  | +					const result = await this.uploadFilePromise(lists[i].url);
 | 
	
		
			
				|  |  | +					let item = this.fileList[fileListLen];
 | 
	
		
			
				|  |  | +					this.fileList.splice(
 | 
	
		
			
				|  |  | +						fileListLen,
 | 
	
		
			
				|  |  | +						1,
 | 
	
		
			
				|  |  | +						Object.assign(item, {
 | 
	
		
			
				|  |  | +							status: "success",
 | 
	
		
			
				|  |  | +							message: "",
 | 
	
		
			
				|  |  | +							url: result,
 | 
	
		
			
				|  |  | +						})
 | 
	
		
			
				|  |  | +					);
 | 
	
		
			
				|  |  | +					fileListLen++;
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			uploadFilePromise(url) {
 | 
	
		
			
				|  |  | +				return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  | +				  let a = uni.uploadFile({
 | 
	
		
			
				|  |  | +					url: baseApi.BaseApi + '/uploadFile',
 | 
	
		
			
				|  |  | +					filePath: url,
 | 
	
		
			
				|  |  | +					name: "file",
 | 
	
		
			
				|  |  | +					formData: {
 | 
	
		
			
				|  |  | +					  user: "test",
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +					success: (res) => {
 | 
	
		
			
				|  |  | +						let data = JSON.parse(res.data);
 | 
	
		
			
				|  |  | +						if(data){
 | 
	
		
			
				|  |  | +							if(data.code!==0) return
 | 
	
		
			
				|  |  | +							setTimeout(() => {
 | 
	
		
			
				|  |  | +								resolve(data.data);
 | 
	
		
			
				|  |  | +							}, 1000);
 | 
	
		
			
				|  |  | +						}
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +				  });
 | 
	
		
			
				|  |  | +				});
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			// 初始化定位
 | 
	
		
			
				|  |  | +			initLocation() {
 | 
	
		
			
				|  |  | +				uni.authorize({
 | 
	
		
			
				|  |  | +					scope: 'scope.userLocation',
 | 
	
		
			
				|  |  | +					success: () => {
 | 
	
		
			
				|  |  | +						this.getLocation();
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +					fail: () => {
 | 
	
		
			
				|  |  | +						uni.showModal({
 | 
	
		
			
				|  |  | +							title: '定位权限申请',
 | 
	
		
			
				|  |  | +							content: '需要获取您的位置信息',
 | 
	
		
			
				|  |  | +							success: (res) => {
 | 
	
		
			
				|  |  | +								if (res.confirm) {
 | 
	
		
			
				|  |  | +									uni.openSetting();
 | 
	
		
			
				|  |  | +								}
 | 
	
		
			
				|  |  | +							}
 | 
	
		
			
				|  |  | +						})
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				})
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			getLocation() {
 | 
	
		
			
				|  |  | +				uni.getLocation({
 | 
	
		
			
				|  |  | +					type: 'gcj02',
 | 
	
		
			
				|  |  | +					altitude: true,
 | 
	
		
			
				|  |  | +					success: (res) => {
 | 
	
		
			
				|  |  | +						this.latitude = res.latitude;
 | 
	
		
			
				|  |  | +						this.longitude = res.longitude;
 | 
	
		
			
				|  |  | +						this.updateMarker();
 | 
	
		
			
				|  |  | +						this.getAddressInfo();
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +					fail: (err) => {
 | 
	
		
			
				|  |  | +						uni.showToast({
 | 
	
		
			
				|  |  | +							title: '定位失败',
 | 
	
		
			
				|  |  | +							icon: 'none'
 | 
	
		
			
				|  |  | +						})
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				})
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			updateMarker() {
 | 
	
		
			
				|  |  | +				this.markers = [{
 | 
	
		
			
				|  |  | +					id: 1,
 | 
	
		
			
				|  |  | +					latitude: this.latitude,
 | 
	
		
			
				|  |  | +					longitude: this.longitude,
 | 
	
		
			
				|  |  | +					// iconPath: '/static/location.png',
 | 
	
		
			
				|  |  | +					width: 30,
 | 
	
		
			
				|  |  | +					height: 30
 | 
	
		
			
				|  |  | +				}]
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			getAddressInfo() {
 | 
	
		
			
				|  |  | +				qqmapsdk.reverseGeocoder({
 | 
	
		
			
				|  |  | +					location: {
 | 
	
		
			
				|  |  | +						latitude: this.latitude,
 | 
	
		
			
				|  |  | +						longitude: this.longitude
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +					success: (res) => {
 | 
	
		
			
				|  |  | +						const result = res.result;
 | 
	
		
			
				|  |  | +						this.nearestBuilding = result.address_reference?.building?.title || result
 | 
	
		
			
				|  |  | +							.address_reference?.street?.title || '未知建筑';
 | 
	
		
			
				|  |  | +						this.address = result.address;
 | 
	
		
			
				|  |  | +					},
 | 
	
		
			
				|  |  | +					fail: (err) => {
 | 
	
		
			
				|  |  | +						console.error('逆地理编码失败:', err)
 | 
	
		
			
				|  |  | +					}
 | 
	
		
			
				|  |  | +				})
 | 
	
		
			
				|  |  | +			},
 | 
	
		
			
				|  |  | +			confirm() {
 | 
	
		
			
				|  |  | +				if(this.fileList.length===0) return this.$showToast('请先外出签到拍照')
 | 
	
		
			
				|  |  | +				let params = { outUserId:this.outUserId };
 | 
	
		
			
				|  |  | +				params[this.isSW==1?'checkInTime':'clockOutTime'] = new Date().Format('hh:mm');
 | 
	
		
			
				|  |  | +				params.file = this.fileList[0].url;
 | 
	
		
			
				|  |  | +				params.position = this.address;
 | 
	
		
			
				|  |  | +				this.$api.post(this.url,params).then(res=>{
 | 
	
		
			
				|  |  | +					if(res.data.code!==0) return this.$showToast(res.data.msg);
 | 
	
		
			
				|  |  | +					this.$showToast('外出签到成功')
 | 
	
		
			
				|  |  | +					setTimeout(()=>{
 | 
	
		
			
				|  |  | +						uni.reLaunch({
 | 
	
		
			
				|  |  | +							url:'/pagesClockin/index'
 | 
	
		
			
				|  |  | +						})
 | 
	
		
			
				|  |  | +					},1500)
 | 
	
		
			
				|  |  | +				})
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +</script>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +<style scoped lang="less">
 | 
	
		
			
				|  |  | +	.page {
 | 
	
		
			
				|  |  | +		background: #FFFFFF;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		.map {
 | 
	
		
			
				|  |  | +			width: 100%;
 | 
	
		
			
				|  |  | +			height: 640rpx;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		.box {
 | 
	
		
			
				|  |  | +			width: 100%;
 | 
	
		
			
				|  |  | +			background: #fff;
 | 
	
		
			
				|  |  | +			padding: 48rpx 29rpx;
 | 
	
		
			
				|  |  | +			box-sizing: border-box;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			.title {
 | 
	
		
			
				|  |  | +				font-family: PingFang-SC, PingFang-SC;
 | 
	
		
			
				|  |  | +				font-weight: bold;
 | 
	
		
			
				|  |  | +				font-size: 36rpx;
 | 
	
		
			
				|  |  | +				color: #1D2129;
 | 
	
		
			
				|  |  | +				line-height: 36rpx;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			.place {
 | 
	
		
			
				|  |  | +				font-family: PingFangSC, PingFang SC;
 | 
	
		
			
				|  |  | +				font-weight: 400;
 | 
	
		
			
				|  |  | +				font-size: 28rpx;
 | 
	
		
			
				|  |  | +				color: #1D2129;
 | 
	
		
			
				|  |  | +				line-height: 28rpx;
 | 
	
		
			
				|  |  | +				margin-top: 30rpx;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			.building {
 | 
	
		
			
				|  |  | +				font-family: PingFangSC, PingFang SC;
 | 
	
		
			
				|  |  | +				font-weight: 400;
 | 
	
		
			
				|  |  | +				font-size: 28rpx;
 | 
	
		
			
				|  |  | +				color: #86909C;
 | 
	
		
			
				|  |  | +				line-height: 28rpx;
 | 
	
		
			
				|  |  | +				padding-top: 20rpx;
 | 
	
		
			
				|  |  | +				border-top: 1rpx solid #EFEFEF;
 | 
	
		
			
				|  |  | +				margin-top: 20rpx;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			.photo {
 | 
	
		
			
				|  |  | +				width: 180rpx;
 | 
	
		
			
				|  |  | +				height: 180rpx;
 | 
	
		
			
				|  |  | +				margin-top: 36rpx;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +			.btn {
 | 
	
		
			
				|  |  | +				width: 100%;
 | 
	
		
			
				|  |  | +				height: 88rpx;
 | 
	
		
			
				|  |  | +				background: #2E69EB;
 | 
	
		
			
				|  |  | +				border-radius: 16rpx;
 | 
	
		
			
				|  |  | +				font-family: PingFang-SC, PingFang-SC;
 | 
	
		
			
				|  |  | +				font-weight: bold;
 | 
	
		
			
				|  |  | +				font-size: 32rpx;
 | 
	
		
			
				|  |  | +				color: #FFFFFF;
 | 
	
		
			
				|  |  | +				line-height: 88rpx;
 | 
	
		
			
				|  |  | +				text-align: center;
 | 
	
		
			
				|  |  | +				letter-spacing: 2rpx;
 | 
	
		
			
				|  |  | +				margin-top: 168rpx;
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +</style>
 |