757 lines
20 KiB
Vue
Raw Normal View History

2022-06-08 15:48:09 +08:00
<template>
2024-12-04 18:37:56 +08:00
<view class="fullHeight">
<headers :showBack="true" :themeType="'white'">
<view class="headerName"> 考勤打卡 </view>
</headers>
<!-- 打卡 -->
<view v-if="!tabActive">
<view class="box personInfoBox">
<image v-if="fieldAcquisitionUrl" :src="url_config + 'image/' + fieldAcquisitionUrl" class="profile_photo"></image>
<image v-else src="/static/profile_photo.png" class="profile_photo"></image>
<view class="info">
<view class="personName">
{{ realName }}
</view>
<view class="groupName"> {{ enterpriseName }}&nbsp;&nbsp;{{ teamName }} </view>
</view>
</view>
<view class="box" :style="{ height: screenHeight - statusBarHeight - 44 - 105 - 15 - 30 - 50 + 'px' }">
<view class="top">
<view class="item">
<view class="type"> 进场 </view>
<view class="time" v-if="attendenceData.enterTime">
<uni-icons2 class="backImg" type="checkbox-filled" size="14" color="#5181F6"></uni-icons2>
{{ enterTime }} 已打卡
</view>
<view class="time" v-else> 未打卡 </view>
</view>
<view class="item">
<view class="type"> 出场 </view>
<view class="time" v-if="attendenceData.outTime">
<uni-icons2 class="backImg" type="checkbox-filled" size="14" color="#5181F6"></uni-icons2>
{{ outTime }}已打卡
</view>
<view class="time" v-else> 未打卡 </view>
</view>
</view>
<view class="bottom">
<view class="">
<view class="circleBox" :class="canClick ? '' : 'grey'" @click="checkInOpen">
<view class="name"> 考勤打卡 </view>
<view class="time">
{{ nowTime }}
</view>
</view>
<view class="address" v-if="canClick">
<uni-icons2 class="backImg" type="checkbox-filled" size="14" color="#707070"></uni-icons2>已进入考勤范围{{
electricFence.addr
}}
</view>
<view class="address" v-else>
<uni-icons2 class="backImg" type="location-filled" size="14" color="#707070"></uni-icons2>未进入考勤区域
</view>
</view>
</view>
</view>
</view>
<!-- 统计 -->
<view v-else>
<view class="box flex-c" :style="{ height: screenHeight - statusBarHeight - 44 - 15 - 30 - 50 + 'px' }">
<view class="personInfoBox" style="padding-bottom: 24px">
<image v-if="fieldAcquisitionUrl" :src="url_config + 'image/' + fieldAcquisitionUrl" class="profile_photo"></image>
<image v-else src="/static/profile_photo.png" class="profile_photo"></image>
<view class="info">
<view class="personName">
{{ realName }}
</view>
<view class="groupName"> {{ enterpriseName }}&nbsp;&nbsp;{{ teamName }} </view>
</view>
</view>
<view class="calendarBox">
<scroll-view scroll-y="true" style="height: 100%">
<view class="calendarStatus">
<view class="item" v-for="item in clockOptions">
<view class="circle" :style="{ backgroundColor: item.color }"></view>
<text class="name">{{ item.name }}</text>
</view>
</view>
<view :class="{ calendar: true, hide: openIcon === 'down' }">
<uni-calendar
class="uni-calendar--hook"
:selected="selected"
:showMonth="false"
@change="dayChange"
@monthSwitch="monthChange"
/>
</view>
<!-- 展开 收起 -->
<view class="open" @click="handleOpen">
<view class="line"></view>
<uni-icons2 class="ic" :type="openIcon" size="20" color="#CCCCCC"></uni-icons2>
<view class="line"></view>
</view>
<view class="detail">
<view class="info">
当日班次
{{ attendenceRule.ruleName || '固定上下班' }}
{{ attendenceRule.startTime || '9:00' }}-{{ attendenceRule.endTime || '18:00' }}
</view>
<view class="info" v-if="attendanceInfo.length">出勤统计打卡{{ attendanceInfo.length }}</view>
<u-time-line>
<u-time-line-item v-for="item in attendanceInfo" :key="item.createTime">
<template v-slot:content>
<view class="u-wrap">
<view class="left">
<view class="u-order-time"> {{ item.passType === 1 ? '进场' : '出场' }} {{ item.createTime.split(' ')[1] }} </view>
<view class="u-order-desc">
<image class="img" src="@/static/checkIn/position.png" />
{{ item.address }}
</view>
</view>
<view class="u-image">
<image
class="image"
:src="url_config + 'image/' + item.imageUrl"
@click="previewImage(url_config + 'image/' + item.imageUrl)"
></image>
</view>
</view>
</template>
</u-time-line-item>
</u-time-line>
<view class="info" v-if="attendanceInfo.length"
>统计截至{{ attendanceInfo.length && attendanceInfo[attendanceInfo.length - 1].createTime }}</view
>
<view class="noData" v-if="!attendanceInfo.length">
<image src="@/static/checkIn/nodata.png"></image>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
<u-tabbar :value="tabActive" @change="handleChangeTab" :list="tabList" activeColor="#4496fc" inactiveColor="#707070"></u-tabbar>
<!-- 确认打卡弹框 -->
<u-popup v-model="checkInShow" :round="10" mode="bottom" @close="checkInClose" closeable>
<view class="popup_content">
<text class="popup_title">打卡备注</text>
<view class="form_item flex">
<view class="name">请选择</view>
<u-radio-group v-model="checkInform.type">
<u-radio :name="1"> 进场 </u-radio>
<u-radio :name="2"> 出场 </u-radio>
</u-radio-group>
</view>
<view class="form_item flex">
<view class="name">{{ phoneClockImageType ? '人脸识别' : '上传图片' }}</view>
<image
class="p_image"
v-if="checkInform.photoUrl"
:src="url_config + 'image/' + checkInform.photoUrl"
@click="uploadImage"
></image>
<image class="p_image" v-else src="@/static/checkIn/camera.png" @click="uploadImage"></image>
</view>
<u-button type="primary" @click="clockIn">打卡</u-button>
</view>
</u-popup>
</view>
2022-06-08 15:48:09 +08:00
</template>
<script>
2025-01-04 17:20:45 +08:00
import { handleAuthLocation } from "@/common/permissionTips"
2022-06-08 15:48:09 +08:00
import uniIcons from "@/components/uni-icons/uni-icons.vue"
export default {
components:{uniIcons},
data() {
return {
statusBarHeight:0,
screenHeight:667,
personDetail:{
fieldAcquisitionUrl:''
},
canClick:false,
projectDetail:{},
electricFence:{},
attendenceData:{
enterTime: null,
outTime: null
},
currentTime:'',
photoUrl:'',
timer:null,
realName:'',
phoneClockImageType:0,
faceScore:0,
timer: '', //定义一个定时器
nowTime: '',
2024-12-04 18:37:56 +08:00
2022-06-08 15:48:09 +08:00
}
},
computed:{
enterTime:function(){
if(this.attendenceData.enterTime){
var a = this.attendenceData.enterTime.split(' ')[1]
return a
}else{
return null
}
},
outTime:function(){
if(this.attendenceData.outTime){
var a = this.attendenceData.outTime.split(' ')[1]
return a
}else{
return null
}
}
},
created() {
this.getTime()
},
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer)
}
},
2022-06-08 15:48:09 +08:00
onLoad() {
this.statusBarHeight=uni.getStorageSync('systemInfo').statusBarHeight
this.screenHeight=uni.getStorageSync('systemInfo').screenHeight
this.projectDetail=JSON.parse(uni.getStorageSync('projectDetail'))
var userInfo=JSON.parse(uni.getStorageSync('userInfo'))
this.workerId = userInfo.workerId
this.realName=userInfo.realName
this.GetLOcation()
this.getAttendenceData()
this.getNowTime()
this.getAttendenceTypeFn()
},
onShow(){
if(this.phoneClockImageType==1&&this.photoUrl){
this.addPhoneAttendanceFn()
}
},
methods: {
//判断是否需要人脸识别
getAttendenceTypeFn(){
var that = this
this.sendRequest({
url: 'xmgl/projectConfig/getProjectConfigList',
data: {projectSn:this.projectDetail.projectSn},
method: "POST",
success(res){
if(res.result){
that.phoneClockImageType=res.result[0].phoneClockImageType
that.faceScore=res.result[0].faceScore
}
}
})
},
getTime() {
this.timer = setInterval(() => {
let timeDate = new Date()
let year = timeDate.getFullYear()
let mounth = timeDate.getMonth() + 1
let day = timeDate.getDate()
let hours = timeDate.getHours()
hours = hours >= 10 ? hours : '0' + hours
let minutes = timeDate.getMinutes()
minutes = minutes >= 10 ? minutes : '0' + minutes
let seconds = timeDate.getSeconds()
seconds = seconds >= 10 ? seconds : '0' + seconds
let week = timeDate.getDay()
let weekArr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
this.nowTime = ` ${hours}:${minutes}:${seconds}`
}, 1000)
},
2022-06-08 15:48:09 +08:00
//获取考勤信息
getAttendenceData(){
var that = this
this.sendRequest({
url: 'xmgl/workerAttendance/getPersonAttendanceTime',
data: {workerId:this.workerId},
method: "POST",
success(res){
console.log('getAttendenceData',res)
that.attendenceData=res.result
}
})
},
GetLOcation: function() {
var that = this;
uni.getLocation({
type: 'gcj02',
success: (res) => {
console.log('success',res)
var latitude = parseFloat(res.latitude);
var longitude = parseFloat(res.longitude);
that.isInArea(longitude,latitude)
},
fail(res) {
console.log('fail',res)
// uni.showModal({
// title:'提示',
// content:res.errMsg
// })
2025-01-04 17:20:45 +08:00
handleAuthLocation('需要获取当前位置用于考勤打卡请允许此App获取此设备的位置信息!')
2022-06-08 15:48:09 +08:00
}
});
},
// 校验坐标位置是否在考勤区域中
isInArea(longitude,latitude){
var that = this
this.sendRequest({
url: 'xmgl/electricFence/checkLocation',
data: {longitude:longitude,projectSn:this.projectDetail.projectSn,latitude:latitude,workerId:this.workerId},
method: "POST",
hideLoading:true,
success(res){
console.log('isInArea',res)
if(res.result.checkType==0){
console.log('不在考勤范围内')
that.canClick=false
}else{
console.log('在考勤范围内')
that.canClick=true
that.electricFence=res.result.electricFence
}
}
})
},
//手机打卡
addPhoneAttendanceFn(){
var that = this
var type = 1
if(this.attendenceData.enterTime){
type=2
}
this.sendRequest({
url: 'xmgl/workerAttendance/addPhoneAttendance',
data: {workerId:this.workerId,photoUrl:this.photoUrl,type:type},
method: "POST",
success(res){
console.log('addPhoneAttendanceFn',res)
that.getAttendenceData()
}
})
},
clockIn(){
if(!this.canClick){
return
}
if(this.phoneClockImageType){
uni.navigateTo({
url:'./faceRecognition?faceScore='+this.faceScore
})
// var that = this
2025-01-04 17:20:45 +08:00
// this.$chooseImage({
2022-06-08 15:48:09 +08:00
// count: 1, //默认9
// sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
// sourceType: ['camera'], //从相册选择
// success: function (res) {
// console.log(JSON.stringify(res.tempFilePaths));
// const tempFilePaths = res.tempFilePaths;
// uni.uploadFile({
// url:that.url_config+'upload/image', //post请求的地址
// filePath:tempFilePaths[0],
// name:'files',
// // formData: {
// // 'username': this.userInfo.username //formData是指除了图片以外额外加的字段
// // },
// success: (uploadFileRes) => {
// //这里要注意uploadFileRes.data是个String类型要转对象的话需要JSON.parse一下
// var obj = JSON.parse(uploadFileRes.data);
// console.log('obj',obj)
// that.photoUrl=obj.data[0].imageUrl
// that.addPhoneAttendanceFn()
// }
// })
// }
// });
}else{
this.addPhoneAttendanceFn()
}
},
}
}
</script>
<style scoped lang="scss">
.profile_photo {
width: 45px;
height: 45px;
border-radius: 50%;
margin-right: 10px;
2022-06-08 15:48:09 +08:00
}
.box {
box-shadow: 0 4px 24px 0px rgba(212, 220, 236, 0.69);
border-radius: 8px;
margin: 15px;
padding: 15px;
2022-06-08 15:48:09 +08:00
}
.personInfoBox {
display: flex;
align-items: center;
flex-direction: row;
.personName {
color: rgba(72, 141, 236, 1);
font-size: 17px;
2022-06-08 15:48:09 +08:00
}
.groupName {
color: rgba(42, 43, 91, 0.8);
2022-06-08 15:48:09 +08:00
font-size: 13px;
}
}
.top {
2022-06-08 15:48:09 +08:00
display: flex;
align-items: center;
flex-direction: row;
.item {
background-color: #f3f4f8;
border-radius: 4px;
padding: 8px 12px;
flex: 1;
font-size: 15px;
// width: calc(50% - 29px);
// display: inline-flex;
&:first-child {
margin-right: 10px;
}
.time {
font-size: 13px;
color: rgba(38, 45, 71, 0.7);
display: flex;
align-items: center;
margin-top: 3px;
flex-direction: row;
}
}
2022-06-08 15:48:09 +08:00
}
.backImg {
margin-right: 3px;
2022-06-08 15:48:09 +08:00
}
.bottom {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: calc(100% - 60px);
.address {
font-size: 13px;
margin-top: 20px;
color: rgba(38, 45, 71, 0.7);
flex-direction: row;
display: flex;
align-items: center;
justify-content: center;
}
2022-06-08 15:48:09 +08:00
}
.circleBox {
width: 140px;
height: 140px;
border-radius: 50%;
background-image: linear-gradient(#0093f9, #066af8);
color: white;
display: inline-block;
box-shadow: 0 4px 24px 0px rgba(81, 129, 246, 0.5);
&.grey {
background-image: linear-gradient(#f1f1f1, #c3c3c3);
box-shadow: 0 4px 24px 0px rgba(195, 195, 195, 0.5);
}
.name {
padding-top: 45px;
font-size: 20px;
}
.time {
font-size: 15px;
}
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
.profile_photo {
width: 48px;
height: 48px;
border-radius: 8px;
margin-right: 10px;
}
.box {
box-shadow: 0 4px 24px 0px rgba(212, 220, 236, 0.69);
border-radius: 8px;
margin: 15px;
padding: 15px;
box-sizing: border-box;
}
.personInfoBox {
display: flex;
align-items: center;
flex-direction: row;
.personName {
// color: rgba(72, 141, 236, 1);
// font-size: 17px;
font-weight: bold;
font-size: 16px;
color: #000000;
}
.groupName {
color: rgba(42, 43, 91, 0.8);
font-size: 13px;
}
}
.top {
display: flex;
align-items: center;
flex-direction: row;
.item {
background-color: #ededed;
border-radius: 12px;
padding: 8px 12px;
flex: 1;
font-size: 15px;
font-family: PingFang SC;
// width: calc(50% - 29px);
// display: inline-flex;
&:first-child {
margin-right: 10px;
}
.type {
font-size: 16px;
color: #000000;
}
.time {
font-size: 13px;
color: #707070;
display: flex;
align-items: center;
margin-top: 3px;
flex-direction: row;
}
}
}
.backImg {
margin-right: 3px;
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
.bottom {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
height: calc(100% - 60px);
.address {
font-size: 13px;
margin-top: 20px;
color: #707070;
flex-direction: row;
display: flex;
align-items: baseline;
justify-content: center;
}
}
.circleBox {
width: 140px;
height: 140px;
border-radius: 50%;
background-image: linear-gradient(#0093f9, #066af8);
color: white;
display: inline-block;
box-shadow: 0 4px 24px 0px rgba(81, 129, 246, 0.5);
&.grey {
background-image: linear-gradient(#f1f1f1, #c3c3c3);
box-shadow: 0 4px 24px 0px rgba(195, 195, 195, 0.5);
}
.name {
padding-top: 45px;
font-size: 20px;
}
.time {
font-size: 15px;
color: rgba(255, 255, 255, 0.7);
}
}
.popup_content {
padding: 16px;
.popup_title {
font-size: 16px;
color: #171717;
font-weight: bold;
display: flex;
justify-content: center;
}
}
.flex {
display: flex;
align-items: center;
}
.flex-c {
display: flex;
flex-direction: column;
}
.form_item {
font-size: 30rpx;
line-height: 40px;
margin: 8px 0;
/* border-bottom: 1px solid rgba(194, 194, 194, 0.2); */
.p_image {
width: 52px;
height: 52px;
}
}
.form_item .name {
margin-right: 6px;
width: 185rpx;
text-align: left;
white-space: nowrap;
&.large {
width: 300rpx;
}
&.large-2 {
width: 240rpx;
}
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
.calendarBox {
width: 100%;
height: calc(100% - 78px - -24px);
// height: 100%;
.calendarTitle {
// background-color: #fff;
font-size: 18px;
font-weight: bold;
color: #000000;
padding: 10px 0;
}
.calendarStatus {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
margin-bottom: 12px;
.item {
display: flex;
align-items: center;
flex-shrink: 0;
.circle {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
}
.text {
font-size: 12px;
color: #707070;
}
}
}
.calendar {
width: 100%;
overflow: hidden;
height: auto;
transition: all 0.3s;
&.hide {
height: 150px;
}
}
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
.open {
display: flex;
align-items: center;
.line {
flex: 1;
height: 1px;
background: #dbdbdb;
}
.ic {
flex-shrink: 0;
margin: 0 2px;
width: 20px;
height: 20px;
}
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
.detail {
padding: 20px 4px;
.info {
font-family: PingFang SC;
font-weight: 400;
font-size: 13px;
color: #707070;
line-height: 16px;
text-align: left;
margin-bottom: 16px;
}
/deep/.u-dot {
background: #707070;
}
/deep/.u-time-axis::before {
border-left: 1px solid #707070;
}
.u-wrap {
display: flex;
justify-content: space-between;
align-items: flex-start;
.left {
flex: 1;
}
.u-image {
width: 42px;
height: 42px;
margin-left: 8px;
.image {
width: 42px;
height: 42px;
border-radius: 8px;
}
}
}
.u-order-time {
font-weight: 500;
font-size: 16px;
color: #000000;
line-height: 16px;
margin-bottom: 10px;
}
.u-order-desc {
font-weight: 400;
font-size: 13px;
color: #707070;
line-height: 16px;
display: flex;
align-items: center;
.img {
width: 32rpx;
height: 16px;
margin-right: 10px;
flex-shrink: 0;
}
}
.noData {
width: 100%;
height: 200px;
display: flex;
justify-content: center;
}
2022-06-08 15:48:09 +08:00
}
2024-12-04 18:37:56 +08:00
/deep/.uni-calendar-item__weeks-box-item {
width: auto;
2022-06-08 15:48:09 +08:00
}
</style>