You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
542 lines
15 KiB
542 lines
15 KiB
<template>
|
|
<view class="content">
|
|
<image src="https://static.ticket.sz-trip.com/tourGuide/images/index/topImg.png" class="topImg"></image>
|
|
|
|
<view class="title" style="margin-top: 40rpx;">·快捷入口</view>
|
|
|
|
<view class="flex-between">
|
|
<view class="nav-item flex-around" @click="goHX()">
|
|
立即核销
|
|
<image src="https://static.ticket.sz-trip.com/tourGuide/images/index/hexiao.png" mode=""></image>
|
|
</view>
|
|
<view class="nav-item flex-around" @click="gotoPath('/subPackages/order/orderList')">
|
|
查看订单
|
|
<image src="https://static.ticket.sz-trip.com/tourGuide/images/index/dingdan.png" mode=""></image>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="!isShowClasses">
|
|
<view class="title">·我的打卡</view>
|
|
<view class="rule" @click="openRule">打卡规则</view>
|
|
<view class="punch-box flex-column" :style="{backgroundColor: isCanPunch ? '#96684F' : ''}" @click="punch">
|
|
<view>上线打卡</view>
|
|
{{currentTime}}
|
|
</view>
|
|
<!-- 未打卡 -->
|
|
<view v-if="!punchSuccess">
|
|
<view class="punch-text">{{isCanPunch ? '您已进入打卡范围:' + userPunch.clock_address[nearestFenceIndex].title : '您当前不在可打卡范围内'}}</view>
|
|
<view class="examine-btn flex-center" v-if="userPunch.is_open_examine" @click="examinePunch">审批打卡</view>
|
|
</view>
|
|
<!-- 打卡成功 -->
|
|
<view v-else>
|
|
<view class="punch-text">打卡时间:{{currentTime.slice(0,5)}}</view>
|
|
<view class="punch-text" style="margin: 0 auto;">{{timeSuccess}}s后自动进入排班页面</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-else>
|
|
<view class="title">·我的排班</view>
|
|
<view class="type-box">
|
|
<view v-for="(item,index) in typeList" :key="index"
|
|
:class="['type-item', {'type-active': index == typeIndex}]" @click="typeIndex = index;getList()">
|
|
{{item.title}}
|
|
<view class="type-line" v-if="index == typeIndex"></view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 日历 -->
|
|
<calendarVue :isShowLunar="true" :isCanChangeDate="userPunch.is_clock == 0 ? true : false" @changeDate="getDate"></calendarVue>
|
|
|
|
<!-- 选择场次 -->
|
|
<view v-for="(item,index) in typeIndex ? sessionLists : sessionList" :key="index"
|
|
class="session-item flex-between">
|
|
{{item.title}}
|
|
<switch :checked="item.isChecked" color="#96684F" @change="switchChange(item,index)" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 打卡规则 -->
|
|
<uni-popup ref="popup" type="center" border-radius="10px" background-color="#fff">
|
|
<view v-html="formateRichText(ruleContent)" style="width: 90%;padding: 30rpx;border-radius: 10rpx;margin: auto;"></view>
|
|
</uni-popup>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import calendarVue from '../../components/calendar.vue';
|
|
export default {
|
|
components: {
|
|
calendarVue
|
|
},
|
|
data() {
|
|
return {
|
|
typeList: [{
|
|
title: '导游服务',
|
|
id: ''
|
|
},
|
|
// {
|
|
// title: '线上咨询',
|
|
// id: ''
|
|
// },
|
|
// {
|
|
// title: '拼团产品',
|
|
// id: ''
|
|
// },
|
|
{
|
|
title: '线路产品',
|
|
id: ''
|
|
}
|
|
],
|
|
typeIndex: 0,
|
|
selectDay: '',
|
|
sessionList: [{
|
|
title: '上午场',
|
|
isChecked: false
|
|
},
|
|
{
|
|
title: '下午场',
|
|
isChecked: false
|
|
},
|
|
{
|
|
title: '全天场',
|
|
isChecked: false
|
|
}
|
|
],
|
|
sessionLists: [{
|
|
title: '当天状态',
|
|
isChecked: false
|
|
}],
|
|
isAllChecked: false,
|
|
// 最近的打卡点索引
|
|
nearestFenceIndex: null,
|
|
// 打卡点半径(单位:米)
|
|
fenceRadius: 500,
|
|
// 用于存储 watchPosition 的返回值,方便后续清除监听
|
|
watchId: null,
|
|
ruleContent: '',
|
|
currentTime: '',
|
|
timeInterval: null,
|
|
isCanPunch: false, // 是否可打卡,
|
|
punchSuccess: false, // 打卡成功
|
|
timeSuccess: 3,
|
|
isShowClasses: true, // 是否显示排班表
|
|
userPunch: {},
|
|
myLonLat: {
|
|
lng: '',
|
|
lat: ''
|
|
}
|
|
}
|
|
},
|
|
onLoad() {
|
|
this.selectDay = this.getNowTime(new Date())
|
|
this.getList()
|
|
},
|
|
onShow() {
|
|
// 获取用户打卡信息
|
|
this.Post({},'/api/Merchants/getGuideClockStatus').then(res => {
|
|
this.userPunch = res.data
|
|
|
|
if(this.userPunch.clock_address.length == 0) return;
|
|
|
|
// 用户是否需要打卡、及打卡状态:0不需要打卡,1未打卡,2已打卡或审批打卡成功,3审批打卡审核中,4审批拒绝
|
|
switch (this.userPunch.is_clock){
|
|
case 0:
|
|
this.isShowClasses = true
|
|
break;
|
|
case 1:
|
|
this.isShowClasses = false
|
|
break;
|
|
case 2:
|
|
this.isShowClasses = true
|
|
break;
|
|
case 3:
|
|
this.isShowClasses = false
|
|
break;
|
|
case 4:
|
|
this.isShowClasses = false
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
this.watchLocation(); // 开始监听用户位置
|
|
this.startTimeUpdate();
|
|
})
|
|
},
|
|
onHide() {
|
|
// 页面隐藏时清除位置监听
|
|
if (this.watchId) {
|
|
navigator.geolocation.clearWatch(this.watchId)
|
|
}
|
|
|
|
// 页面隐藏时清除时间更新定时器
|
|
if (this.timeInterval) {
|
|
clearInterval(this.timeInterval);
|
|
}
|
|
},
|
|
methods: {
|
|
// 点击打卡
|
|
punch() {
|
|
if (this.nearestFenceIndex === null) {
|
|
uni.showToast({
|
|
title: '您当前不在可打卡范围内,无法打卡',
|
|
icon: 'none'
|
|
});
|
|
return;
|
|
}
|
|
this.Post({
|
|
address_id: this.userPunch.clock_address[this.nearestFenceIndex].id,
|
|
lon: this.myLonLat.lng,
|
|
lat: this.myLonLat.lat
|
|
}, '/api/Merchants/guideClockLocate').then(res => {
|
|
if(res.code == 1) {
|
|
this.punchSuccess = true
|
|
clearInterval(this.timeInterval); // 打卡成功后清除时间定时器
|
|
let timer = setInterval(() => {
|
|
this.timeSuccess--;
|
|
if (this.timeSuccess == 0) {
|
|
clearInterval(timer);
|
|
this.isShowClasses = true
|
|
}
|
|
}, 1000);
|
|
}
|
|
})
|
|
},
|
|
// 审批打卡
|
|
examinePunch() {
|
|
if(this.userPunch.is_clock == 1) {
|
|
// 去审批打卡
|
|
this.gotoPath('/subPackages/clockIn/apply')
|
|
}else if([3,4].includes(this.userPunch.is_clock)) {
|
|
// 查看审批记录
|
|
this.gotoPath('/subPackages/clockIn/apply?id=' + this.userPunch.clock_id)
|
|
}
|
|
},
|
|
// 监听用户位置变化
|
|
watchLocation() {
|
|
if (navigator.geolocation) {
|
|
this.watchId = navigator.geolocation.watchPosition(
|
|
(position) => {
|
|
let coords = position.coords;
|
|
let [gcj02Lng, gcj02Lat] = this.wgs84ToGcj02(coords.longitude, coords.latitude);
|
|
this.myLonLat = {
|
|
lng: gcj02Lng,
|
|
lat: gcj02Lat
|
|
}
|
|
// 初始化是否可打卡为 false
|
|
this.isCanPunch = false;
|
|
// 遍历每个打卡点
|
|
for (let i = 0; i < this.userPunch.clock_address.length; i++) {
|
|
const item = this.userPunch.clock_address[i];
|
|
const distance = this.calculateDistance(item.lat, item.lon, gcj02Lat, gcj02Lng);
|
|
console.log(`与打卡点 ${item.title} 的距离:`, distance);
|
|
if (distance <= item.range) {
|
|
this.isCanPunch = true;
|
|
this.nearestFenceIndex = i;
|
|
break; // 找到满足条件的打卡点后结束循环
|
|
}
|
|
}
|
|
},
|
|
(error) => {
|
|
let locationStatus = ''
|
|
switch (error.code) {
|
|
case 1:
|
|
locationStatus = '定位未开启,用户拒绝了定位请求';
|
|
break;
|
|
case 2:
|
|
locationStatus = '定位信息不可用';
|
|
break;
|
|
case 3:
|
|
locationStatus = '定位请求超时';
|
|
break;
|
|
case 4:
|
|
locationStatus = '定位出现未知错误';
|
|
break;
|
|
}
|
|
uni.showToast({
|
|
title: locationStatus,
|
|
icon: 'none'
|
|
})
|
|
}
|
|
);
|
|
} else {
|
|
// 浏览器不支持地理定位
|
|
uni.showToast({
|
|
title: '浏览器不支持地理定位功能',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
},
|
|
// WGS84坐标转换为GCJ-02坐标
|
|
// 判断是否在中国范围内
|
|
outOfChina(lng, lat) {
|
|
return (lng < 72.004 || lng > 137.8347) || (lat < 0.8293 || lat > 55.8271);
|
|
},
|
|
// 转换纬度
|
|
transformLat(x, y) {
|
|
let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
|
|
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
|
|
ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
|
|
ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
|
|
return ret;
|
|
},
|
|
// 转换经度
|
|
transformLng(x, y) {
|
|
let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
|
|
ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
|
|
ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
|
|
ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
|
|
return ret;
|
|
},
|
|
// WGS84 转换为 GCJ02
|
|
wgs84ToGcj02(lng, lat) {
|
|
const a = 6378245.0; // 地球长半轴
|
|
const ee = 0.00669342162296594323; // 扁率
|
|
if (this.outOfChina(lng, lat)) {
|
|
return [lng, lat];
|
|
}
|
|
let dLat = this.transformLat(lng - 105.0, lat - 35.0);
|
|
let dLng = this.transformLng(lng - 105.0, lat - 35.0);
|
|
const radLat = lat / 180.0 * Math.PI;
|
|
let magic = Math.sin(radLat);
|
|
magic = 1 - ee * magic * magic;
|
|
const sqrtMagic = Math.sqrt(magic);
|
|
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * Math.PI);
|
|
dLng = (dLng * 180.0) / (a / sqrtMagic * Math.cos(radLat) * Math.PI);
|
|
const mgLat = lat + dLat;
|
|
const mgLng = lng + dLng;
|
|
return [mgLng, mgLat];
|
|
},
|
|
goHX() {
|
|
uni.switchTab({
|
|
url: '/pages/verification/index'
|
|
})
|
|
},
|
|
// 选择场次
|
|
switchChange(item, index) {
|
|
item.isChecked = !item.isChecked
|
|
|
|
// if(index == 2) {
|
|
// for (let i = 0; i < 2; i++) {
|
|
// this.sessionList[i].isChecked = this.sessionList[2].isChecked
|
|
// }
|
|
// }else {
|
|
// if(this.sessionList[0].isChecked && this.sessionList[1].isChecked) {
|
|
// this.sessionList[2].isChecked = true
|
|
// }else {
|
|
// this.sessionList[2].isChecked = false
|
|
// }
|
|
// }
|
|
|
|
let url = this.typeIndex ? '/api/Merchants/updateGuideSched' : '/api/Merchants/updateGuideSchedService'
|
|
|
|
this.Post({
|
|
date: this.selectDay,
|
|
online_type: item.isChecked ? 1 : 0, // 开关
|
|
date_half: this.typeIndex ? '' : index + 1, // 导游服务 1上午 2下午 3全天
|
|
classify: this.typeIndex ? 2 : '' // 1拼团,2线路
|
|
}, url).then(res => {
|
|
|
|
})
|
|
},
|
|
getDate(date) {
|
|
console.log('传递过来的日期', date)
|
|
this.selectDay = date
|
|
|
|
this.sessionList.forEach(item => item.isChecked = false)
|
|
this.sessionLists[0].isChecked = false
|
|
|
|
this.getList()
|
|
},
|
|
changeWork(date) {
|
|
// // 导游服务值班状态 date_half:分类(1上午,2下午,3全天)
|
|
// this.Post({
|
|
// date: date,
|
|
// date_half
|
|
// },'/api/Merchants/updateGuideSchedService').then(res => {
|
|
|
|
// })
|
|
},
|
|
getList() {
|
|
// 获取排班列表 classify:分类(1拼团,2线路,3导游服务)
|
|
this.Post({
|
|
start_date: this.selectDay,
|
|
end_date: this.selectDay,
|
|
classify: this.typeIndex ? 2 : 3
|
|
}, '/api/Merchants/getGuideSchedList').then(res => {
|
|
if (res.data && res.data.length > 0) {
|
|
let data = res.data[0]
|
|
if (this.typeIndex) {
|
|
// 线路
|
|
this.sessionLists[0].isChecked = data.online_type ? true : false
|
|
} else {
|
|
// 导游
|
|
this.sessionList[0].isChecked = data.morning_type ? true : false
|
|
this.sessionList[1].isChecked = data.afternoon_type ? true : false
|
|
this.sessionList[2].isChecked = data.day_type ? true : false
|
|
}
|
|
}
|
|
})
|
|
},
|
|
// 获取当前日期
|
|
formatDate(date, format = 'YYYY/MM/DD') {
|
|
const year = date.getFullYear();
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const hour = String(date.getHours()).padStart(2, '0');
|
|
const minute = String(date.getMinutes()).padStart(2, '0');
|
|
const second = String(date.getSeconds()).padStart(2, '0');
|
|
|
|
return format
|
|
.replace('YYYY', year)
|
|
.replace('MM', month)
|
|
.replace('DD', day)
|
|
.replace('HH', hour)
|
|
.replace('mm', minute)
|
|
.replace('ss', second);
|
|
},
|
|
getNowTime(time, type) {
|
|
const format = type === 1 ? (uni.getSystemInfoSync().platform === 'ios' ? 'YYYY/MM/DD HH:mm:ss' : 'YYYY-MM-DD HH:mm:ss') : 'YYYY/MM/DD';
|
|
return this.formatDate(time, format);
|
|
},
|
|
// 打卡规则
|
|
openRule() {
|
|
this.Post({
|
|
id: 7
|
|
},'/api/article/getArticleById').then(res => {
|
|
this.ruleContent = res.data.content
|
|
this.$refs.popup.open()
|
|
})
|
|
},
|
|
// 开始更新时间
|
|
startTimeUpdate() {
|
|
this.updateTime();
|
|
this.timeInterval = setInterval(() => {
|
|
this.updateTime();
|
|
}, 1000);
|
|
},
|
|
// 更新时间
|
|
updateTime() {
|
|
const now = new Date();
|
|
const hours = String(now.getHours()).padStart(2, '0');
|
|
const minutes = String(now.getMinutes()).padStart(2, '0');
|
|
const seconds = String(now.getSeconds()).padStart(2, '0');
|
|
this.currentTime = `${hours}:${minutes}:${seconds}`;
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.content {
|
|
background: #FFFFFF;
|
|
min-height: 100vh;
|
|
padding: 40rpx 26rpx 200rpx;
|
|
}
|
|
|
|
.topImg {
|
|
width: 299.33rpx;
|
|
height: 70rpx;
|
|
display: flex;
|
|
margin: auto;
|
|
}
|
|
|
|
.title {
|
|
font-weight: bold;
|
|
font-size: 35rpx;
|
|
color: #000000;
|
|
margin: 68rpx 0 34rpx;
|
|
}
|
|
|
|
.rule {
|
|
font-weight: 500;
|
|
font-size: 31rpx;
|
|
color: #96684F;
|
|
text-align: right;
|
|
}
|
|
|
|
.punch-box {
|
|
width: 267rpx;
|
|
height: 267rpx;
|
|
background: #CCCCCC;
|
|
box-shadow: 0rpx 0rpx 29rpx 0rpx rgba(153,153,153,0.6);
|
|
border-radius: 50%;
|
|
justify-content: center;
|
|
align-items: center;
|
|
font-weight: bold;
|
|
font-size: 32rpx;
|
|
color: #FFFFFF;
|
|
margin: 200rpx auto 0;
|
|
}
|
|
|
|
.punch-text {
|
|
font-weight: bold;
|
|
font-size: 30rpx;
|
|
color: #111111;
|
|
margin: 40rpx auto;
|
|
text-align: center;
|
|
}
|
|
|
|
.examine-btn {
|
|
width: 257rpx;
|
|
height: 80rpx;
|
|
border-radius: 13rpx;
|
|
border: 1rpx solid #96684F;
|
|
font-weight: 500;
|
|
font-size: 31rpx;
|
|
color: #96684F;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.nav-item {
|
|
font-weight: 500;
|
|
font-size: 32rpx;
|
|
color: #000000;
|
|
width: 338rpx;
|
|
height: 118rpx;
|
|
background: #F5F5F5;
|
|
border-radius: 20rpx;
|
|
padding: 0 15rpx;
|
|
|
|
image {
|
|
width: 80.67rpx;
|
|
height: 80.67rpx;
|
|
}
|
|
}
|
|
|
|
.type-box {
|
|
display: flex;
|
|
|
|
.type-item {
|
|
margin-right: 44rpx;
|
|
font-weight: 500;
|
|
font-size: 32rpx;
|
|
color: #000000;
|
|
}
|
|
|
|
.type-active {
|
|
color: #96684F;
|
|
}
|
|
|
|
.type-line {
|
|
width: 117rpx;
|
|
height: 6rpx;
|
|
background: #96684F;
|
|
border-radius: 3rpx;
|
|
margin: 4rpx auto 0;
|
|
}
|
|
}
|
|
|
|
.session-item {
|
|
width: 697rpx;
|
|
height: 93rpx;
|
|
background: #F5F5F5;
|
|
border-radius: 47rpx;
|
|
margin: 28rpx auto 0;
|
|
font-family: PingFang SC;
|
|
font-weight: 500;
|
|
font-size: 32rpx;
|
|
color: #000000;
|
|
padding: 0 28rpx;
|
|
}
|
|
</style>
|