@@ -1,344 +1,538 @@
< template >
< view class = "con-body" >
< view class = "con-bg " >
<!-- 头部 -- >
< customHeader ref = "customHeaderRef" :title = "'签到打卡'" :leftFlag = "true" :rightFlag = "false "> < / customHeader >
< view class = "content " >
< view class = "pub-add-box1" @click ="addInsertMapForSignIn" >
< view style = "color: aliceblue; font-size: 22px; "> 签到 < / view >
< / view >
<!-- 高度来避免头部遮挡 -- >
< view class = "top-height " > < / view >
< view class = "pub-add-box" @click ="addInsertMapClockIn" >
< view style = "color: aliceblue; font-size: 22px; " > 打卡 < / view >
< / view >
<!-- 正文内容 -- >
< view class = "white-bg" >
< image src = "../../../../static/images/business/btn-qd.png" class = "btn-image" @click ="handleCheckIn" / >
< ! - - < image src = "../../../../static/images/business/btn-dk.png" class = "btn-image" @click ="handleClockIn" /> - - >
< image src = "../../../../static/images/business/btn-dk.png" class = "btn-image" @click ="handleClick" / >
< view class = "check-desc" >
业务人员可通过
< text class = "font-orange" > 签到 < / text >
或
< text
class = "font-blue " > 打卡
< / text >
进行行为记录 , 该时间会和走访报告中的时间进行关联 , 便于查看 。
<!-- 自定义打卡确认弹窗 -- >
< view class = "clock-in-modal" v-if = "showClockInModal" @click="cancelClockIn" >
< view class = "modal-content" @click.stop >
< view class = "modal-header" >
< text class = "modal-title" > 请选择工作状态 < / text >
< / view >
< view class = "modal-body" >
< text class = "address-text" > { { clockInAddress } } < / text >
< / view >
< view class = "modal-footer" >
< view class = "btn-group " >
< button class = "cancel-btn" @click ="businessTripClockIn" > 出差 < / button >
< button class = "confirm-btn" @click ="confirmClockIn" > 未出差 < / button >
< / view >
< / view >
< / view >
< / view >
< / view >
< closeable-modal v-model = "modalVisible"
title = "确认在此处打卡?"
:content = "form.addressForStart"
closeTip = "轻触空白处关闭"
cancelText = "出差"
confirmText = "打卡"
contentAlign = "center"
@confirm ="handleConfirm"
@cancel ="handleCancel" />
< ! - - 打卡遮罩层 - - >
<!-- < view class = "check-con" v-if = "checkFlag" >
< view class = "check-in" >
< view class = "check-tip" > 打卡 < / view >
< view class = "check-title" > 确定要在此处打卡吗 ? < / view >
< view class = "check-location" >
< uni-icons type = "location-filled" size = "30" color = "#0395E0" > < / uni-icons > 亚洲金融大厦
< / view >
< view class = "check-address" > 北京市朝阳区天辰东路1号院 < / view >
< view class = "check-footer" >
< button class = "btn-default" type = "default" @click ="handleCancel" size = "mini" > 取 消 < / button >
< button class = "btn-primary" type = "primary" @click ="handleSubmit" size = "mini" > 确 定 < / button >
< / view >
< / view >
< / view > -- >
< / view >
< / template >
< script setup >
import { ref , onMounted , reactive } from 'vue'
import customHeader from '@/components/customHeader.vue'
import { addStartMapForClockIn , businessTripClockIn } from '../../../../api/crm/activity/map' ;
import CloseableModal from "@/components/closeableModal.vue" ;
import { MapApiConfig } from "../../../../constants/mapApiConstants" ;
< script >
import { addStartMapForClockIn , businessTripClockIn } from '@/api/crm/activity/map.js' ;
let f orm = reactive ( {
addressForStart : null ,
addressForEnd : null ,
createId : null ,
staffName : null ,
visistCode : null ,
visistId : null ,
remark : null ,
mapId : null
} )
// 签到
let handleCheckIn = ( ) => {
uni . navigateTo ( {
url : './addRearkSignIn'
} )
}
// 打卡
// let handleClockIn = () => {
// uni.getLocation({
// type: 'wgs84',
// success: (res) => {
// const latiude = res.latitude;
// const longitude = res.longitude;
// //进行解析
// inverseGeocoding(latiude, longitude);
// },
// fail: function (err) {
// console.log("获取地址失败" + err)
// }
// })
// }
let modalVisible = ref ( false ) ;
let handleClick = ( ) => {
uni . getLocation ( {
type : 'wgs84' ,
success : ( res ) => {
const latitude = res . latitude ;
const longitude = res . longitude ;
console . log ( '纬度:' , latitude , ',经度:' , longitude ) ;
inverseGeocoding ( latitude , longitude ) ;
} ,
fail : function ( err ) {
console . log ( "获取地址失败" + err )
exp ort default {
data ( ) {
return {
arrUrl : null ,
mescroll : undefined ,
// 打卡相关数据
showClockInModal : false ,
clockInAddress : '' ,
clockInData : null ,
downOption : {
auto : false //是否在初始化后,自动执行downCallback; 默认true
} ,
upOption : {
onScroll : true ,
use : true , // 是否启用上拉加载; 默认true
auto : true , // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
page : {
num : 0 , // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
size : 10 // 每页数据的数量,默认10
} ,
noMoreSize : 1 , // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
empty : {
tip : '暂无相关数据'
}
} ,
dataList : [ ] ,
dzList : [ ] ,
searchContent : '' ,
form : {
addressForStart : null ,
addressForEnd : null ,
c reateId : null ,
staffName : null ,
visistCode : null ,
visistId : null ,
mapId : null ,
}
}
} )
}
// 反馈提示
const feedback = ( res , callback ) => {
if ( res . code === 200 ) {
if ( callback ) callback ( ) ;
uni . showToast ( {
title : MapApiConfig . OTHER . typeName === form . addressForStart ? MapApiConfig . OTHER . typeName : res . msg ,
icon : 'success'
} ,
onLoad ( ) {
// 修改顶部导航背景色
uni . setNavigationBarColor ( {
frontColor : '#ffffff' ,
backgroundColor : '#29abe2' ,
animation : {
duration : 400 ,
timingFunc : 'easeIn'
}
} )
setTimeout ( ( ) => {
uni . navigateBack ( 1 ) ;
} , 500 )
}
else {
uni . showToast ( {
icon : 'none' ,
title : res . msg
} )
}
}
// 出差
const handleCancel = ( ) => {
businessTripClockIn ( form ) . then ( res => {
feedback ( res , ( ) => console . log ( '出差打卡成功' ) ) ;
} ) . catch ( e => {
console . log ( e ) ;
} )
}
// 打卡
let handleConfirm = ( ) => {
addStartMapForClockIn ( form ) . then ( res => {
feedback ( res , ( ) => console . log ( '打卡成功' ) ) ;
} ) . catch ( e => {
console . log ( e )
} )
}
let date = new Date ( ) ;
this . form . createTime = this . dateFormat ( "YYYY-mm-dd" , date ) ;
} ,
onShow ( ) {
this . arrUrl = uni . getStorageSync ( "avatar" ) ;
} ,
methods : {
addInsertMap ( ) {
uni . navigateTo ( {
url : './insertMap'
} )
} ,
//签到
addInsertMapForSignIn ( ) {
// 判定是否在范围内
function isWithinRange ( lat , lon , centerLat , centerLon , rangeKm ) {
const distance = haversineDistance ( centerLat , centerLon , lat , lon ) ;
return distance <= rangeKm ;
}
uni . navigateTo ( {
url : './addRearkSignIn'
} )
function haversineDistance ( lat1 , lon1 , lat2 , lon2 , radius = 6371 ) {
const dLat = degToRad ( lat2 - lat1 ) ;
const dLon = degToRad ( lon2 - lon1 ) ;
const a =
Math . sin ( dLat / 2 ) * Math . sin ( dLat / 2 ) +
Math . cos ( degToRad ( lat1 ) ) * Math . cos ( degToRad ( lat2 ) ) *
Math . sin ( dLon / 2 ) * Math . sin ( dLon / 2 ) ;
const c = 2 * Math . atan2 ( Math . sqrt ( a ) , Math . sqrt ( 1 - a ) ) ;
return radius * c ; // 距离,单位:公里
}
// 角度转弧度
function degToRad ( deg ) {
return deg * ( Math . PI / 180 ) ;
}
//解析地址
function inverseGeocoding ( latitude , longitude ) {
uni . showLoading ( ) ;
const apiURL = MapApiConfig . URL ;
const params = {
lat : latitud e ,
} ,
//打卡
addInsertMapClockIn ( ) {
var that = this ;
uni . getLocation ( {
type : 'wgs84' ,
success : ( res ) => {
const latiude = res . latitude ;
const longitude = res . longitude ;
//进行解析
this . inverseGeocoding ( latiude , longitude ) ;
} ,
fail : function ( err ) {
console . log ( "获取地址失败" + err )
}
} )
} ,
// 显示打卡确认弹窗
showClockInConfirm ( address , data ) {
this . clockInAddress = address ;
this . clockInData = data ;
this . showClockInModal = tru e;
} ,
// 取消打卡(点击遮罩层)
cancelClockIn ( ) {
this . showClockInModal = false ;
this . clockInAddress = '' ;
this . clockInData = null ;
} ,
// 确认打卡
confirmClockIn ( ) {
const that = this ;
addStartMapForClockIn ( this . clockInData ) . then ( res => {
if ( res . code == 200 ) {
uni . showToast ( {
icon : 'success' ,
title : res . msg ,
duration : 1500 ,
} ) ;
setTimeout ( ( ) => {
uni . navigateBack ( 1 )
} , 500 )
} else {
uni . showToast ( {
icon : 'none' ,
title : res . msg ,
} ) ;
}
that . cancelClockIn ( ) ; // 关闭弹窗
} )
} ,
// 出差打卡
businessTripClockIn ( ) {
const that = this ;
businessTripClockIn ( this . clockInData ) . then ( res => {
if ( res . code == 200 ) {
uni . showToast ( {
icon : 'success' ,
title : res . msg ,
duration : 1500 ,
} ) ;
setTimeout ( ( ) => {
uni . navigateBack ( 1 )
} , 500 )
} else {
uni . showToast ( {
icon : 'none' ,
title : res . msg ,
} ) ;
}
that . cancelClockIn ( ) ; // 关闭弹窗
} )
} ,
isWithinRange ( lat , lon , centerLat , centerLon , rangeKm ) {
const distance = this . haversineDistance ( centerLat , centerLon , lat , lon ) ;
return distance <= rangeKm ;
} ,
dateFormat ( fmt , date ) {
let ret ;
const opt = {
"Y+" : date . getFullYear ( ) . toString ( ) , // 年
"m+" : ( date . getMonth ( ) + 1 ) . toString ( ) , // 月
"d+" : date . getDate ( ) . toString ( ) , // 日
"H+" : date . getHours ( ) . toString ( ) , // 时
"M+" : date . getMinutes ( ) . toString ( ) , // 分
"S+" : date . getSeconds ( ) . toString ( ) // 秒
// 有其他格式化字符需求可以继续添加,必须转化成字符串
} ;
for ( let k in opt ) {
ret = new RegExp ( "(" + k + ")" ) . exec ( fmt ) ;
if ( ret ) {
fmt = fmt . replace ( ret [ 1 ] , ( ret [ 1 ] . length == 1 ) ? ( opt [ k ] ) : ( opt [ k ] . padStart ( ret [ 1 ] . length , "0" ) ) )
} ;
} ;
return fmt ;
} ,
haversineDistance ( lat1 , lon1 , lat2 , lon2 , radius = 6371 ) {
const dLat = this . degToRad ( lat2 - lat1 ) ;
const dLon = this . degToRad ( lon2 - lon1 ) ;
const a =
Math . sin ( dLat / 2 ) * Math . sin ( dLat / 2 ) +
Math . cos ( this . degToRad ( lat1 ) ) * Math . cos ( this . degToRad ( lat2 ) ) *
Math . sin ( dLon / 2 ) * Math . sin ( dLon / 2 ) ;
const c = 2 * Math . atan2 ( Math . sqrt ( a ) , Math . sqrt ( 1 - a ) ) ;
return radius * c ; // 距离,单位:公里
} ,
degToRad ( deg ) {
return deg * ( Math . PI / 180 ) ;
} ,
inverseGeocoding ( latiude , longitude ) {
var that = this ;
let points = longitude + ',' + latiude
const apiURL = 'https://tiles.geovisearth.com/geo/v1/geocode/regeo' ;
const params = {
lat : latiude ,
lng : longitude ,
radius : 1000 ,
pageSize : 1 ,
currentPage : 1 ,
//classify: 220100
} ;
const token = MapApiConfig . token ;
uni . request ( {
} ;
const token = '66c87c897f0251295afdc794e4fbf73046a070338a726fe04f06cece6cb1ffdf' ;
uni . request ( {
url : apiURL ,
method : 'GET' ,
data : params ,
header : {
'Authorization' : 'Bearer ' + token
'Authorization' : 'Bearer ' + token
} ,
success : ( res ) => {
modalVisible . valu e=true ;
console . log ( re s , "经纬度解析成功" )
if ( res. statusCode === 200 && res . data . status === 200 ) {
let resdata = res . data . data . rows [ 0 ] . address
if ( resdata == null ) {
console . log ( resdata . srcLat )
if ( isWithinRange ( latitude1 , longitude1 , latiude , longitude , 1 ) ) {
form . addressForStart = MapApiConfig . XI _AN _BAN . typeName ;
form . path = MapApiConfig . XI _AN _BAN . longitude + ',' + MapApiConfig . XI _AN _BAN . latitude ;
}
}
else {
form . addressForStart = resdata ;
form . path = longitude + ',' + latit ude ; // 经度,纬度
if ( res . statusCod e == 200 && res . data . status == 200 ) {
let resdata = res . data . data . row s [ 0 ] . address
if ( resdata == null ) {
const latiude1 = 34.1360 ;
const longitude1 = 108.9126 ;
if ( this . isWithinRange ( latiude1 , longitude1 , latiude , longitude , 1 ) ) {
this . form . addressForStart = "西安办事处位置打卡"
let data = {
mapId : that . form . mapId ,
addressForStart : that . form . addressForStart ,
cusName : that . form . cusName ,
cusId : that . form . cusId ,
remark : that . form . remark ,
path : longitude1 + "," + latiude1
}
that . showClockInConfirm ( that . form . addressForStart , data ) ;
}
} else {
form . addressForStart = MapApiConfig . OTHER . typeName ;
form . p ath = longitude + ',' + latitude ; // 经度,纬度
this . form . addressForStart = resdata ;
let d ata = {
mapId : that . form . mapId ,
addressForStart : that . form . addressForStart ,
cusName : that . form . cusName ,
cusId : that . form . cusId ,
path : points
}
that . showClockInConfirm ( that . form . addressForStart , data ) ;
}
uni . hideLoading ( ) ;
} else {
this . form . addressForStart = "第三方维护打卡"
let data = {
mapId : that . form . mapId ,
addressForStart : that . form . addressForStart ,
cusName : that . form . cusName ,
cusId : that . form . cusId ,
path : points
}
that . showClockInConfirm ( that . form . addressForStart , data ) ;
}
} ,
fail ( e ) {
console . log ( "获取位置失败" , e )
uni . hideLoading ( ) ;
} ,
} )
console . log ( "获取位置失败" , e )
}
} )
} ,
fail : function ( err ) {
console . log ( "调用失败" + err )
}
}
}
< / script >
< style scoped >
. white - bg {
width : 650 rpx ;
margin : 0 ;
border - radius : 8 px 8 px 0 0 ;
padding : 50 rpx ;
/* #ifdef APP-PLUS */
height : calc ( 100 vh - 125 px ) ;
/* #endif */
/* #ifndef APP-PLUS */
height : calc ( 100 vh - 98 px ) ;
/* #endif */
< style scoped lang = "scss" >
. bg - green {
background - color : # 29 abe2 ;
}
. btn - image {
width : 340 rpx ;
height : 340 rpx ;
margin : 30 rpx auto 6 0rpx ;
display : block ;
. avatar {
align - items : center ;
overflow : hidden ;
width : 5 0rpx ;
height : 60 rpx ;
border - radius : 50 % ;
margin : 0 auto ;
margin - right : 10 px ;
}
. check - desc {
background - color : # F5F5F5 ;
padding : 4 0rpx 50 r px;
font - size : 28 r px;
border - radius : 1 0px ;
margin - top : 100 rpx ;
. pub - add - box {
z - index : 9999 ;
width : 32 0u px;
height : 320 u px;
border - radius : 5 0% ;
background : # 13579 E ;
position : fixed ;
right : 205 upx ;
bottom : 200 upx ;
display : flex ;
flex - direction : column ;
align - items : center ;
justify - content : center ;
box - shadow : 0 px 0 px 80 upx # 13579 E ;
. img {
width : 40 upx ;
height : 40 upx ;
}
}
. check - desc . font - orange {
color : # F5813A ;
font - size : 32 r px;
font - w eight: bold ;
. pub - add - box1 {
z - index : 9999 ;
width : 320 u px;
h eight: 320 upx ;
border - radius : 50 % ;
background : # EA8021 ;
position : fixed ;
right : 205 upx ;
top : 100 upx ;
display : flex ;
flex - direction : column ;
align - items : center ;
justify - content : center ;
box - shadow : 0 px 0 px 80 upx # EA8021 ;
. img {
width : 40 upx ;
height : 40 upx ;
}
}
. check - desc . font - blue {
color : # 2 CBAEF ;
font - size : 32 rpx ;
font - weight : bold ;
. avatarInner {
align - items : center ;
overflow : hidden ;
width : 50 rpx ;
height : 60 rpx ;
border - radius : 50 % ;
margin : 0 auto ;
margin - right : 10 px ;
}
/* 弹窗处理 */
. check - con {
. info - item - flex2 {
width : 100 rpx ;
}
. info - item1 {
align - items : center ;
// height: 100rpx;
}
. scroolView {
width : 630 rpx ;
white - space : nowrap ;
display : flex ;
align - items : baseline ;
}
// 筛选
// .screen-cont{
// height: 80upx;
// background: #fff;
// margin-top: 100upx;
// }
// 客户列表
. cus - list {
background : # fff ;
. item {
padding : 20 upx 40 upx ;
border - bottom : 1 px solid # f7f7f7 ;
& : last - child {
border - bottom : none ;
}
. head {
display : flex ;
align - items : center ;
justify - content : space - between ;
. text {
font - size : 32 upx ;
color : # 000 ;
}
. btn - box {
. deal - btn {
width : 100 upx ;
text - align : center ;
padding : 10 upx 0 ;
font - size : 24 upx ;
border - radius : 4 upx ;
& . deal {
background : # 29 abe2 ;
color : # fff ;
}
& . un - deal {
background : # EFEFEF ;
color : # BDBDBD ;
}
}
. nameContent {
font - size : 17 upx ;
}
}
}
. head {
display : flex ;
align - items : center ;
justify - content : space - between ;
. name {
font - size : 32 upx ;
color : # 000 ;
}
}
. cus - info {
. info - item {
& . flex {
display : flex ;
align - items : center ;
justify - content : space - between ;
. img - box {
display : flex ;
align - items : center ;
. img {
width : 40 upx ;
height : 40 upx ;
margin - left : 10 upx ;
}
}
}
. left {
font - size : 28 upx ;
color : # 737373 ;
}
}
}
}
}
/* 自定义打卡弹窗样式 */
. clock - in - modal {
position : fixed ;
top : 0 ;
left : 0 ;
right : 0 ;
bottom : 0 ;
background - color : rgba ( 0 , 0 , 0 , 0.4 ) ;
width : 100 % ;
height : 100 % ;
background - color : rgba ( 0 , 0 , 0 , 0.5 ) ;
z - index : 9999 ;
display : flex ;
justify - content : center ;
align - items : center ;
z - index : 999 ;
justify - content : center ;
}
/* 遮罩层内容样式 */
. check - in {
. modal - content {
width : 80 % ;
max - width : 500 rpx ;
background - color : # fff ;
padding : 40 rpx 30 rpx 6 0rpx ;
border - radius : 10 px ;
/* width: 620rpx; */
width : 560 rpx ;
position : absolute ;
top : 48 % ;
left : 50 % ;
transform : translate ( - 50 % , - 50 % ) ;
border - radius : 2 0rpx ;
overflow : hidden ;
box - shadow : 0 10 rpx 30 rpx rgba ( 0 , 0 , 0 , 0.3 ) ;
}
. modal - header {
padding : 40 rpx 30 rpx 20 rpx ;
text - align : center ;
border - bottom : 1 rpx solid # f0f0f0 ;
}
. modal - title {
font - size : 32 rpx ;
font - weight : bold ;
color : # 333 ;
}
. modal - body {
padding : 30 rpx ;
text - align : center ;
}
. check - in . check - tip {
font - weight : bold ;
. address - text {
font - size : 28 rpx ;
color : # 666 ;
line - height : 1.6 ;
}
. check - in . check - title {
font - size : 32 rpx ;
padding : 20 rpx 0 ;
. modal - footer {
padding : 20 rpx 30 rpx 30 rpx ;
}
. check - in . check - location {
color : # 0395 E0 ;
font - size : 42 rpx ;
font - weight : bold ;
. btn - group {
display : flex ;
align - items : center ;
justify - content : center ;
margin : 30 rpx 0 ;
gap : 20 rpx ;
}
. check - in . check - location . uniui - location - filled {
font - weight : normal ;
margin - right : 20 rpx ;
. cancel - btn ,
. confirm - btn {
flex : 1 ;
height : 80 rpx ;
line - height : 80 rpx ;
text - align : center ;
border - radius : 40 rpx ;
font - size : 28 rpx ;
border : none ;
}
. check - in . check - address {
color : # 919191 ;
font - size : 32 rpx ;
margin - bottom : 80 rpx ;
. cancel - btn {
background - color: # f5f5f5 ;
color : # 666 ;
}
. check - in . check - footer {
display : flex ;
}
. check - in . check - footer . btn - default ,
. check - in . check - footer . btn - primary {
background - color : # fff ;
border : 1 px solid # 05 A3F4 ;
color : # 05 A3F4 ;
border - radius : 25 px ;
padding : 0 rpx 80 rpx ;
font - size : 34 rpx ;
/* margin-left: 0;
margin-right: 20rpx; */
}
. check - in . check - footer . btn - primary {
background - color : # 05 A3F4 ;
border : 1 px solid # 05 A3F4 ;
. confirm - btn {
background - color : # 13579 E ;
color : # fff ;
/* padding: 0rpx 60rpx; */
}
< / style >
< / style >