修改签到打卡

This commit is contained in:
jiangyanshan
2025-12-18 11:16:44 +08:00
parent c36aa01f0a
commit dc9183664c
2 changed files with 302 additions and 488 deletions

View File

@@ -8,12 +8,19 @@
<view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view> <view class="top-height" :style="{ paddingTop: navBarPaddingTop + 'px' }"></view>
<view class="inner-box"> <view class="inner-box">
<view class="cu-form-group"> <view class="cu-form-group" style="height: 200px;padding:10px;">
<textarea v-model="form.remark" placeholder="请输入签到备注内容" name="input"></textarea> <uni-forms ref="formRef" :model="form" label-width="100px">
</view> <uni-forms-item label="签到备注内容" name="understandTheWay"
<view class="btn-box"> class="uni-forms-item is-direction-top is-top">
<uni-easyinput type="textarea" autoHeight v-model="form.remark" placeholder="请输入签到备注内容"
class="form-texarea" />
</uni-forms-item>
</uni-forms>
<view class="btn-box" style="padding-top: 50px;">
<button type="primary" @click="addInsertMapClockIn">签到</button> <button type="primary" @click="addInsertMapClockIn">签到</button>
</view> </view>
</view>
</view> </view>
</view> </view>
</view> </view>
@@ -27,20 +34,20 @@ import { parseTime } from '@/utils/datetime.js';
import { onLoad } from '@dcloudio/uni-app'; import { onLoad } from '@dcloudio/uni-app';
import { getNavBarPaddingTop } from '@/utils/system.js' import { getNavBarPaddingTop } from '@/utils/system.js'
let form = reactive({ let form = reactive({
addressForStart: null, addressForStart: null,
addressForEnd: null, addressForEnd: null,
createId: null, createId: null,
staffName: null, staffName: null,
visistCode: null, visistCode: null,
visistId: null, visistId: null,
mapId: null, mapId: null,
remark: null remark: null
}) })
// 获取导航栏高度用于内容区域padding // 获取导航栏高度用于内容区域padding
const navBarPaddingTop = ref(0); const navBarPaddingTop = ref(0);
onMounted(() => { onMounted(() => {
navBarPaddingTop.value = getNavBarPaddingTop() * 2; navBarPaddingTop.value = getNavBarPaddingTop() * 2;
}) })
onLoad(option => { onLoad(option => {

View File

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