nxdt-uniapp/pages/attendanceManagement/index.vue

437 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<Navbar title="考勤打卡" :showBack="false" />
<div class="content">
<ProSelect v-if="this.userType != '00'" @selectPro="selectPro" />
<u-row :gutter="20">
<u-col span="12">
<div class="top-cont">
<div class="name">{{ name }}</div>
<div class="calendar" @click="handleCalendar">
<u-icon name="calendar" color="#2979ff" size="21"></u-icon>
<span>{{ isShow ? '考勤统计' : '考勤打卡' }}</span>
</div>
</div>
</u-col>
</u-row>
<div v-if="isShow">
<u-row :gutter="20" justify="center">
<u-col span="6">
<div class="col-item">
<div>上班 {{ attendanceTime }}</div>
<div class="time" v-if="clockIn">
<u-icon name="checkmark-circle-fill" color="#2979ff" style="margin-right: 6px"></u-icon>
{{ clockIn }}
</div>
<div class="time" v-else>未打卡</div>
</div>
</u-col>
<u-col span="6">
<div class="col-item">
<div>下班 {{ arriveTime }}</div>
<div class="time" v-if="clockOut">
<u-icon name="checkmark-circle-fill" color="#2979ff" style="margin-right: 6px"></u-icon>
{{ clockOut }}
</div>
<div class="time" v-else>未打卡</div>
</div>
</u-col>
</u-row>
<div class="clock-in-item" :class="{ isInLocation: isInLocation }" @click="isConfirm">
<div v-if="isInLocation">考勤打卡</div>
<div v-else>不在打卡范围内</div>
<div class="now-time">{{ nowTime }}</div>
</div>
<div class="location">
<u-icon name="map"></u-icon>
{{ locationSuccess ? location : '获取位置失败, 请开启系统权限后再尝试' }}
</div>
</div>
<div v-else>
<ren-calendar ref="ren" :markDays="markDays" @onDayClick="onDayClick"></ren-calendar>
<div style="margin: 20px 0; display: flex">
上班打卡{{ clockIn }}
<u-text v-if="!clockIn && isInClockTime" type="error" text="未打卡"></u-text>
</div>
<div style="display: flex">
下班打卡:{{ clockOut }}
<u-text v-if="!clockOut && isInClockTime" type="error" text="未打卡"></u-text>
</div>
</div>
</div>
<Tabbar />
</view>
</template>
<script>
import RenCalendar from '@/components/ren-calendar/ren-calendar.vue'
import ProSelect from 'pages/component/Proselect'
import { getAttendanceTime, attendancePunch } from '@/api/checkInAdmin'
export default {
components: {
RenCalendar,
ProSelect
},
data() {
return {
userId: uni.getStorageSync('userInfo').constructionId,
userType: uni.getStorageSync('userInfo').userType,
proId: '',
isShow: true,
name: uni.getStorageSync('userInfo').nickName,
// type: uni.getStorageSync('phone-platform') == 'ios' ? 'wgs84' : 'gcj02',
attendanceTime: '-', // 上班时间
arriveTime: '-', // 下班时间
// 上班卡时间戳限制 14:00:00前都是上班卡 获取14:00:00前的时间戳
clockInTimeLimit: new Date(new Date().toLocaleDateString()).getTime() + 14 * 60 * 60 * 1000,
// 当前时间的时间戳
nowTimeLimit: new Date().getTime(),
clockIn: '', //上班打卡时间
clockOut: '', //下班打卡时间
clockInStatus: '',
nowTime: '', //当前时间
location: '', //当前位置
isInLocation: false, //是否在打卡范围内
lat: '', //纬度
lon: '', //经度
// 打卡规定地址范围
clockInLocation: [],
clockInDistance: 3000, // 考勤距离
currentId: '',
curDate: '', //当前日期
today: '', //今天日期
isInClockTime: true, // 是否在打卡时间内
markDays: [], //标记日期
// 获取位置成功
locationSuccess: false
}
},
created() {
this.getNowTime()
this.getToday()
this.updateNowTime()
this.getLocation()
console.log('时间戳-上班', this.clockInTimeLimit)
console.log('时间戳-当前', this.nowTimeLimit)
},
methods: {
// 获取打卡信息
async getAttendanceInfo() {
const params = {
time: this.today,
userId: this.userId,
proId: this.proId
}
console.log('参数', params)
const res = await getAttendanceTime(params)
console.log('getAttendanceTime', res)
if (res.data) {
this.clockInLocation = res.data.addressList || '-'
this.attendanceTime = res.data.upperWorkTime || '-'
this.arriveTime = res.data.belowWorkTime
this.clockIn = res.data.goWorkTime || ''
this.clockOut = res.data.offWorkTime || ''
setTimeout(() => {
this.isInClockInLocation()
}, 200)
} else {
this.clockInLocation = []
this.isInLocation = false
}
},
// 选择工程
selectPro(pro) {
console.log('🚀 ~ selectPro ~ 选择工程:', pro)
this.proId = pro.proId
setTimeout(() => {
this.getAttendanceInfo()
}, 200)
},
// 获取当前时间
getNowTime() {
const date = new Date()
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
const second = String(date.getSeconds()).padStart(2, '0')
this.nowTime = `${hour}:${minute}:${second}`
},
// 获取今天日期
getToday() {
const date = new Date()
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
this.today = `${year}-${month}-${day}`
},
// 更新当前时间
updateNowTime() {
setInterval(() => {
this.getNowTime()
}, 1000)
},
// 获取当前位置
getLocation() {
uni.getLocation({
// type: this.type,
type: 'wgs84', // type: wgs84 返回 gps 坐标gcj02
isHighAccuracy: true,
altitude: true,
success: res => {
this.locationSuccess = true
console.log('getLocation', res)
this.lat = res.latitude
this.lon = res.longitude
// if (res.address) {
// this.location = res.address.province + res.address.city + res.address.district + res.address.street
// }
this.handleLocation()
},
fail: err => {
this.locationSuccess = false
console.log('getLocation', err, this.locationSuccess)
uni.showToast({
title: '获取位置失败',
icon: 'none'
})
}
})
},
// 处理位置
handleLocation() {
uni.request({
url: `https://api.map.baidu.com/reverse_geocoding/v3/?ak=PM43nB8eDNTBrXkQwGrTQFcmOni3Z9nO&output=json&coordtype=wgs84ll&location=${this.lat},${this.lon}`,
method: 'GET',
success: res => {
console.log('解析的地址', res)
this.location = res.data.result.formatted_address
}
})
},
// 计算 当前位置 是否在clockInLocation[] 中任意一项的打卡范围内 默认范围为 500m
isInClockInLocation() {
if (this.clockInLocation.length == 0) return
this.clockInLocation.forEach(item => {
let distance = this.getDistance(this.lat, this.lon, item.lat, item.lon)
console.log('🚀 ~ isInClockInLocation ~ distance:', distance)
if (this.type == 'gcj02') {
distance = distance - 700
} else {
distance = distance - 1000
}
console.log('🚀 ~ isInClockInLocation ~ distance-计算后:', distance)
if (distance <= this.clockInDistance) {
if (this.distance < 0) this.distance = 0
this.isInLocation = true
this.currentId = item.id
return
}
})
},
// 计算两点之间的距离
getDistance(lat1, lon1, lat2, lon2) {
const radLat1 = this.deg2rad(lat1)
const radLat2 = this.deg2rad(lat2)
const a = radLat1 - radLat2
const b = this.deg2rad(lon1) - this.deg2rad(lon2)
let 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 // 地球半径(单位:公里)
s = Math.round(s * 10000) / 10 // 保留一位小数
return s
},
deg2rad(deg) {
return (deg * Math.PI) / 180
},
// 切换考勤打卡和考勤统计
handleCalendar() {
console.log('跳转到考勤统计页面')
this.isShow = !this.isShow
this.getToday()
this.getAttendanceInfo()
if (!this.isShow) {
setTimeout(() => {
this.markDays = []
let today = this.$refs.ren.getToday().date
this.curDate = today
this.markDays.push(today)
}, 300)
}
},
onDayClick(data) {
console.log('🚀 ~ onDayClick ~ data:', data)
if (new Date(data.date).getTime() > new Date().getTime()) {
this.isInClockTime = false
} else {
this.isInClockTime = true
}
this.curDate = data.date
this.today = data.date
this.getAttendanceInfo()
},
// 是否确认打下班卡
parseTime(timeStr) {
const [hours, minutes, seconds] = timeStr.split(':').map(Number)
const date = new Date()
date.setHours(hours, minutes, seconds, 0)
return date
},
isConfirm() {
if (!this.isInLocation) {
uni.showToast({
title: '不在打卡范围内',
icon: 'none'
})
return
}
if (this.clockIn) {
const now = this.parseTime(this.nowTime)
const clockIn = this.parseTime(this.clockIn)
const sixtyMinutesLater = new Date(clockIn.getTime() + 60 * 60000) // 考勤时间加上 60 分钟
if (now < sixtyMinutesLater) {
uni.showModal({
title: '提示',
content: '是否确认打下班卡?',
showCancel: true,
cancelText: '取消',
confirmText: '确认',
success: res => {
if (res.confirm) {
console.log('用户点击确认')
this.handleClockIn()
} else {
console.log('用户点击取消')
}
}
})
} else {
this.handleClockIn()
}
} else {
this.handleClockIn()
}
},
// 打卡
async handleClockIn() {
try {
let attendanceType = ''
if (this.nowTimeLimit < this.clockInTimeLimit && !this.clockIn) {
attendanceType = '1'
} else if (this.nowTimeLimit < this.clockInTimeLimit && this.clockIn) {
attendanceType = '2'
} else if (this.nowTimeLimit > this.clockInTimeLimit) {
attendanceType = '2'
}
console.log('打卡')
// 开启loading
uni.showLoading({
title: '打卡中...',
icon: 'loading', // loading icon有哪几种: 1. none 2. success 3. loading
mask: true // 是否显示透明蒙层,防止触摸穿透
})
const params = {
userId: this.userId,
proId: this.proId,
attendanceType,
time: this.nowTime,
punchLocation: this.currentId
}
console.log('🚀 ~ 打卡参数 ~ params:', params)
const res = await attendancePunch(params)
console.log('🚀 ~ handleClockIn ~ res:', res)
this.getAttendanceInfo()
uni.hideLoading()
uni.showToast({
title: '打卡成功',
icon: 'success',
duration: 1500
})
} catch (error) {
console.log('🚀 ~ handleClockIn ~ error:', error)
}
}
}
}
</script>
<style lang="scss">
.content {
padding: 0 20px;
white-space: pre-wrap;
word-wrap: break-word;
}
.top-cont {
margin-bottom: 20px;
display: flex;
justify-content: space-between;
padding: 15px;
background-color: #f5f5f5;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
.name {
display: flex;
align-items: center;
font-size: 19px;
font-weight: 800;
color: #333;
}
.calendar {
font-size: 12px;
display: flex;
flex-direction: column;
align-items: center;
}
}
.col-item {
padding: 10px;
background-color: #f5f5f5;
border-radius: 10px;
font-size: 15px;
line-height: 1.8;
color: #333;
.time {
display: flex;
font-size: 12px;
color: #999;
}
}
.clock-in-item {
margin: 36% auto 30px;
width: 100px;
height: 100px;
padding: 10px;
background: linear-gradient(135deg, #a4c1fa, #adebe3);
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
font-size: 16px;
font-weight: 800;
line-height: 1.8;
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.now-time {
font-size: 12px;
}
// 不在打卡范围内 背景置灰
&:not(.isInLocation) {
background: #909399;
font-size: 14px;
}
}
.location {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: #333;
}
</style>