|
@@ -0,0 +1,362 @@
|
|
|
+<template>
|
|
|
+ <view class="ding flex flex-col">
|
|
|
+ <RadiusSelect title="服务单元名称" placeholder="请选择服务单元" :list="serviceUnit" :value.sync="params.serviceId" :on-change="getSopList"></RadiusSelect>
|
|
|
+ <RadiusSelect title="SOP" placeholder="请选择SOP" :list="sopList" :value.sync="params.sopNo" :on-change="getDingInfo"></RadiusSelect>
|
|
|
+ <view class="msg-item">
|
|
|
+ <view class="m-head flex justify-between">
|
|
|
+ <view class="m-title flex-col">
|
|
|
+ <text class="title">签到{{ dingInfo.signInTimeScope ||'' }}</text>
|
|
|
+ <view class="flex ">
|
|
|
+ <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signInTime"/>
|
|
|
+ <view class="sub-title mr2">{{dingInfo.signInTime?dateFormat(dingInfo.signInTime, 'hh:mm')+'已打卡':'未打卡'}}</view>
|
|
|
+ <a @click="checkDingObj('IN')" class="a-title" v-show="dingInfo.signInTime">更新打卡</a>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="m-title flex-col">
|
|
|
+ <text class="title">签退{{ dingInfo.signOutTimeScope ||'' }}</text>
|
|
|
+ <view class="flex ">
|
|
|
+ <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signOutTime"/>
|
|
|
+ <view class="sub-title mr2">{{dingInfo.signOutTime?dateFormat(dingInfo.signOutTime, 'hh:mm')+'已打卡':'未打卡'}}</view>
|
|
|
+ <a @click="checkDingObj('OUT')" class="a-title" v-show="dingInfo.signOutTime">更新打卡</a>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="m-body flex items-center" >
|
|
|
+ <view class="m-box flex flex-col items-center" v-show="signOutEnable||signInEnable" @click="checkDingObj(signOutEnable?'OUT':(signInEnable?'IN':''))" >
|
|
|
+ <text class="m-box-1">{{signOutEnable?'签退':(signInEnable?'签到':'')}}打卡</text>
|
|
|
+ <text class="m-box-2" v-show="dingInfo.faceOpen">(人脸识别)</text>
|
|
|
+ <text class="m-box-3">{{dateFormat(currentTime, 'hh:mm')}}</text>
|
|
|
+ </view>
|
|
|
+ <view class="m-box-disable m-box flex flex-col items-center" v-show="!signOutEnable&&!signInEnable">
|
|
|
+ <text class="m-box-1">无法打卡</text>
|
|
|
+ <text class="m-box-2" v-show="dingInfo.faceOpen">(人脸识别)</text>
|
|
|
+ <text class="m-box-3">{{dateFormat(currentTime, 'hh:mm')}}</text>
|
|
|
+ </view>
|
|
|
+ <view class="m-box-success flex flex-col items-center" v-show="dingSuccess">
|
|
|
+ <image src="../../static/icon/ding-success.png" class="image-success"/>
|
|
|
+ <text class="title-success">打卡成功,打卡时间{{dateFormat(dingObj.signTime, 'hh:mm')}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="m-foot flex">
|
|
|
+ <u-icon name="map" color="#8C8C8C" size="28"></u-icon>
|
|
|
+ <text class="sub-title">{{dingObj.signAddress}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="msg-item">
|
|
|
+ <view class="m-head flex justify-between">
|
|
|
+ <view class="msg-foot flex flex-col">
|
|
|
+ <u-icon name="error-circle" color="#8C8C8C" size="50"></u-icon>
|
|
|
+ <text class="sub-title">异常处理</text>
|
|
|
+ </view>
|
|
|
+ <view class="msg-separator"></view>
|
|
|
+ <view class="msg-foot flex flex-col">
|
|
|
+ <u-icon name="clock" color="#8C8C8C" size="50"></u-icon>
|
|
|
+ <text class="sub-title">统计</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <u-toast ref="uToast"/>
|
|
|
+
|
|
|
+ <u-popup v-model="show" mode="center" border-radius="14">
|
|
|
+ <view class="popup-main flex flex-col justify-between items-center">
|
|
|
+ <text class="title">打卡提醒</text>
|
|
|
+ <text class="sub-title">请选择你是签到还是签退</text>
|
|
|
+ <u-button type="primary" @click="checkDingObj('IN')">签到</u-button>
|
|
|
+ <u-button type="primary" plain @click="checkDingObj('OUT')">签退</u-button>
|
|
|
+ <u-button type="default" @click="show=false">取消</u-button>
|
|
|
+ </view>
|
|
|
+ </u-popup>
|
|
|
+ <u-popup v-if="faceModel" mode="center" border-radius="200" :mask-close-able="false">
|
|
|
+ <DingFace :on-change="dingFace"></DingFace>
|
|
|
+ </u-popup>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {dingFindRunningSop, getDingInfo, dingSave, locAddr} from '@/api/ding'
|
|
|
+import {getServiceUnit} from '@/api/common'
|
|
|
+import {dateFormat, getAppWxLatLon, timeCompare} from '@/utils/utils'
|
|
|
+import RadiusSelect from "@/components/radius-select.vue";
|
|
|
+import DingFace from "./ding-face.vue";
|
|
|
+
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'ding',
|
|
|
+ components: {RadiusSelect,DingFace},
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ dateFormat,
|
|
|
+ loadingFlag: 0,
|
|
|
+ serviceUnit: [],
|
|
|
+ sopList: [],
|
|
|
+ params: {serviceId: '', sopNo: null},
|
|
|
+ show: false,
|
|
|
+ faceModel: false,
|
|
|
+ signOutEnable: false,
|
|
|
+ signInEnable: false,
|
|
|
+ dingSuccess: false,
|
|
|
+ dingInfo: {},
|
|
|
+ setTime:null,
|
|
|
+ currentTime:null,
|
|
|
+ dingObj:{
|
|
|
+ signTime: null,
|
|
|
+ sopNo:null,
|
|
|
+ signType: "OUT",
|
|
|
+ signAddress: "",
|
|
|
+ axisX: "",
|
|
|
+ axisY: ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ const _this=this;
|
|
|
+ getAppWxLatLon(async (res) => {
|
|
|
+ const loc=await locAddr(res);
|
|
|
+ _this.dingObj.signAddress=loc.result.address
|
|
|
+ _this.dingObj.axisX = res.longitude;
|
|
|
+ _this.dingObj.axisY = res.latitude;
|
|
|
+ })
|
|
|
+ getServiceUnit().then((res) => {
|
|
|
+ this.serviceUnit = (res || []).map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.id,
|
|
|
+ label: item.name
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ this.setTime = setInterval( ()=> {
|
|
|
+ this.currentTime = new Date().getTime()
|
|
|
+ }, 1000)
|
|
|
+
|
|
|
+ },
|
|
|
+ onHide(){
|
|
|
+ clearInterval(this.setTime)
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getSopList(serviceId) {
|
|
|
+ dingFindRunningSop({serviceUnitId: serviceId}).then((res) => {
|
|
|
+ this.sopList = (res || []).map((item) => {
|
|
|
+ return {
|
|
|
+ value: item.sopNo,
|
|
|
+ label: item.sopNo + item.customName
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ },
|
|
|
+ getDingInfo(sopNo) {
|
|
|
+ getDingInfo({sopNo: sopNo}).then((res) => {
|
|
|
+ this.dingInfo = res;
|
|
|
+ if (this.dingInfo.signInTimeScope) {
|
|
|
+ this.signInEnable=timeCompare(this.dingInfo.signInTimeScope.split("~")[0],this.currentTime,this.dingInfo.signInTimeScope.split("~")[1]);
|
|
|
+ }
|
|
|
+ if (this.dingInfo.signOutTimeScope) {
|
|
|
+ this.signOutEnable=timeCompare(this.dingInfo.signOutTimeScope.split("~")[0],this.currentTime,this.dingInfo.signOutTimeScope.split("~")[1]);
|
|
|
+ }
|
|
|
+ if(!this.dingInfo.signInTimeScope||!this.dingInfo.signOutTimeScope){
|
|
|
+ this.show=true;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ checkDingObj(signType) {
|
|
|
+ if(!signType){
|
|
|
+ this.show=true;return;
|
|
|
+ }
|
|
|
+ this.dingObj.sopNo = this.params.sopNo;
|
|
|
+ this.dingObj.signType = signType;
|
|
|
+ this.dingObj.signTime = this.currentTime;
|
|
|
+ this.dingObj.facePass = false;
|
|
|
+ if (this.dingInfo.faceOpen) {
|
|
|
+ this.faceModel=true;
|
|
|
+ }else{
|
|
|
+ const _this=this;
|
|
|
+ getAppWxLatLon(async (res) => {
|
|
|
+ const loc = await locAddr(res);
|
|
|
+ _this.dingObj.signAddress = loc.result.address
|
|
|
+ _this.dingObj.axisX = res.longitude;
|
|
|
+ _this.dingObj.axisY = res.latitude;
|
|
|
+ dingSave(_this.dingObj).then((res) => {
|
|
|
+ _this.dingSuccess = true;
|
|
|
+ _this.signInEnable = false;
|
|
|
+ _this.signOutEnable = false;
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ dingFace(facePhotoPath){
|
|
|
+ const _this=this;
|
|
|
+ getAppWxLatLon(async (res) => {
|
|
|
+ const loc = await locAddr(res);
|
|
|
+ _this.dingObj.signAddress = loc.result.address
|
|
|
+ _this.dingObj.facePhotoPath = facePhotoPath;
|
|
|
+ _this.dingObj.axisX = res.longitude;
|
|
|
+ _this.dingObj.axisY = res.latitude;
|
|
|
+ dingSave(_this.dingObj).then((res) => {
|
|
|
+ _this.dingSuccess = true;
|
|
|
+ _this.signInEnable = false;
|
|
|
+ _this.signOutEnable = false;
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.ding {
|
|
|
+ height: calc(100% - 80rpx);
|
|
|
+ padding: 24rpx;
|
|
|
+}
|
|
|
+.msg-item {
|
|
|
+ padding: 24rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ background: #fff;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+ .m-body {
|
|
|
+ margin-top: 16rpx;
|
|
|
+ justify-content: center;
|
|
|
+ .m-box{
|
|
|
+ width: 272rpx;
|
|
|
+ height: 272rpx;
|
|
|
+ justify-content: center;
|
|
|
+ background: linear-gradient(180deg, #3196FF 0%, #165DFF 100%);
|
|
|
+ box-shadow: 0rpx 0rpx 24rpx 0rpx rgba(22,93,255,0.5);
|
|
|
+ border-radius: 136rpx;
|
|
|
+ margin-top: 80rpx;
|
|
|
+ .m-box-1{
|
|
|
+ height: 56rpx;
|
|
|
+ font-size: 40rpx;
|
|
|
+ font-family: PingFangSC-Medium, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #FFFFFF;
|
|
|
+ line-height: 56rpx;
|
|
|
+ }
|
|
|
+ .m-box-2{
|
|
|
+ height: 44rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: rgba(255,255,255,0.5);
|
|
|
+ line-height: 44rpx;
|
|
|
+ }
|
|
|
+ .m-box-3{
|
|
|
+ height: 48rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-family: PingFangSC-Medium, PingFang SC;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #FFFFFF;
|
|
|
+ line-height: 48rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .m-box-disable{
|
|
|
+ width: 272rpx;
|
|
|
+ height: 272rpx;
|
|
|
+ justify-content: center;
|
|
|
+ background: linear-gradient(180deg, #C0C0C0 0%, #8C8C8C 100%);
|
|
|
+ box-shadow: 0px 0px 12px 0px #BFBFBF;
|
|
|
+ border-radius: 136rpx;
|
|
|
+ margin-top: 80rpx;
|
|
|
+ }
|
|
|
+ .m-box-success{
|
|
|
+ margin-top: 80rpx;
|
|
|
+ .title-success{
|
|
|
+ width: 188px;
|
|
|
+ height: 24px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #262626;
|
|
|
+ line-height: 24px;
|
|
|
+ }
|
|
|
+ .image-success{
|
|
|
+
|
|
|
+ width: 60px;
|
|
|
+ height: 60px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .m-foot {
|
|
|
+ padding-top: 40rpx;
|
|
|
+ padding-bottom: 70rpx;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ .m-head {
|
|
|
+ .msg-foot{
|
|
|
+ min-width: 48%;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+ .msg-separator{
|
|
|
+ width: 2rpx;
|
|
|
+ background: #E5E5E5;
|
|
|
+ }
|
|
|
+ .m-title {
|
|
|
+ min-width: 48%;
|
|
|
+ background: #f0f0f0;
|
|
|
+ align-items: center;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ padding: 10rpx;
|
|
|
+ .title {
|
|
|
+ height: 48rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #262626;
|
|
|
+ line-height: 48rpx;
|
|
|
+ }
|
|
|
+ .ding-success{
|
|
|
+ margin: 3px 0 3px 0;
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ }
|
|
|
+ .a-title{
|
|
|
+ height: 40rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #165DFF;
|
|
|
+ line-height: 40rpx;
|
|
|
+ }
|
|
|
+ .sub-title{
|
|
|
+ height: 40rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #8C8C8C;
|
|
|
+ line-height: 40rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.popup-main {
|
|
|
+ min-width: 240px;
|
|
|
+ min-height: 254px;
|
|
|
+ padding: 25rpx;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: linear-gradient(180deg, #D7EAFF 0%, #FFFFFF 100%);
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
+ .title {
|
|
|
+ width: 160px;
|
|
|
+ height: 24px;
|
|
|
+ font-size: 16px;
|
|
|
+ font-family: PingFangSC-Semibold, PingFang SC;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #262626;
|
|
|
+ line-height: 24px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .sub-title {
|
|
|
+ width: 168px;
|
|
|
+ height: 20px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-family: PingFangSC-Regular, PingFang SC;
|
|
|
+ font-weight: 400;
|
|
|
+ color: #595959;
|
|
|
+ line-height: 20px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ u-button{
|
|
|
+ width: -webkit-fill-available;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+</style>
|