123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- <template>
- <view class="location-box">
- <!-- 地图 -->
- <view class="map">
- <map
- id="myMap"
- ref="mapRef"
- class="map-box"
- scale="14"
- :latitude="latitud.latitude"
- :longitude="latitud.longitude"
- :markers="markers"
- :circles="circles"
- show-location
- show-compass
- >
- </map>
- </view>
- <!-- 打卡 -->
- <view class="box-cont">
- <view class="visit-operate">
- <view class="title"> <text>选择拜访对象</text></view>
- <view class="visit-box">
- <picker-view indicator-class="select" class="visit-list" @change="visitScroll">
- <picker-view-column>
- <view
- class="info-row"
- v-for="(item, index) in locationList"
- :key="index"
- :class="{ act: act === index }"
- >
- <view> {{ item.signEntName }} ({{ item.dis }} )</view>
- </view>
- </picker-view-column>
- </picker-view>
- <!-- 打卡 -->
- <view class="sign-box">
- <view class="sign-in" @click="signFn" v-if="notClockedIn">
- <view>签到</view>
- </view>
- <view class="sign-in" @click="toDetail" v-else>
- <view>签退</view>
- </view>
- </view>
- </view>
- <view class="check-in-time" v-if="!notClockedIn">
- <view>签到时间: {{ signResultTime }}</view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import dayjs from 'dayjs'
- import { onLoad, onShow } from '@dcloudio/uni-app'
- import { getCurrentInstance, ref, reactive, onUnmounted } from 'vue'
- import { getWxLocationApi } from '@/wxUtils/index'
- import { debounce } from '@/utils/index'
- import { useTask } from '@/store/task'
- import {
- getDictPointApi,
- getPointSignInfoApi,
- signTempApi,
- getSignTempResultApi
- } from '@/service/modules/getTask'
- import { getDistance } from '@/utils/index'
- const useTaskStore = useTask()
- const { ctx }: any = getCurrentInstance()
- const mapRef = ref()
- const latitud = reactive({
- latitude: 39.909,
- longitude: 116.39742
- })
- const markers = ref<any[]>([])
- const circles = ref<any[]>([])
- const myMap = ref()
- const taskTypeId = ref('')
- const title = ref('')
- // 定时器
- const timer = ref()
- onLoad(async (e: any) => {
- // 保存当前任务类型和标题
- title.value = e?.title
- taskTypeId.value = e?.value
- myMap.value = uni.createMapContext('myMap', ctx)
- // 获取可打卡范围
- await getDictPoint()
- })
- const getLocation = async () => {
- try {
- // 获取位置
- const res: any = await getWxLocationApi()
- const { latitude, longitude } = res || {}
- // 当本次坐标与上次坐标不相同时
- if (latitud.latitude !== latitude || latitud.longitude !== longitude) {
- latitud.latitude = latitude
- latitud.longitude = longitude
- flag.value = true
- setMapRegion() // 设置地图区域
- // 获取医院列表
- await fetchHospitalList()
- }
- } catch (err) {
- handleLocationError()
- }
- }
- // 提取获取医院列表的函数
- const fetchHospitalList = async () => {
- try {
- await getPointSignInfo()
- } catch (err) {
- console.error('获取终端失败,请联系管理员')
- }
- }
- // 错误处理函数
- const handleLocationError = () => {
- flag.value = false
- // 清除定时器
- if (timer.value) {
- clearInterval(timer.value)
- timer.value = null
- }
- uni.showModal({
- title: '错误提示',
- showCancel: false,
- content: '获取定位失败'
- })
- }
- const mapRadius = ref()
- const getDictPoint = async () => {
- uni.showLoading()
- const res = await getDictPointApi()
- mapRadius.value = res.data[0].value
- uni.hideLoading()
- }
- // 设置地图范围
- const setMapRegion = () => {
- myMap.value.getRegion({
- success() {
- // 获取东经西经维度
- circles.value.push({
- latitude: latitud.latitude,
- longitude: latitud.longitude,
- color: '#ffffff',
- fillColor: '#7cb5ec88',
- radius: Number(mapRadius.value),
- strokeWidth: 2
- })
- }
- })
- }
- const hospitalList = ref<any[]>([])
- const getPointSignInfo = async () => {
- const res = await getPointSignInfoApi(latitud)
- hospitalList.value = res.data
- calcDistance()
- }
- const curentDistance = ref()
- // 计算当前地址和医院的距离,得到最短的距离
- async function calcDistance() {
- //! 数据处理
- if (!hospitalList.value.length) return
- let hospitalMap = hospitalList.value.map((item: any) => {
- // 计算每个地点与当前位置的距离
- item.distance = getDistance(
- latitud.longitude,
- latitud.latitude,
- item!.longitude,
- item!.latitude
- )
- return item
- })
- // 根据距离排序
- hospitalMap.sort((itemA: any, itemB: any) => {
- return itemA.distance - itemB.distance > 0 ? 1 : -1
- })
- hospitalList.value = hospitalMap
- // 默认设置最近的地点
- curentDistance.value = hospitalMap[0]
- // 获取第一个医院的签到结果
- getsignTempResult()
- setMarkers(curentDistance.value)
- checkSignInState()
- }
- // 设置marker
- const setMarkers = (distanceItem: any) => {
- markers.value = [
- {
- id: Number(distanceItem.signEntId),
- latitude: distanceItem.latitude,
- longitude: distanceItem.longitude,
- width: 25,
- height: 35,
- callout: {
- display: 'BYCLICK',
- content: distanceItem.signEntName,
- color: 'black',
- borderRadius: 8,
- bgColor: '#ffffff',
- padding: 10,
- textAlign: 'center'
- }
- }
- ]
- }
- // 是否在范围内
- const withinTheScopeOf = ref(false)
- const locationList = ref<any[]>([])
- // 检查可以签到状态
- const checkSignInState = () => {
- // 计算当前距离距离选择地点的距离
- let result = getDistance(
- latitud.longitude,
- latitud.latitude,
- curentDistance?.value?.longitude,
- curentDistance?.value?.latitude
- )
- result = result * 1000
- // 半径范围与距离判断
- withinTheScopeOf.value = mapRadius.value >= result
- // 对地点进行过滤,去除超过当前半径范围内的
- locationList.value = hospitalList.value.filter((item) => {
- let itemResult = getDistance(latitud.longitude, latitud.latitude, item.longitude, item.latitude)
- itemResult = itemResult * 1000
- if (mapRadius.value > itemResult) {
- item.value = item.signEntId
- item.label = item.signEntName
- return item
- }
- })
- }
- const act = ref(0)
- const getResult = ref(false)
- // 拜访对象滚动
- const visitScroll = async (event: any) => {
- getResult.value = true
- uni.showLoading({
- mask: true,
- title: '获取签到信息中'
- })
- let curr = event.detail.value[0]
- act.value = curr
- // 设置当前选中的地方,重置markers
- curentDistance.value = locationList.value[curr]
- await getSignInfo()
- setMarkers(curentDistance.value)
- uni.hideLoading()
- }
- const getSignInfo = debounce(() => {
- getsignTempResult()
- }, 500)
- const getsignTempResult = async () => {
- uni.showLoading({
- mask: true,
- title: '获取签到信息中'
- })
- const obj = {
- locationId: curentDistance.value?.signEntId,
- taskTypeId: taskTypeId.value,
- combine: true,
- signTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
- }
- const res = await getSignTempResultApi(obj)
- if (res.data) {
- notClockedIn.value = false
- signResultTime.value = res.data
- } else {
- notClockedIn.value = true
- signResultTime.value = ''
- }
- getResult.value = false
- uni.hideLoading()
- }
- const notClockedIn = ref(true)
- const signResultTime = ref('')
- const signFn = async () => {
- if (!curentDistance.value) {
- return uni.showToast({
- title: '请选择签到地点',
- icon: 'none'
- })
- }
- const obj = {
- locationId: curentDistance.value?.signEntId,
- taskTypeId: taskTypeId.value,
- combine: true,
- signTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
- }
- const res = await signTempApi(obj)
- if (res.code === 0) {
- uni.showToast({
- title: '签到成功',
- icon: 'none'
- })
- notClockedIn.value = false
- signResultTime.value = res.data
- }
- }
- let flag = ref(false)
- onShow(async () => {
- Lock.value = false
- // 获取当前坐标 并且每分钟更新一次当前坐标
- await getLocation()
- // 仅当获取定位成功后才启动定时器
- if (flag.value) {
- timer.value = setInterval(() => {
- getLocation()
- }, 60 * 1000)
- }
- })
- const Lock = ref(false)
- // 签退去填写表单
- const toDetail = debounce(() => {
- setTimeout(() => {
- if (getResult.value) return
- uni.showLoading({
- mask: true,
- title: '加载中'
- })
- let TIME: number = 10
- // 签退时间
- const signOutTime = dayjs(signResultTime.value)
- // 获取当前时间
- const currentTime = dayjs()
- // 计算时间差是否超过15分钟
- if (currentTime.diff(signOutTime, 'minute') > TIME) {
- const data = {
- address: curentDistance.value?.signEntName,
- locationId: curentDistance.value?.signEntId,
- signTime: signResultTime.value,
- signEntType: curentDistance.value?.signEntType,
- longitude: curentDistance.value?.longitude,
- latitude: curentDistance.value?.latitude,
- checkOutTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
- }
- useTaskStore.setSignAddressInfo(data)
- uni.navigateTo({
- url: `/pages-sub-task/task/index?value=${taskTypeId.value}&title=${title.value}`
- })
- uni.hideLoading()
- setTimeout(() => {
- Lock.value = false
- }, 3000)
- } else {
- uni.showToast({
- title: `签到和签退时间间隔不可少于${TIME}分钟。`,
- icon: 'none'
- })
- }
- }, 1000)
- }, 500)
- // 卸载
- onUnmounted(() => {
- if (timer.value) {
- clearInterval(timer.value)
- }
- })
- </script>
- <style lang="scss" scoped>
- .location-box {
- height: 100vh;
- display: flex;
- flex-direction: column;
- .map {
- height: 450rpx;
- .map-box {
- width: 100%;
- height: 470rpx;
- position: relative;
- }
- }
- .box-cont {
- flex: 1;
- background-color: #fff;
- position: relative;
- z-index: 99;
- padding: 30rpx;
- border-radius: 16px;
- .visit-operate {
- .title {
- font-size: 28rpx;
- color: #333;
- font-weight: 600;
- line-height: 40rpx;
- }
- .visit-box {
- flex: 1;
- margin-top: 40rpx;
- .visit-list {
- height: 350rpx;
- }
- .info-row {
- height: 45rpx;
- line-height: 60rpx;
- text-align: center;
- }
- .sign-box {
- margin-top: 30rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- .sign-in {
- width: 160rpx;
- height: 160rpx;
- border-radius: 50%;
- background-color: #6eb657;
- text-align: center;
- line-height: 160rpx;
- font-size: 36rpx;
- color: #fff;
- font-weight: 600;
- }
- }
- }
- .check-in-time {
- margin-top: 60rpx;
- text-align: center;
- font-size: 30rpx;
- color: #000;
- }
- }
- }
- }
- </style>
|