Browse Source

新增外勤打卡和统计日历样式修改

htc 1 month ago
parent
commit
038edfb735

+ 6 - 0
pages.json

@@ -276,6 +276,12 @@
 					"style": {
 						"navigationStyle": "custom"
 					}
+				},
+				{
+					"path": "fieldService",
+					"style": {
+						"navigationStyle": "custom"
+					}
 				}
 			]
 		},

+ 4 - 4
pages/login/wxLogin.vue

@@ -27,7 +27,7 @@
 					let url = '';
 					let roleCodes = JSON.parse(uni.getStorageSync('userInfo'))?.roleCodes;
 					if(roleCodes.length==0||!roleCodes) url = '/pagesClockin/index';
-					else if(roleCodes.includes('Maintenance')) url = '/pages/operation/index';
+					else if(roleCodes.includes('Maintenance')||roleCodes.includes('Inspection')) url = '/pages/operation/index';
 					else if(roleCodes.includes('inventory')||roleCodes.includes('Admin')||roleCodes.includes('Warehouse Manager')) url = '/pagesStorage/home';
 					if(!url) return this.$showToast('无菜单角色,请后台查看!')
 					uni.reLaunch({ url })
@@ -54,15 +54,15 @@
 									headUrl:res.data.data.headUrl,
 									roleCodes:res.data.data.roleCodes
 								}));
-								uni.hideLoading();
-								that.$showToast('登录成功');
 								
 								let url = '';
 								let roleCodes = res.data.data.roleCodes;
 								if(roleCodes.length==0) url = '/pagesClockin/index';
-								else if(roleCodes.includes('Maintenance')) url = '/pages/operation/index';
+								else if(roleCodes.includes('Maintenance')||roleCodes.includes('Inspection')) url = '/pages/operation/index';
 								else if(roleCodes.includes('inventory')||roleCodes.includes('Warehouse Manager')) url = '/pagesStorage/home';
 								if(!url) return that.$showToast('无菜单角色,请后台查看!')
+								uni.hideLoading();
+								that.$showToast('登录成功');
 								setTimeout(()=>{
 									uni.reLaunch({ url })
 								},1500)

+ 3 - 3
pages/operation/index.vue

@@ -7,11 +7,11 @@
 				<div class="top">
 					<div class="left">
 						<p>本月巡检次数</p>
-						<p>{{xjData.abnormalNum||0}}/{{xjData.normalNum||0}}</p>
+						<p>{{xjData.normalNum||0}}/{{xjData.abnormalNum||0}}</p>
 					</div>
 					<div class="right">
-						<p>异常:<span>{{xjData.abnormalNum||0}}</span></p>
-						<p>正常:<span>{{xjData.normalNum||0}}</span></p>
+						<p>异常:<span>{{xjData.normalNum||0}}</span></p>
+						<p>正常:<span>{{xjData.abnormalNum||0}}</span></p>
 					</div>
 				</div>
 				<div class="bottom">

+ 259 - 0
pagesClockin/fieldService.vue

@@ -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>

+ 12 - 5
pagesClockin/index.vue

@@ -34,12 +34,14 @@
 				</div>
 			</div>
 			<div class="c_clock">
-				<div class="cc_box" :style="{'background-image':'url('+imgBase+(canClock?'clockingin/clock_active_bg.png':'clockingin/clock_inactive_bg.png')+')'}" @tap="clock">
+			<!-- 	<div class="cc_box" :style="{'background-image':'url('+imgBase+(canClock?'clockingin/clock_active_bg.png':'clockingin/clock_inactive_bg.png')+')'}" @tap="clock"> -->
+				<div class="cc_box" :style="{'background-image':'url('+imgBase+'clockingin/clock_active_bg.png)'}" @tap="clock">
 					<p>{{isSW?'上班':'下班'}}打卡</p>
 					<p class="time">{{currentSFM}}</p>
 				</div>
 				<div class="cc_location">
-					<image :src="imgBase+(canClock?'clockingin/location_active.png':'clockingin/location_inactive.png')"></image>
+					<!-- <image :src="imgBase+(canClock?'clockingin/location_active.png':'clockingin/location_inactive.png')"></image> -->
+					<image :src="imgBase+'clockingin/location_active.png'"></image>
 					<span v-if="canClock">已进入考勤范围:谷锐特设备自动化有限公司</span>
 					<span v-else>当前定位不在考勤范围内</span>
 				</div>
@@ -86,8 +88,8 @@
 				clockTime:'',
 				distance: null,
 				targetLocation: { //谷锐特 31.962084,117.020446  /跨境电商大厦 31.865786,117.15297
-					latitude: 31.962084,
-					longitude: 117.020446
+					latitude: 31.865786,
+					longitude: 117.15297
 				},
 				userInfo:null
 			}
@@ -145,10 +147,15 @@
 				this.isSW =  new Date().getHours()>12?false:true;
 			},
 			clock(){
-				if(!this.canClock) return
 				let url = this.isSW?'/wms/outsourced/attendance/checkIn':'/wms/outsourced/attendance/clockOut';
 				let params = {outUserId:this.userInfo?.id};
 				params[this.isSW?'checkInTime':'clockOutTime'] = new Date().Format('hh:mm');
+				if(!this.canClock){
+					uni.navigateTo({
+						url:`/pagesClockin/fieldService?url=${url}&outUserId=${this.userInfo?.id}&isSW=${this.isSW?1:0}`
+					})
+					return
+				} 
 				this.$api.post(url,params).then(res=>{
 					if(res.data.code!==0) return this.$showToast(res.data.msg);
 					this.clockTime = new Date().Format('hh:mm');

+ 2 - 2
pagesOperation/record/add.vue

@@ -113,14 +113,14 @@
 					{key:'inspector',tip:'请输入巡检负责人'},
 					{key:'startDate',tip:'请选择巡检时间'},
 					// {key:'status',tip:'请选择巡检结果'},
-					{key:'remark',tip:'请输入备注'},
+					// {key:'remark',tip:'请输入备注'},
 					// {key:'inspectionFile',tip:'请选择巡检打卡图片'}
 				]
 			}
 		},
 		onLoad() {
 			let d = new Date();
-			this.minDate = new Date(d.getFullYear()-1,d.getMonth()+1,d.getDate()).getTime();
+			this.minDate = new Date(d.getFullYear(),d.getMonth(),1).getTime();
 			this.maxDate = new Date().getTime();
 			this.getOrderList();
 			this.getCustomerList();

+ 1 - 1
pagesOperation/repair/index.vue

@@ -147,7 +147,7 @@
 		},
 		onLoad() {
 			let d = new Date();
-			this.minDate = new Date(d.getFullYear()-1,d.getMonth()+1,d.getDate()).getTime();
+			this.minDate = new Date(d.getFullYear(),d.getMonth()-1,d.getDate()).getTime();
 			this.maxDate = new Date().getTime();
 			this.getOrderList();
 			this.getCustomerList();

+ 6 - 6
pagesOperation/workorder/index.vue

@@ -39,9 +39,9 @@
 		</template>
 		<u-picker :itemHeight="88" :show="show1" :columns="columns1" keyName="label" title="故障类型"
 			@cancel="show1=false" @confirm="e=>confirm(e,1)" :immediateChange="true"></u-picker>
-		<u-datetime-picker :itemHeight="88" :show="show2" v-model="queryParams.month" mode="date" title="巡检时间"
+		<u-datetime-picker :itemHeight="88" :show="show2" v-model="queryParams.month" mode="date" title="故障时间"
 			@cancel="show2=false" @confirm="e=>confirm(e,2)" :immediateChange="true" :minDate="minDate" :maxDate="maxDate"></u-datetime-picker>
-		<u-picker :itemHeight="88" :show="show3" :columns="columns3" title="巡检地点"
+		<u-picker :itemHeight="88" :show="show3" :columns="columns3" title="报修地点"
 			@cancel="show3=false" @confirm="e=>confirm(e,3)" :immediateChange="true"></u-picker>
 	</view>
 </template>
@@ -56,8 +56,8 @@
 			return {
 				sidx:0,
 				typeText:'故障类型',
-				timeText:'巡检时间',
-				placeText:'巡检地点',
+				timeText:'故障时间',
+				placeText:'报修地点',
 				queryParams:{
 					page:1,
 					limit:10,
@@ -113,8 +113,8 @@
 			init(){
 				this.sidx = 0;
 				this.typeText = '故障类型';
-				this.timeText = '巡检时间';
-				this.placeText = '巡检地点';
+				this.timeText = '故障时间';
+				this.placeText = '报修地点';
 				this.queryParams = {
 					page:1,
 					limit:10,

+ 5 - 1
pagesStatistics/components/CusCalendar/index.vue

@@ -62,6 +62,8 @@
 						else if(t.isLeave===1||t.isLeave===2||t.isLeave===3) status = 1;
 						this.$set(this.calendarList[i],'status',status);
 						this.$set(this.calendarList[i],'id',t?.id);
+						this.$set(this.calendarList[i],'file',t?.file);
+						this.$set(this.calendarList[i],'position',t?.position);
 					} 
 					else this.$set(this.calendarList[i],'status',2);
 				})
@@ -114,7 +116,9 @@
 					workHours:0,
 					sbTime:'',
 					xbTime:'',
-					status:0
+					status:0,
+					file:pre.file,
+					position:pre.position
 				}
 				if(pre.id){
 					let res = await this.$api.get('/wms/outsourced/attendance/'+pre?.id||'');

+ 75 - 29
pagesStatistics/index.vue

@@ -31,24 +31,44 @@
 				<image :src="imgBase+'clockingin/time_line.png'"></image>
 				<div class="ct_info">
 					<div class="cti_pre" v-if="info.sbTime">
-						<div class="ctip_left">上班 {{info.sbTime}}</div>
-						<div class="ctip_right">
-							<image :src="imgBase+'clockingin/location_inactive.png'"></image>
-							<span>{{'谷锐特设备自动化有限公司'}}</span>
+						<div class="ctip_left">
+							<div class="text">
+								上班 {{info.sbTime}}
+								<div class="wc">外出</div>
+							</div>
+							<div class="addr">
+								<image :src="imgBase+'clockingin/location_inactive.png'"></image>
+								<span>{{info.position||'谷锐特设备自动化有限公司'}}</span>
+							</div>
+						</div>
+						<div class="ctip_right" v-if="info.file">
+							<image :src="info.file"></image>
 						</div>
 					</div>
 					<div class="cti_pre" v-else>
-						<div class="ctip_left">未打卡</div>
+						<div class="ctip_left">
+							<div class="text">未打卡</div>
+						</div>
 					</div>
 					<div class="cti_pre" v-if="info.xbTime">
-						<div class="ctip_left">下班 {{info.xbTime}}</div>
-						<div class="ctip_right">
-							<image :src="imgBase+'clockingin/location_inactive.png'"></image>
-							<span>{{'谷锐特设备自动化有限公司'}}</span>
+						<div class="ctip_left">
+							<div class="text">
+								下班 {{info.xbTime}}
+								<div class="wc">外出</div>
+							</div>
+							<div class="addr">
+								<image :src="imgBase+'clockingin/location_inactive.png'"></image>
+								<span>{{info.position||'谷锐特设备自动化有限公司'}}</span>
+							</div>
+						</div>
+						<div class="ctip_right" v-if="info.file">
+							<image :src="info.file"></image>
 						</div>
 					</div>
 					<div class="cti_pre" v-else>
-						<div class="ctip_left">未打卡</div>
+						<div class="ctip_left">
+							<div class="text">未打卡</div>
+						</div>
 					</div>
 				</div>
 			</div>
@@ -232,38 +252,64 @@
 				align-items: center;
 				&>image{
 					width: 10rpx;
-					height: 82rpx;
+					height: 155rpx;
 				}
 				.ct_info{
+					width: 100%;
 					margin-left: 10rpx;
 					.cti_pre{
 						display: flex;
 						align-items: center;
+						justify-content: space-between;
 						&:last-child{
 							margin-top: 48rpx;
 						}
 						.ctip_left{
-							font-family: PingFang-SC, PingFang-SC;
-							font-weight: bold;
-							font-size: 24rpx;
-							color: #1D2129;
-							line-height: 24rpx;
+							.text{
+								font-family: PingFang-SC, PingFang-SC;
+								font-weight: bold;
+								font-size: 32rpx;
+								color: #1D2129;
+								line-height: 32rpx;
+								position: relative;
+								.wc{
+									width: 64rpx;
+									height: 42rpx;
+									border: 4rpx solid #2E69EB;
+									font-family: PingFang-SC, PingFang-SC;
+									font-weight: bold;
+									font-size: 24rpx;
+									color: #2E69EB;
+									line-height: 42rpx;
+									text-align: center;
+									position: absolute;
+									left: 180rpx;
+									top: 50%;
+									margin-top: -21rpx;
+								}
+							}
+							.addr{
+								display: flex;
+								align-items: center;
+								margin-top: 27rpx;
+								image{
+									width: 24rpx;
+									height: 24rpx;
+								}
+								span{
+									font-family: PingFangSC, PingFang SC;
+									font-weight: 400;
+									font-size: 24rpx;
+									color: #86909C;
+									line-height: 24rpx;
+									margin-left: 8rpx;
+								}
+							}
 						}
 						.ctip_right{
-							display: flex;
-							align-items: center;
-							margin-left: 20rpx;
 							image{
-								width: 24rpx;
-								height: 24rpx;
-							}
-							span{
-								font-family: PingFangSC, PingFang SC;
-								font-weight: 400;
-								font-size: 24rpx;
-								color: #86909C;
-								line-height: 24rpx;
-								margin-left: 8rpx;
+								width: 80rpx;
+								height: 80rpx;
 							}
 						}
 					}