refactor code & fixed location
This commit is contained in:
parent
41c1aa8e45
commit
487a6b520e
23 changed files with 566 additions and 145 deletions
|
|
@ -9,7 +9,8 @@ export default {
|
|||
checkStatus: () => `${leave}/check-status`,
|
||||
|
||||
keycloakLogSSO: `${env.API_URI}/org/keycloak/log/sso`,
|
||||
keycloakPosition: () => `${env.API_URI}/org/profile/keycloak/position`,
|
||||
keycloakPosition: () =>
|
||||
`${env.API_URI}/org/profile/keycloak/position-checkin`,
|
||||
fileByFile: (name: string, group: string, id: string, fileName: string) =>
|
||||
`${urlFile}/file/${name}/${group}/${id}/${fileName}`,
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ''
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
|
|
|||
29
src/interface/keycloak-position.ts
Normal file
29
src/interface/keycloak-position.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Interface for Keycloak position data from /org/profile/keycloak/position API
|
||||
*/
|
||||
export interface KeycloakPosition {
|
||||
/** Privacy consent status */
|
||||
privacyCheckin: boolean
|
||||
/** Profile image filename */
|
||||
avatarName?: string
|
||||
/** User profile ID */
|
||||
profileId: string
|
||||
/** Organization hierarchy data */
|
||||
organization?: Organization
|
||||
}
|
||||
|
||||
/**
|
||||
* Organization hierarchy structure
|
||||
*/
|
||||
export interface Organization {
|
||||
/** Top level organization */
|
||||
root?: string
|
||||
/** Child level 1 */
|
||||
child1?: string
|
||||
/** Child level 2 */
|
||||
child2?: string
|
||||
/** Child level 3 */
|
||||
child3?: string
|
||||
/** Child level 4 */
|
||||
child4?: string
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import App from '@/App.vue'
|
|||
import '@/registerServiceWorker'
|
||||
import router from '@/router'
|
||||
import { createPinia } from 'pinia'
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
|
||||
import { Quasar, Dialog, Notify, Loading } from 'quasar'
|
||||
import '@vuepic/vue-datepicker/dist/main.css'
|
||||
import quasarUserOptions from '@/quasar-user-options'
|
||||
|
|
@ -14,6 +15,7 @@ import http from '@/plugins/http'
|
|||
|
||||
const app = createApp(App)
|
||||
const pinia = createPinia()
|
||||
pinia.use(piniaPluginPersistedstate)
|
||||
|
||||
app.use(router)
|
||||
app.use(pinia)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import Axios, { type AxiosRequestConfig, type AxiosResponse } from 'axios'
|
|||
import { getToken } from './auth'
|
||||
|
||||
const http = Axios.create({
|
||||
timeout: 1000000000, // เพิ่มค่า timeout
|
||||
timeout: 30000, // 30 seconds - reasonable timeout for API calls
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
},
|
||||
|
|
@ -30,6 +30,7 @@ http.interceptors.response.use(
|
|||
// eslint-disable-next-line no-prototype-builtins
|
||||
if (error.hasOwnProperty('response')) {
|
||||
if (error.response.status === 401 || error.response.status === 403) {
|
||||
// TODO: Implement proper logout logic on 401/403
|
||||
// Store.commit("SET_ERROR_MESSAGE", error.response.data.message);
|
||||
// Store.commit("REMOVE_ACCESS_TOKEN")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// // authen with keycloak client
|
||||
// import Keycloak from 'keycloak-js'
|
||||
|
||||
export {}
|
||||
|
||||
// const ACCESS_TOKEN = 'BMAHRIS_KEYCLOAK_IDENTITY'
|
||||
// const REFRESH_TOKEN = 'BMAHRIS_KEYCLOAK_REFRESH'
|
||||
// const keycloakConfig = {
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ const router = createRouter({
|
|||
Auth: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/map',
|
||||
name: 'map',
|
||||
component: MapView,
|
||||
meta: {
|
||||
Auth: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// path: '/map',
|
||||
// name: 'map',
|
||||
// component: MapView,
|
||||
// meta: {
|
||||
// Auth: true,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const mixin = useCounterMixin()
|
|||
const { date2Thai } = mixin
|
||||
|
||||
/** store for checkin history*/
|
||||
export const useChekIn = defineStore('checkin', () => {
|
||||
export const useCheckIn = defineStore('checkin', () => {
|
||||
const year = ref<number>(calculateFiscalYear(new Date())) //ปีงบประมาณ
|
||||
const rows = ref<Datalist[]>([])
|
||||
const tab = ref<string>('history')
|
||||
|
|
@ -1,65 +1,66 @@
|
|||
import { ref } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import type { KeycloakPosition } from '@/interface/keycloak-position'
|
||||
|
||||
export const usePositionKeycloakStore = defineStore('positionKeycloak', () => {
|
||||
const menuData = ref<string[]>([
|
||||
'ลงเวลาปฏิบัติงาน',
|
||||
'ประวัติการลงเวลา',
|
||||
'รายการลงเวลากรณีพิเศษ',
|
||||
])
|
||||
const dataPositionKeycloak = ref<any>(null)
|
||||
export const usePositionKeycloakStore = defineStore(
|
||||
'positionKeycloak',
|
||||
() => {
|
||||
const menuData = ref<string[]>([
|
||||
'ลงเวลาปฏิบัติงาน',
|
||||
'ประวัติการลงเวลา',
|
||||
'รายการลงเวลากรณีพิเศษ',
|
||||
])
|
||||
const dataPositionKeycloak = ref<KeycloakPosition | null>(null)
|
||||
|
||||
function setPositionKeycloak(data: any) {
|
||||
dataPositionKeycloak.value = data
|
||||
}
|
||||
|
||||
function findOrgName(obj: any) {
|
||||
if (obj) {
|
||||
let name =
|
||||
obj.child4 != null &&
|
||||
obj.child4 !== '' &&
|
||||
obj.child3 != null &&
|
||||
obj.child3 !== ''
|
||||
? obj.child4 + (obj.child3 ? '/' : '')
|
||||
: obj.child4 != null && obj.child4 !== ''
|
||||
? obj.child4
|
||||
: ''
|
||||
|
||||
name +=
|
||||
obj.child3 != null &&
|
||||
obj.child3 !== '' &&
|
||||
obj.child2 != null &&
|
||||
obj.child2 !== ''
|
||||
? obj.child3 + (obj.child2 ? '/' : '')
|
||||
: obj.child3 != null && obj.child3 !== ''
|
||||
? obj.child3
|
||||
: ''
|
||||
|
||||
name +=
|
||||
obj.child2 != null &&
|
||||
obj.child2 !== '' &&
|
||||
obj.child1 != null &&
|
||||
obj.child1 !== ''
|
||||
? obj.child2 + (obj.child1 ? '/' : '')
|
||||
: obj.child2 != null && obj.child2 !== ''
|
||||
? obj.child2
|
||||
: ''
|
||||
|
||||
name +=
|
||||
obj.child1 != null &&
|
||||
obj.child1 !== '' &&
|
||||
obj.root != null &&
|
||||
obj.root !== ''
|
||||
? obj.child1 + (obj.root ? '/' : '')
|
||||
: obj.child1 != null && obj.child1 !== ''
|
||||
? obj.child1
|
||||
: ''
|
||||
name += obj.root != null && obj.root !== '' ? obj.root : ''
|
||||
return name == '' ? '-' : name
|
||||
} else {
|
||||
return ''
|
||||
function setPositionKeycloak(data: KeycloakPosition) {
|
||||
dataPositionKeycloak.value = data
|
||||
}
|
||||
}
|
||||
|
||||
return { setPositionKeycloak, dataPositionKeycloak, findOrgName, menuData }
|
||||
})
|
||||
/**
|
||||
* ล้างข้อมูล Keycloak position (ใช้เมื่อ logout)
|
||||
*/
|
||||
function clearPositionKeycloak() {
|
||||
dataPositionKeycloak.value = null
|
||||
// ลบ localStorage ด้วยเพื่อให้แน่ใจว่าข้อมูลหายจริง
|
||||
localStorage.removeItem('positionKeycloak')
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้างชื่อหน่วยงานจากข้อมูลลำดับชั้นองค์กร
|
||||
* @param obj ข้อมูลองค์กรที่มีโครงสร้าง child4 -> child3 -> child2 -> child1 -> root
|
||||
* @returns ชื่อหน่วยงานในรูปแบบ string เชื่อมด้วย /
|
||||
*/
|
||||
function findOrgName(obj: KeycloakPosition | null): string {
|
||||
if (!obj?.organization) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const org = obj.organization
|
||||
const levels = ['child4', 'child3', 'child2', 'child1', 'root'] as const
|
||||
const parts: string[] = []
|
||||
|
||||
for (const level of levels) {
|
||||
const value = org[level]
|
||||
if (value) {
|
||||
parts.push(value)
|
||||
}
|
||||
}
|
||||
|
||||
return parts.length > 0 ? parts.join('/') : '-'
|
||||
}
|
||||
|
||||
return {
|
||||
setPositionKeycloak,
|
||||
clearPositionKeycloak,
|
||||
dataPositionKeycloak,
|
||||
findOrgName,
|
||||
menuData,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: {
|
||||
key: 'positionKeycloak',
|
||||
storage: localStorage, // ใช้ localStorage เพื่อให้ข้อมูลอยู่ถาวร
|
||||
},
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useQuasar } from 'quasar'
|
|||
import { useRouter } from 'vue-router'
|
||||
import http from '@/plugins/http'
|
||||
import config from '@/app.config'
|
||||
import { useChekIn } from '@/stores/chekin'
|
||||
import { useCheckIn } from '@/stores/checkin'
|
||||
import { useCounterMixin } from '@/stores/mixin'
|
||||
import { calculateFiscalYear } from '@/utils/function'
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ import ToolBar from '@/components/ToolBar.vue' // เมนู Herder
|
|||
|
||||
const $q = useQuasar() //ใช้ noti quasar
|
||||
const router = useRouter()
|
||||
const stores = useChekIn()
|
||||
const stores = useCheckIn()
|
||||
const { showLoader, hideLoader, messageError } = useCounterMixin()
|
||||
const { fetchHistoryList } = stores
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ watch(
|
|||
:tab="stores.tab"
|
||||
/>
|
||||
<TableHistory
|
||||
:fetchData="functionFetch"
|
||||
:fetch-data="functionFetch"
|
||||
:page-size="pageSize"
|
||||
:total="total"
|
||||
:page="page"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch, onBeforeUnmount } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import moment from 'moment'
|
||||
import { format } from 'date-fns'
|
||||
import Camera from 'simple-vue-camera'
|
||||
|
||||
import config from '@/app.config'
|
||||
|
|
@ -69,15 +69,16 @@ const formattedM = ref()
|
|||
const formattedH = ref()
|
||||
const formattedHH = ref()
|
||||
const formattedA = ref()
|
||||
const clockInterval = ref<number | NodeJS.Timeout | undefined>()
|
||||
|
||||
/** function อัพเดทเวลา*/
|
||||
function updateClock() {
|
||||
const date = Date.now()
|
||||
const hh = moment(date).format('HH')
|
||||
const mm = moment(date).format('mm')
|
||||
const ss = moment(date).format('ss')
|
||||
const HH = moment(date).format('hh')
|
||||
const A = moment(date).format('a')
|
||||
const date = new Date()
|
||||
const hh = format(date, 'HH')
|
||||
const mm = format(date, 'mm')
|
||||
const ss = format(date, 'ss')
|
||||
const HH = format(date, 'hh')
|
||||
const A = format(date, 'a')
|
||||
|
||||
formattedS.value = ss
|
||||
formattedM.value = mm
|
||||
|
|
@ -85,7 +86,6 @@ function updateClock() {
|
|||
formattedHH.value = HH
|
||||
formattedA.value = A
|
||||
}
|
||||
setInterval(updateClock, 1000)
|
||||
|
||||
/** form-data */
|
||||
const formLocation = reactive({
|
||||
|
|
@ -137,7 +137,10 @@ async function getDelayedFreshPosition() {
|
|||
maximumAge: 0,
|
||||
})
|
||||
|
||||
await wait(MOCK_CHECK_DELAY_MS)
|
||||
// Only add delay in development mode for testing
|
||||
if (import.meta.env.DEV) {
|
||||
await wait(MOCK_CHECK_DELAY_MS)
|
||||
}
|
||||
|
||||
try {
|
||||
return await getCurrentPositionAsync({
|
||||
|
|
@ -191,7 +194,7 @@ const availableCameras = ref<any[]>([])
|
|||
const currentCameraIndex = ref<number>(0)
|
||||
const currentCameraType = ref<'front' | 'back' | 'unknown'>('unknown')
|
||||
|
||||
const intervalId = ref<number | undefined>(undefined) // ต้องใช้ตัวแปรเก็บค่า interval
|
||||
const intervalId = ref<number | NodeJS.Timeout | undefined>(undefined) // ต้องใช้ตัวแปรเก็บค่า interval
|
||||
|
||||
/**
|
||||
* เริ่มจาก onMounted #1 เช็ค status คิว
|
||||
|
|
@ -246,18 +249,6 @@ async function fetchCheckStatus() {
|
|||
}
|
||||
}
|
||||
|
||||
/** ตัวเก่าก่อนเปลี่ยน */
|
||||
|
||||
// async function stopChecking() {
|
||||
// if (intervalId.value !== undefined) {
|
||||
// clearInterval(intervalId.value) // หยุด interval
|
||||
// setTimeout(() => {
|
||||
// fetchCheckTime()
|
||||
// }, 1000)
|
||||
// intervalId.value = undefined // รีเซ็ตค่า
|
||||
// }
|
||||
// }
|
||||
|
||||
/** ตัวใหม่ที่เปลี่ยนก่อนเปลี่ยน
|
||||
* เริ่มจาก onMounted #3 เช็ค status คิว
|
||||
*
|
||||
|
|
@ -674,6 +665,7 @@ async function requestCamera() {
|
|||
onMounted(async () => {
|
||||
isLoadingCheckTime.value = true
|
||||
updateClock()
|
||||
clockInterval.value = setInterval(updateClock, 1000)
|
||||
startChecking() //เช็ค status จาก คิว #1
|
||||
|
||||
// เรียกแผนที่เฉพาะเมื่อยอมรับ privacy แล้ว
|
||||
|
|
@ -690,6 +682,12 @@ onMounted(async () => {
|
|||
onBeforeUnmount(() => {
|
||||
resetCameraAndImage()
|
||||
|
||||
// หยุด clock interval
|
||||
if (clockInterval.value !== undefined) {
|
||||
clearInterval(clockInterval.value)
|
||||
clockInterval.value = undefined
|
||||
}
|
||||
|
||||
// หยุด interval ถ้ามี
|
||||
if (intervalId.value !== undefined) {
|
||||
clearInterval(intervalId.value)
|
||||
|
|
@ -1279,7 +1277,7 @@ watch(
|
|||
</div>
|
||||
<div class="col-12 text-center row q-pt-md">
|
||||
<div class="col-12 text-subtitle1 text-weight-medium text-secondary">
|
||||
พื้นที่ใกล้เคียง {{ formLocation.POI ?? formLocation.POI }}
|
||||
พื้นที่ใกล้เคียง {{ formLocation.POI }}
|
||||
</div>
|
||||
<div class="col-12 text-subtitle1 text-weight-medium text-secondary">
|
||||
{{ location }}
|
||||
|
|
@ -1318,7 +1316,7 @@ watch(
|
|||
.card-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: autu; /* Adjust as needed */
|
||||
height: auto; /* Adjust as needed */
|
||||
background: #f5f5f5;
|
||||
height: 35vh !important;
|
||||
box-shadow: none !important;
|
||||
|
|
@ -1327,7 +1325,7 @@ watch(
|
|||
.card-container-xs {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: autu; /* Adjust as needed */
|
||||
height: auto; /* Adjust as needed */
|
||||
background: #ffffff;
|
||||
height: 35vh !important;
|
||||
border-radius: 10px;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { logout, tokenParsed, getCookie, gotoLeavePage } from '@/plugins/auth'
|
|||
import { useCounterMixin } from '@/stores/mixin'
|
||||
import { usePrivacyStore } from '@/stores/privacy'
|
||||
import { usePositionKeycloakStore } from '@/stores/positionKeycloak'
|
||||
import type { KeycloakPosition } from '@/interface/keycloak-position'
|
||||
|
||||
// import type { notiType } from '@/interface/index/Main'
|
||||
// import type { Noti } from '@/interface/response/Main'
|
||||
|
|
@ -144,6 +145,8 @@ function onClickLogout() {
|
|||
ok: 'ยืนยัน',
|
||||
persistent: true,
|
||||
}).onOk(async () => {
|
||||
// ล้างข้อมูล positionKeycloak ก่อน logout
|
||||
positionKeycloakStore.clearPositionKeycloak()
|
||||
await http.post(config.API.keycloakLogSSO, { text: 'ออกจากระบบ' })
|
||||
await logout()
|
||||
})
|
||||
|
|
@ -174,16 +177,50 @@ const landingPageUrl = ref<string>(configParam.landingPageUrl)
|
|||
|
||||
/** ฟังก์ชันเรียกข้อมูลผู้ใช่งาน*/
|
||||
async function fetchKeycloakPosition() {
|
||||
// เช็คว่ามีข้อมูลใน store อยู่แล้วหรือไม่ (จาก localStorage)
|
||||
const existingData = positionKeycloakStore.dataPositionKeycloak
|
||||
|
||||
if (existingData) {
|
||||
// มีข้อมูลอยู่แล้ว ใช้ข้อมูลเดิม
|
||||
privacyStore.modalPrivacy = !existingData.privacyCheckin
|
||||
privacyStore.setAccepted(existingData.privacyCheckin)
|
||||
|
||||
//เช็คว่ามีรูปไหม ถ้ามีรูปเรียกข้อมูลรูป
|
||||
if (existingData.avatarName) {
|
||||
getImg(existingData.profileId, existingData.avatarName)
|
||||
} else {
|
||||
profileImg.value = avatar
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ไม่มีข้อมูล ให้ fetch จาก API
|
||||
await http
|
||||
.get(config.API.keycloakPosition())
|
||||
.then(async (res) => {
|
||||
const data = await res.data.result
|
||||
positionKeycloakStore.setPositionKeycloak(data)
|
||||
privacyStore.modalPrivacy = !data.privacyCheckin ? true : false
|
||||
privacyStore.setAccepted(data.privacyCheckin)
|
||||
const apiData = await res.data.result
|
||||
|
||||
// Map API response to KeycloakPosition interface
|
||||
const keycloakData: KeycloakPosition = {
|
||||
privacyCheckin: apiData.privacyCheckin ?? false,
|
||||
avatarName: apiData.avatarName,
|
||||
profileId: apiData.profileId,
|
||||
organization: {
|
||||
root: apiData.root,
|
||||
child1: apiData.child1,
|
||||
child2: apiData.child2,
|
||||
child3: apiData.child3,
|
||||
child4: apiData.child4,
|
||||
},
|
||||
}
|
||||
|
||||
positionKeycloakStore.setPositionKeycloak(keycloakData)
|
||||
privacyStore.modalPrivacy = !keycloakData.privacyCheckin
|
||||
privacyStore.setAccepted(keycloakData.privacyCheckin)
|
||||
|
||||
//เช็คว่ามีรูปไหม ถ้ามีรูปเรียกข้อมูลรูป
|
||||
if (data.avatarName) {
|
||||
getImg(data.profileId, data.avatarName)
|
||||
if (keycloakData.avatarName) {
|
||||
getImg(keycloakData.profileId, keycloakData.avatarName)
|
||||
} else {
|
||||
profileImg.value = avatar
|
||||
}
|
||||
|
|
@ -297,6 +334,7 @@ onMounted(async () => {
|
|||
const checkTokenParsed = await tokenParsed()
|
||||
const SSO_TOKEN = await getCookie('SSO')
|
||||
isSsoToken.value = SSO_TOKEN === 'y' ? true : false
|
||||
|
||||
if (checkTokenParsed != null) {
|
||||
fullName.value = checkTokenParsed.name
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue