224 lines
6.7 KiB
Vue
224 lines
6.7 KiB
Vue
<template>
|
|
<div class="row g-0">
|
|
<div class="col-6 pe-1">
|
|
<button
|
|
class="btn border-3 w-100 py-0 h-px-50"
|
|
:class="workTime ? 'p-0 btn-primary pe-none' : 'btn-outline-primary'"
|
|
@click="setWorkTime"
|
|
>
|
|
<i v-if="!workTime" class="bx bx-run fs-2"></i>
|
|
<span v-if="workTime" class="ql-size-12px">{{ workTime }}</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="col-6 ps-1">
|
|
<button
|
|
class="btn border-3 w-100 py-0 h-px-50"
|
|
:class="!workTime ? 'btn-outline-secondary pe-none disabled' : 'btn-outline-secondary'"
|
|
@click="setLeaveTime"
|
|
:disabled="!workTime"
|
|
>
|
|
<i v-if="!leaveTime" class='bx bxs-door-open fs-2'></i>
|
|
<span v-if="leaveTime" class="ql-size-12px">{{ leaveTime }}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, defineProps, defineEmits, onMounted, watch } from 'vue';
|
|
import $api from '@api';
|
|
import { useGeolocation } from '@vueuse/core';
|
|
|
|
const props = defineProps({
|
|
userId: {
|
|
type: Number,
|
|
required: false
|
|
},
|
|
checkedInProject: {
|
|
type: Object,
|
|
required: false
|
|
},
|
|
pendingProjectChange: {
|
|
type: Object,
|
|
default: null
|
|
}
|
|
});
|
|
|
|
const emit = defineEmits(['workTimeUpdated', 'leaveTimeUpdated', 'projectChangeComplete', 'update:pendingProjectChange']);
|
|
|
|
const workTime = ref(null);
|
|
const leaveTime = ref(null)
|
|
const userLocation = ref(null);
|
|
|
|
// 위치 정보 가져오기 설정
|
|
const { coords, isSupported, error } = useGeolocation({
|
|
enableHighAccuracy: true,
|
|
});
|
|
|
|
// 주소 변환 함수
|
|
const getAddress = async (lat, lng) => {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof kakao === 'undefined' || !kakao.maps) {
|
|
reject('Kakao Maps API가 로드되지 않았습니다.');
|
|
return;
|
|
}
|
|
|
|
const geocoder = new kakao.maps.services.Geocoder();
|
|
|
|
geocoder.coord2Address(lng, lat, (result, status) => {
|
|
|
|
if (status === kakao.maps.services.Status.OK) {
|
|
if (result && result.length > 0 && result[0].address) {
|
|
const address = result[0].address.address_name;
|
|
resolve(address);
|
|
} else {
|
|
// 결과가 있지만 주소가 없는 경우
|
|
reject('주소 정보가 없습니다.');
|
|
}
|
|
} else if (status === kakao.maps.services.Status.ZERO_RESULT) {
|
|
// ZERO_RESULT 상태 처리
|
|
// 좌표로 주소를 찾지 못했을 때 도로명 주소로 시도
|
|
geocoder.coord2RegionCode(lng, lat, (regionResult, regionStatus) => {
|
|
if (regionStatus === kakao.maps.services.Status.OK && regionResult.length > 0) {
|
|
// 행정구역 정보로 대체
|
|
const region = regionResult[0].address_name;
|
|
resolve(`[대략적 위치] ${region}`);
|
|
} else {
|
|
// 행정구역 정보도 없는 경우 좌표 자체를 반환
|
|
resolve(`위도: ${lat}, 경도: ${lng} (주소 정보 없음)`);
|
|
}
|
|
});
|
|
} else {
|
|
reject(`주소를 가져올 수 없습니다. 상태: ${status}`);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
// 위치 정보 가져오기 함수
|
|
const getLocation = async () => {
|
|
if (!isSupported.value) {
|
|
alert('브라우저가 위치 정보를 지원하지 않습니다.');
|
|
return null;
|
|
}
|
|
|
|
if (error.value) {
|
|
alert(`위치 정보를 가져오는데 실패했습니다: ${error.value.message}`);
|
|
return null;
|
|
}
|
|
|
|
if (!coords.value) {
|
|
return null;
|
|
}
|
|
|
|
userLocation.value = {
|
|
lat: coords.value.latitude,
|
|
lng: coords.value.longitude,
|
|
};
|
|
|
|
try {
|
|
const address = await getAddress(coords.value.latitude, coords.value.longitude);
|
|
return address;
|
|
} catch (error) {
|
|
alert(error);
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// 오늘 사용자의 출근 정보 조회
|
|
const todayCommuterInfo = async () => {
|
|
if (!props.userId) return;
|
|
|
|
const res = await $api.get(`commuters/today/${props.userId}`);
|
|
if (res.status === 200) {
|
|
const commuterInfo = res.data.data[0];
|
|
|
|
if (commuterInfo) {
|
|
workTime.value = commuterInfo.COMMUTCMT;
|
|
leaveTime.value = commuterInfo.COMMUTLVE;
|
|
|
|
// 부모 컴포넌트에 상태 업데이트 알림
|
|
emit('workTimeUpdated', workTime.value);
|
|
emit('leaveTimeUpdated', leaveTime.value);
|
|
}
|
|
}
|
|
};
|
|
|
|
// 출근 시간
|
|
const setWorkTime = async () => {
|
|
// 이미 출근 시간이 설정된 경우 중복 실행 방지
|
|
if (workTime.value) return;
|
|
|
|
// 현재 위치 주소 가져오기
|
|
const address = await getLocation();
|
|
|
|
if (!address) {
|
|
// 주소를 가져오지 못했을 때도 계속 진행할지 사용자에게 확인
|
|
if (!confirm('위치 정보를 가져오지 못했습니다. 위치 없이 출근 처리하시겠습니까?')) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
$api.post('commuters/insert', {
|
|
memberSeq: props.userId,
|
|
projctSeq: props.checkedInProject.PROJCTSEQ,
|
|
commutLve: null,
|
|
commutArr: address,
|
|
commutOut: null,
|
|
}).then(res => {
|
|
if (res.status === 200) {
|
|
todayCommuterInfo();
|
|
emit('workTimeUpdated', true);
|
|
}
|
|
});
|
|
};
|
|
|
|
// 퇴근
|
|
const setLeaveTime = async () => {
|
|
// 현재 위치 주소 가져오기
|
|
const address = await getLocation();
|
|
|
|
|
|
if (!address && !leaveTime.value) {
|
|
// 주소를 가져오지 못했을 때도 계속 진행할지 사용자에게 확인
|
|
if (!confirm('위치 정보를 가져오지 못했습니다. 위치 없이 퇴근 처리하시겠습니까?')) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
$api.patch('commuters/updateLve', {
|
|
memberSeq: props.userId,
|
|
commutLve: leaveTime.value || null,
|
|
projctLve: props.pendingProjectChange ? props.pendingProjectChange.projctSeq : props.checkedInProject.PROJCTSEQ,
|
|
commutOut: address,
|
|
}).then(res => {
|
|
if (res.status === 200) {
|
|
todayCommuterInfo();
|
|
emit('leaveTimeUpdated');
|
|
emit('update:pendingProjectChange', null);
|
|
}
|
|
});
|
|
};
|
|
|
|
// props 변경 감지
|
|
watch(() => props.userId, async () => {
|
|
if (props.userId) {
|
|
await todayCommuterInfo();
|
|
}
|
|
});
|
|
|
|
onMounted(async () => {
|
|
await todayCommuterInfo();
|
|
});
|
|
|
|
// 외부에서 접근할 메서드 노출
|
|
defineExpose({
|
|
todayCommuterInfo,
|
|
workTime,
|
|
leaveTime
|
|
});
|
|
</script>
|