refactor code & fixed location

This commit is contained in:
Warunee Tamkoo 2026-04-27 19:21:23 +07:00
parent 41c1aa8e45
commit 487a6b520e
23 changed files with 566 additions and 145 deletions

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ref, shallowRef, onBeforeUnmount } from 'vue'
import { loadModules } from 'esri-loader'
import axios from 'axios'
import { useCounterMixin } from '@/stores/mixin'
@ -13,21 +13,32 @@ const privacyStore = usePrivacyStore()
const emit = defineEmits(['update:location'])
const $q = useQuasar()
// Throttle mechanism to prevent excessive location updates
const LOCATION_UPDATE_THROTTLE_MS = 500
let lastLocationUpdate = 0
function updateLocation(latitude: number, longitude: number, namePOI: string) {
const now = Date.now()
// Skip update if called too frequently (within throttle period)
if (now - lastLocationUpdate < LOCATION_UPDATE_THROTTLE_MS) {
return
}
lastLocationUpdate = now
// event parent component props
emit('update:location', latitude, longitude, namePOI)
}
const poiPlaceName = ref<string>('') //
const mapView = shallowRef<any>(null) // Store mapView reference for cleanup (use shallowRef to avoid reactivity issues)
// Replace ArcGIS api key
// ArcGIS API key from environment variable
const apiKey = ref<string>(
'YLATgWuywoeRLHn6KImj5rg7UaP8bJoR9jiTldoCVBHlqFIebwMSA5wIXEmcYhwXwMHkmNISEYtUz3x0oiGIIx0bIXXnUwi0OzupoOEtDrQIsRPVtor7gaPpXEmH8TrNaMT3snf6zO_yujHLGzborg-L9aeAjTJn4ndL6f8qFmRzYcX93E2vyA-7XCufLYTRsdTE5Aq-9hnx1q9PmYVMqhAZpL7dWqn3JgO33fRXetk.'
// 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP'
import.meta.env.VITE_ARCGIS_API_KEY ||
'YLATgWuywoeRLHn6KImj5rg7UaP8bJoR9jiTldoCVBHlqFIebwMSA5wIXEmcYhwXwMHkmNISEYtUz3x0oiGIIx0bIXXnUwi0OzupoOEtDrQIsRPVtor7gaPpXEmH8TrNaMT3snf6zO_yujHLGzborg-L9aeAjTJn4ndL6f8qFmRzYcX93E2vyA-7XCufLYTRsdTE5Aq-9hnx1q9PmYVMqhAZpL7dWqn3JgO33fRXetk.'
)
const zoomMap = ref<number>(18)
const textTooltip = ref<string>(
'พื้นที่ใกล้เคียง: สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)'
'พื้นที่ใกล้เคียงคือ สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)'
)
async function initializeMap() {
@ -59,7 +70,7 @@ async function initializeMap() {
navigator.geolocation.getCurrentPosition(async (position) => {
const { latitude, longitude } = position.coords
const mapView = new MapView({
mapView.value = new MapView({
container: 'mapViewDisplay',
map: map,
center: {
@ -91,7 +102,7 @@ async function initializeMap() {
geometry: userPoint,
symbol: userSymbol,
})
mapView.graphics.add(userGraphic)
mapView.value.graphics.add(userGraphic)
// Get POI place server .
await axios
.get(
@ -131,9 +142,9 @@ async function initializeMap() {
geometry: poiPoint,
symbol: poiSymbol,
})
mapView.graphics.add(poiGraphic)
mapView.value.graphics.add(poiGraphic)
// POI
mapView.goTo({
mapView.value.goTo({
target: [userPoint, poiPoint],
zoom: zoomMap.value,
})
@ -180,9 +191,9 @@ async function initializeMap() {
geometry: poiPoint,
symbol: poiSymbol,
})
mapView.graphics.add(poiGraphic)
mapView.value.graphics.add(poiGraphic)
// POI
mapView.goTo({
mapView.value.goTo({
target: [userPoint, poiPoint],
zoom: zoomMap.value,
})
@ -271,6 +282,14 @@ const requestLocationPermission = () => {
)
}
// Cleanup map resources when component unmounts
onBeforeUnmount(() => {
if (mapView.value) {
mapView.value.destroy()
mapView.value = null
}
})
defineExpose({
requestLocationPermission,
})
@ -309,13 +328,13 @@ defineExpose({
"
>
นทใกลเคยง
<span class="q-px-sm">:</span>
{{ poiPlaceName }}
<q-icon name="mdi-information-outline" size="xs" class="q-mr-xs" />
<q-tooltip anchor="top middle" self="bottom middle" :offset="[0, 5]">
{{ textTooltip }}
</q-tooltip>
<span class="q-px-sm">:</span>
{{ poiPlaceName }}
</div>
</div>

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, watch, onMounted, reactive } from 'vue'
import { PropType } from 'vue'
import type { PropType } from 'vue'
import { useQuasar } from 'quasar'
import moment from 'moment'
@ -38,10 +38,7 @@ const props = defineProps({
},
fetchData: {
type: Function,
require: true,
default: () => {
console.log('fetchData func')
},
required: true,
},
action: {
type: String,

View file

@ -1,6 +1,11 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { GoogleMap, Marker } from 'vue3-google-map'
import { useQuasar } from 'quasar'
const $q = useQuasar()
const googleMapsApiKey = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || ''
declare var google: any
const center = ref<any>()
@ -29,11 +34,32 @@ function findNearestPlace() {
},
(error) => {
console.error(error)
console.log('erroe')
// Show user-friendly error message
let errorMessage = 'ไม่สามารถระบุตำแหน่งได้'
switch (error.code) {
case error.PERMISSION_DENIED:
errorMessage = 'คุณปฏิเสธการอนุญาตให้เข้าถึงตำแหน่ง'
break
case error.POSITION_UNAVAILABLE:
errorMessage = 'ข้อมูลตำแหน่งไม่พร้อมใช้งาน'
break
case error.TIMEOUT:
errorMessage = 'หมดเวลาในการระบุตำแหน่ง'
break
}
$q.notify({
type: 'negative',
message: errorMessage,
position: 'top',
})
}
)
} else {
console.error('เบราว์เซอร์ไม่รองรับการระบุตำแหน่ง')
$q.notify({
type: 'negative',
message: 'เบราว์เซอร์ไม่รองรับการระบุตำแหน่ง',
position: 'top',
})
}
}
@ -69,6 +95,14 @@ function findNearestPlaceFromAPI(userLocation: any) {
onMounted(() => {
findNearestPlace()
})
// Cleanup resources
onBeforeUnmount(() => {
// Clear location reference to prevent memory leaks
center.value = undefined
location.value = ''
test.value = undefined
})
</script>
<template>
@ -80,7 +114,7 @@ onMounted(() => {
>
<div style="width: 100%; height: 90%">
<GoogleMap
api-key="AIzaSyBzPSF5NxUZ1G8DKBnJvJPTqCR0Ct2xf58"
:api-key="googleMapsApiKey"
style="width: 100%; height: 100%"
:center="center"
:zoom="15"

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import { PropType } from 'vue'
import type { PropType } from 'vue'
import type { DataCheckIn } from '@/interface/index/Main'

View file

@ -4,9 +4,11 @@ import { useQuasar } from 'quasar'
import http from '@/plugins/http'
import config from '@/app.config'
import { usePrivacyStore } from '@/stores/privacy'
import { usePositionKeycloakStore } from '@/stores/positionKeycloak'
const $q = useQuasar()
const privacyStore = usePrivacyStore()
const positionKeycloakStore = usePositionKeycloakStore()
const modal = defineModel<boolean>('modal', {
required: true,
@ -78,6 +80,12 @@ const handleAccept = async () => {
accept: true,
})
privacyStore.setAccepted(true)
// privacyCheckin positionKeycloak store
if (positionKeycloakStore.dataPositionKeycloak) {
positionKeycloakStore.dataPositionKeycloak.privacyCheckin = true
}
modal.value = false
} catch (error) {}
}
@ -89,7 +97,7 @@ const toggleDetails = () => {
const checkIfScrollable = () => {
nextTick(() => {
const container = scrollContainer.value?.$el || scrollContainer.value
const container = (scrollContainer.value as any)?.$el || scrollContainer.value
if (container) {
const { scrollHeight, clientHeight } = container

View file

@ -2,42 +2,42 @@
import { ref, watch } from 'vue'
import { useCounterMixin } from '@/stores/mixin'
import { useChekIn } from '@/stores/chekin'
import { useCheckIn } from '@/stores/checkin'
import { is, type QTableProps } from 'quasar'
import type { QTableProps } from 'quasar'
import type { Pagination, DataCheckIn } from '@/interface/index/Main'
import Popup from '@/components/PopUp.vue' // dialog /
import SkeletonTable from '@/components/SkeletonTable.vue' // skeleton table
const { date2Thai } = useCounterMixin()
const stores = useChekIn()
const stores = useCheckIn()
/** props ข้อมูลจาก Components Page HistoryView */
const props = defineProps({
paging: {
type: Boolean,
defualt: false,
default: false,
},
pageSize: {
type: Number,
defualt: 10,
default: 10,
},
page: {
type: Number,
defualt: 1,
default: 1,
},
maxPage: {
type: Number,
defualt: 1,
default: 1,
},
total: {
type: Number,
defualt: 0,
default: 0,
},
tab: {
type: String,
defualt: '',
default: '',
required: true,
},
fetchData: {
@ -254,7 +254,7 @@ function classStatus(status: string) {
case 'ปฏิบัติงานไม่ครบตามกำหนดเวลา':
return 'text-orange-8'
default:
break
return ''
}
}

View file

@ -2,14 +2,14 @@
import { onMounted, ref, watch } from 'vue'
import { useCounterMixin } from '@/stores/mixin'
import { useChekIn } from '@/stores/chekin'
import { useCheckIn } from '@/stores/checkin'
import { calculateFiscalYear } from '@/utils/function'
import type { DataDateMonthObject } from '@/interface/index/Main'
import Popup from '@/components/PopUp.vue'
const stores = useChekIn()
const stores = useCheckIn()
const { monthYear2Thai } = useCounterMixin()
/**
@ -18,11 +18,11 @@ const { monthYear2Thai } = useCounterMixin()
const props = defineProps({
fetchData: {
type: Function,
require: true,
required: true,
},
tab: {
type: String,
require: true,
required: true,
},
})
const emit = defineEmits(['update:year'])
@ -43,8 +43,6 @@ function filterYearFn(type: string) {
const year = type === 'year' ? filterYear.value : dateMonth.value.year
const month = dateMonth.value.month
console.log(year, month)
//
if (isNaN(Number(year)) || isNaN(Number(month))) {
console.warn('Invalid year or month value:', { year, month })