|
|
@ -15,28 +15,50 @@ |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- <view class="title">·我的打卡</view> |
|
|
|
<view class="rule">打卡规则</view> --> |
|
|
|
<view v-if="!isShowClasses"> |
|
|
|
<view class="title">·我的打卡</view> |
|
|
|
<view class="rule" @click="openRule">打卡规则</view> |
|
|
|
<view class="punch-box flex-column" :style="{backgroundColor: isCanPunch ? '#96684F' : ''}" @click="punch"> |
|
|
|
<view>上线打卡</view> |
|
|
|
{{currentTime}} |
|
|
|
</view> |
|
|
|
<!-- 未打卡 --> |
|
|
|
<view v-if="!punchSuccess"> |
|
|
|
<view class="punch-text">{{isCanPunch ? '您已进入打卡范围:' + userPunch.clock_address[nearestFenceIndex].title : '您当前不在可打卡范围内'}}</view> |
|
|
|
<view class="examine-btn flex-center" v-if="userPunch.is_open_examine">审批打卡</view> |
|
|
|
</view> |
|
|
|
<!-- 打卡成功 --> |
|
|
|
<view v-else> |
|
|
|
<view class="punch-text">打卡时间:{{currentTime.slice(0,5)}}</view> |
|
|
|
<view class="punch-text" style="margin: 0 auto;">{{timeSuccess}}s后自动进入排班页面</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<view class="title">·我的排班</view> |
|
|
|
<view v-else> |
|
|
|
<view class="title">·我的排班</view> |
|
|
|
<view class="type-box"> |
|
|
|
<view v-for="(item,index) in typeList" :key="index" |
|
|
|
:class="['type-item', {'type-active': index == typeIndex}]" @click="typeIndex = index;getList()"> |
|
|
|
{{item.title}} |
|
|
|
<view class="type-line" v-if="index == typeIndex"></view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 日历 --> |
|
|
|
<calendarVue :isShowLunar="true" :isCanChangeDate="userPunch.is_clock == 0 ? true : false" @changeDate="getDate"></calendarVue> |
|
|
|
|
|
|
|
<view class="type-box"> |
|
|
|
<view v-for="(item,index) in typeList" :key="index" |
|
|
|
:class="['type-item', {'type-active': index == typeIndex}]" @click="typeIndex = index;getList()"> |
|
|
|
<!-- 选择场次 --> |
|
|
|
<view v-for="(item,index) in typeIndex ? sessionLists : sessionList" :key="index" |
|
|
|
class="session-item flex-between"> |
|
|
|
{{item.title}} |
|
|
|
<view class="type-line" v-if="index == typeIndex"></view> |
|
|
|
<switch :checked="item.isChecked" color="#96684F" @change="switchChange(item,index)" /> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 日历 --> |
|
|
|
<calendarVue :isShowLunar="true" @changeDate="getDate"></calendarVue> |
|
|
|
|
|
|
|
<!-- 选择场次 --> |
|
|
|
<view v-for="(item,index) in typeIndex ? sessionLists : sessionList" :key="index" |
|
|
|
class="session-item flex-between"> |
|
|
|
{{item.title}} |
|
|
|
<switch :checked="item.isChecked" color="#96684F" @change="switchChange(item,index)" /> |
|
|
|
</view> |
|
|
|
<!-- 打卡规则 --> |
|
|
|
<uni-popup ref="popup" type="center" border-radius="10px" background-color="#fff"> |
|
|
|
<view v-html="formateRichText(ruleContent)" style="width: 90%;padding: 30rpx;border-radius: 10rpx;margin: auto;"></view> |
|
|
|
</uni-popup> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
@ -85,21 +107,24 @@ |
|
|
|
isChecked: false |
|
|
|
}], |
|
|
|
isAllChecked: false, |
|
|
|
// 打卡中心点经纬度 |
|
|
|
fenceCenter: [ |
|
|
|
{latitude: 31.267166, longitude: 120.632449}, // 丽丰广场麦当劳 |
|
|
|
{latitude: 31.266902, longitude: 120.630162}, // |
|
|
|
{latitude: 31.266841, longitude: 120.628526}, |
|
|
|
{latitude: 31.26576, longitude: 120.629256} |
|
|
|
], |
|
|
|
// 最近的打卡点索引 |
|
|
|
nearestFenceIndex: null, |
|
|
|
// 打卡点半径(单位:米) |
|
|
|
fenceRadius: 500, |
|
|
|
// 用户与打卡坐标的距离 |
|
|
|
distance: null, |
|
|
|
// 用于存储 watchPosition 的返回值,方便后续清除监听 |
|
|
|
watchId: null |
|
|
|
watchId: null, |
|
|
|
ruleContent: '', |
|
|
|
currentTime: '', |
|
|
|
timeInterval: null, |
|
|
|
isCanPunch: false, // 是否可打卡, |
|
|
|
punchSuccess: false, // 打卡成功 |
|
|
|
timeSuccess: 3, |
|
|
|
isShowClasses: true, // 是否显示排班表 |
|
|
|
userPunch: {}, |
|
|
|
myLonLat: { |
|
|
|
lng: '', |
|
|
|
lat: '' |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
onLoad() { |
|
|
@ -107,16 +132,76 @@ |
|
|
|
this.getList() |
|
|
|
}, |
|
|
|
onShow() { |
|
|
|
// 开始监听用户位置 |
|
|
|
this.watchLocation() |
|
|
|
// 获取用户打卡信息 |
|
|
|
this.Post({},'/api/Merchants/getGuideClockStatus').then(res => { |
|
|
|
this.userPunch = res.data |
|
|
|
|
|
|
|
if(this.userPunch.clock_address.length == 0) return; |
|
|
|
|
|
|
|
// 用户是否需要打卡、及打卡状态:0不需要打卡,1未打卡,2已打卡或审批打卡成功,3审批打卡审核中,4审批拒绝 |
|
|
|
switch (this.userPunch.is_clock){ |
|
|
|
case 0: |
|
|
|
this.isShowClasses = true |
|
|
|
break; |
|
|
|
case 1: |
|
|
|
this.isShowClasses = false |
|
|
|
break; |
|
|
|
case 2: |
|
|
|
this.isShowClasses = true |
|
|
|
break; |
|
|
|
case 3: |
|
|
|
this.isShowClasses = false |
|
|
|
break; |
|
|
|
case 4: |
|
|
|
this.isShowClasses = false |
|
|
|
break; |
|
|
|
default: |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
this.watchLocation(); // 开始监听用户位置 |
|
|
|
this.startTimeUpdate(); |
|
|
|
}) |
|
|
|
}, |
|
|
|
onHide() { |
|
|
|
// 页面隐藏时清除位置监听 |
|
|
|
if (this.watchId) { |
|
|
|
navigator.geolocation.clearWatch(this.watchId) |
|
|
|
} |
|
|
|
|
|
|
|
// 页面隐藏时清除时间更新定时器 |
|
|
|
if (this.timeInterval) { |
|
|
|
clearInterval(this.timeInterval); |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
// 点击打卡 |
|
|
|
punch() { |
|
|
|
if (this.nearestFenceIndex === null) { |
|
|
|
uni.showToast({ |
|
|
|
title: '您当前不在可打卡范围内,无法打卡', |
|
|
|
icon: 'none' |
|
|
|
}); |
|
|
|
return; |
|
|
|
} |
|
|
|
this.Post({ |
|
|
|
address_id: this.userPunch.clock_address[this.nearestFenceIndex].id, |
|
|
|
lon: this.myLonLat.lng, |
|
|
|
lat: this.myLonLat.lat |
|
|
|
}, '/api/Merchants/guideClockLocate').then(res => { |
|
|
|
if(res.code == 200) { |
|
|
|
this.punchSuccess = true |
|
|
|
clearInterval(this.timeInterval); // 打卡成功后清除时间定时器 |
|
|
|
let timer = setInterval(() => { |
|
|
|
this.timeSuccess--; |
|
|
|
if (this.timeSuccess == 0) { |
|
|
|
clearInterval(timer); |
|
|
|
this.isShowClasses = true |
|
|
|
} |
|
|
|
}, 1000); |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
// 监听用户位置变化 |
|
|
|
watchLocation() { |
|
|
|
if (navigator.geolocation) { |
|
|
@ -124,22 +209,23 @@ |
|
|
|
(position) => { |
|
|
|
let coords = position.coords; |
|
|
|
let [gcj02Lng, gcj02Lat] = this.wgs84ToGcj02(coords.longitude, coords.latitude); |
|
|
|
console.log('自身坐标',gcj02Lat,gcj02Lng) |
|
|
|
// 计算用户与每个打卡点的距离,找出最近的打卡点 |
|
|
|
let minDistance = Infinity; |
|
|
|
let nearestIndex = null; |
|
|
|
this.fenceCenter.forEach((item,index) => { |
|
|
|
const distance = this.calculateDistance(item.latitude,item.longitude, gcj02Lat, gcj02Lng) |
|
|
|
console.log(distance) |
|
|
|
if (distance < minDistance) { |
|
|
|
minDistance = distance; |
|
|
|
nearestIndex = index; |
|
|
|
this.myLonLat = { |
|
|
|
lng: gcj02Lng, |
|
|
|
lat: gcj02Lat |
|
|
|
} |
|
|
|
// 初始化是否可打卡为 false |
|
|
|
this.isCanPunch = false; |
|
|
|
// 遍历每个打卡点 |
|
|
|
for (let i = 0; i < this.userPunch.clock_address.length; i++) { |
|
|
|
const item = this.userPunch.clock_address[i]; |
|
|
|
const distance = this.calculateDistance(item.latitude, item.longitude, gcj02Lat, gcj02Lng); |
|
|
|
console.log(`与打卡点 ${item.title} 的距离:`, distance); |
|
|
|
if (distance <= item.range) { |
|
|
|
this.isCanPunch = true; |
|
|
|
this.nearestFenceIndex = i; |
|
|
|
break; // 找到满足条件的打卡点后结束循环 |
|
|
|
} |
|
|
|
}) |
|
|
|
this.distance = minDistance; |
|
|
|
this.nearestFenceIndex = nearestIndex; |
|
|
|
console.log('用户与最近打卡点之间的距离',this.distance) |
|
|
|
console.log('最近的打卡点索引', this.nearestFenceIndex); |
|
|
|
} |
|
|
|
}, |
|
|
|
(error) => { |
|
|
|
let locationStatus = '' |
|
|
@ -283,28 +369,50 @@ |
|
|
|
}) |
|
|
|
}, |
|
|
|
// 获取当前日期 |
|
|
|
getNowTime(time, type) { |
|
|
|
var date = time, |
|
|
|
year = date.getFullYear(), |
|
|
|
month = date.getMonth() + 1, |
|
|
|
day = date.getDate(), |
|
|
|
hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(), |
|
|
|
minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(), |
|
|
|
second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds(); |
|
|
|
month >= 1 && month <= 9 ? (month = "0" + month) : ""; |
|
|
|
day >= 0 && day <= 9 ? (day = "0" + day) : ""; |
|
|
|
if (type == 1) { |
|
|
|
if (uni.getSystemInfoSync().platform == 'ios') { |
|
|
|
var timer = year + '/' + month + '/' + day + ' ' + hour + ':' + minute + ':' + second; |
|
|
|
} else { |
|
|
|
var timer = year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second; |
|
|
|
} |
|
|
|
} else { |
|
|
|
var timer = year + '/' + month + '/' + day; |
|
|
|
} |
|
|
|
formatDate(date, format = 'YYYY/MM/DD') { |
|
|
|
const year = date.getFullYear(); |
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0'); |
|
|
|
const day = String(date.getDate()).padStart(2, '0'); |
|
|
|
const hour = String(date.getHours()).padStart(2, '0'); |
|
|
|
const minute = String(date.getMinutes()).padStart(2, '0'); |
|
|
|
const second = String(date.getSeconds()).padStart(2, '0'); |
|
|
|
|
|
|
|
return timer; |
|
|
|
return format |
|
|
|
.replace('YYYY', year) |
|
|
|
.replace('MM', month) |
|
|
|
.replace('DD', day) |
|
|
|
.replace('HH', hour) |
|
|
|
.replace('mm', minute) |
|
|
|
.replace('ss', second); |
|
|
|
}, |
|
|
|
getNowTime(time, type) { |
|
|
|
const format = type === 1 ? (uni.getSystemInfoSync().platform === 'ios' ? 'YYYY/MM/DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ss') : 'YYYY/MM/DD'; |
|
|
|
return this.formatDate(time, format); |
|
|
|
}, |
|
|
|
// 打卡规则 |
|
|
|
openRule() { |
|
|
|
this.Post({ |
|
|
|
id: 7 |
|
|
|
},'/api/article/getArticleById').then(res => { |
|
|
|
this.ruleContent = res.data.content |
|
|
|
this.$refs.popup.open() |
|
|
|
}) |
|
|
|
}, |
|
|
|
// 开始更新时间 |
|
|
|
startTimeUpdate() { |
|
|
|
this.updateTime(); |
|
|
|
this.timeInterval = setInterval(() => { |
|
|
|
this.updateTime(); |
|
|
|
}, 1000); |
|
|
|
}, |
|
|
|
// 更新时间 |
|
|
|
updateTime() { |
|
|
|
const now = new Date(); |
|
|
|
const hours = String(now.getHours()).padStart(2, '0'); |
|
|
|
const minutes = String(now.getMinutes()).padStart(2, '0'); |
|
|
|
const seconds = String(now.getSeconds()).padStart(2, '0'); |
|
|
|
this.currentTime = `${hours}:${minutes}:${seconds}`; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
@ -337,6 +445,39 @@ |
|
|
|
text-align: right; |
|
|
|
} |
|
|
|
|
|
|
|
.punch-box { |
|
|
|
width: 267rpx; |
|
|
|
height: 267rpx; |
|
|
|
background: #CCCCCC; |
|
|
|
box-shadow: 0rpx 0rpx 29rpx 0rpx rgba(153,153,153,0.6); |
|
|
|
border-radius: 50%; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
font-weight: bold; |
|
|
|
font-size: 32rpx; |
|
|
|
color: #FFFFFF; |
|
|
|
margin: 200rpx auto 0; |
|
|
|
} |
|
|
|
|
|
|
|
.punch-text { |
|
|
|
font-weight: bold; |
|
|
|
font-size: 30rpx; |
|
|
|
color: #111111; |
|
|
|
margin: 40rpx auto; |
|
|
|
text-align: center; |
|
|
|
} |
|
|
|
|
|
|
|
.examine-btn { |
|
|
|
width: 257rpx; |
|
|
|
height: 80rpx; |
|
|
|
border-radius: 13rpx; |
|
|
|
border: 1rpx solid #96684F; |
|
|
|
font-weight: 500; |
|
|
|
font-size: 31rpx; |
|
|
|
color: #96684F; |
|
|
|
margin: 0 auto; |
|
|
|
} |
|
|
|
|
|
|
|
.nav-item { |
|
|
|
font-weight: 500; |
|
|
|
font-size: 32rpx; |
|
|
|