fix switch Landscape -> Portrait
This commit is contained in:
parent
ec22714f01
commit
e9d28197df
5 changed files with 964 additions and 279 deletions
5
.claude/agent-memory/hrms-checkin-expert/MEMORY.md
Normal file
5
.claude/agent-memory/hrms-checkin-expert/MEMORY.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# HRMS Check-in Expert Memory
|
||||
|
||||
## Project Issues & Fixes
|
||||
|
||||
- [Position Orientation Change Fix](issue_position_orientation_change_fix.md) - Fix for position map not displaying on screen orientation changes
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
name: position_orientation_change_fix
|
||||
description: Fix for position map not displaying on screen orientation changes
|
||||
type: project
|
||||
---
|
||||
|
||||
## Issue: Position/Geolocation Not Displaying on Screen Orientation Changes
|
||||
|
||||
**Problem:**
|
||||
The ArcGIS map component (`AscGISMap.vue`) failed to display or update position when the screen orientation changed between portrait and landscape modes.
|
||||
|
||||
**Root Cause:**
|
||||
1. The component uses `v-if="$q.screen.gt.xs"` for conditional rendering of different layouts
|
||||
2. When orientation changes, the ArcGIS MapView doesn't automatically resize to fit the new container dimensions
|
||||
3. ArcGIS MapView requires manual `resize()` call when its container's size changes
|
||||
|
||||
**Solution Implemented:**
|
||||
Added screen resize handling to `src/components/AscGISMap.vue`:
|
||||
|
||||
1. **Added state tracking:**
|
||||
- `isMapInitialized` ref to track when map is ready
|
||||
- `isInitializing` ref to prevent concurrent initializations
|
||||
|
||||
2. **Added `handleMapResize()` function:**
|
||||
- Checks if mapView exists and isn't destroyed
|
||||
- Uses `nextTick()` to ensure DOM updates complete before resizing
|
||||
- Calls `mapView.value.resize()` to update map dimensions
|
||||
|
||||
3. **Added watcher on `$q.screen.gt.xs`:**
|
||||
- Triggers when screen size crosses the xs breakpoint
|
||||
- Waits for DOM update via `nextTick()`
|
||||
- Calls `handleMapResize()` to adjust map
|
||||
|
||||
4. **Added window resize event listener:**
|
||||
- Falls back for orientation changes that might not trigger Quasar's screen watcher
|
||||
- Debounced with 300ms timeout to avoid excessive calls
|
||||
- Properly cleaned up on component unmount
|
||||
|
||||
**Files Modified:**
|
||||
- `src/components/AscGISMap.vue` - Added resize handling logic
|
||||
|
||||
**Best Practices Applied:**
|
||||
- Proper cleanup of event listeners in `onBeforeUnmount`
|
||||
- Debouncing resize events for performance
|
||||
- Using `nextTick()` to ensure DOM synchronization
|
||||
- Checking for destroyed state before calling map methods
|
||||
|
||||
**Note:** The Google Maps component (`MapCheckin.vue`) was not affected as `vue3-google-map` handles resize automatically.
|
||||
|
|
@ -1,5 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, shallowRef, onBeforeUnmount } from 'vue'
|
||||
import {
|
||||
ref,
|
||||
shallowRef,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
watch,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import { loadModules } from 'esri-loader'
|
||||
import axios from 'axios'
|
||||
import { useCounterMixin } from '@/stores/mixin'
|
||||
|
|
@ -11,12 +18,24 @@ const { messageError } = mixin
|
|||
const privacyStore = usePrivacyStore()
|
||||
|
||||
const emit = defineEmits(['update:location'])
|
||||
|
||||
// Accept initial POI from parent to prevent skeleton on re-mount
|
||||
const props = defineProps<{
|
||||
initialPOI?: string
|
||||
}>()
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
// Throttle mechanism to prevent excessive location updates
|
||||
const LOCATION_UPDATE_THROTTLE_MS = 500
|
||||
let lastLocationUpdate = 0
|
||||
|
||||
// Track if map is initialized to prevent re-initialization
|
||||
const isMapInitialized = ref<boolean>(false)
|
||||
const isInitializing = ref<boolean>(false)
|
||||
const currentScreenSize = ref<boolean>($q.screen.gt.xs) // Track screen size to detect changes
|
||||
const mobileMapExpanded = ref<boolean>(true)
|
||||
|
||||
function updateLocation(latitude: number, longitude: number, namePOI: string) {
|
||||
const now = Date.now()
|
||||
// Skip update if called too frequently (within throttle period)
|
||||
|
|
@ -28,8 +47,15 @@ function updateLocation(latitude: number, longitude: number, namePOI: string) {
|
|||
emit('update:location', latitude, longitude, namePOI)
|
||||
}
|
||||
|
||||
const poiPlaceName = ref<string>('') // ชื่อพื้นที่ใกล้เคียง
|
||||
const poiPlaceName = ref<string>(props.initialPOI || '') // ชื่อพื้นที่ใกล้เคียง - use initialPOI if provided
|
||||
const mapView = shallowRef<any>(null) // Store mapView reference for cleanup (use shallowRef to avoid reactivity issues)
|
||||
const desktopMapContainerRef = ref<HTMLElement | null>(null)
|
||||
const mobileMapContainerRef = ref<HTMLElement | null>(null)
|
||||
|
||||
// Unique container ID for each component instance to avoid conflicts
|
||||
const mapContainerId = `mapViewDisplay_${Math.random()
|
||||
.toString(36)
|
||||
.substring(2, 9)}`
|
||||
|
||||
// ArcGIS API key from environment variable
|
||||
const apiKey = ref<string>(
|
||||
|
|
@ -39,174 +65,457 @@ const zoomMap = ref<number>(18)
|
|||
const textTooltip = ref<string>(
|
||||
'พื้นที่ใกล้เคียงคือ สถานที่สำคัญรอบตัวคุณ (ไม่ใช่ตำแหน่งปัจจุบัน)'
|
||||
)
|
||||
const MAP_DEBUG = true
|
||||
let attachRetryTimer: number | undefined
|
||||
let mapInitVersion = 0
|
||||
let isRecoveryReinitializing = false
|
||||
|
||||
function logMapDebug(event: string, payload?: Record<string, unknown>) {
|
||||
if (!MAP_DEBUG) {
|
||||
return
|
||||
}
|
||||
|
||||
const mode = $q.screen.gt.xs ? 'desktop' : 'mobile'
|
||||
console.log('[AscGISMap]', event, {
|
||||
mode,
|
||||
isMapInitialized: isMapInitialized.value,
|
||||
isInitializing: isInitializing.value,
|
||||
hasMapView: !!mapView.value,
|
||||
mapDestroyed: !!mapView.value?.destroyed,
|
||||
...payload,
|
||||
})
|
||||
}
|
||||
|
||||
function getActiveMapContainer() {
|
||||
return $q.screen.gt.xs
|
||||
? desktopMapContainerRef.value
|
||||
: mobileMapContainerRef.value
|
||||
}
|
||||
|
||||
async function waitForActiveMapContainer(maxAttempts = 25, delayMs = 120) {
|
||||
for (let i = 0; i < maxAttempts; i += 1) {
|
||||
await nextTick()
|
||||
const container = getActiveMapContainer()
|
||||
if (i === 0 || i === maxAttempts - 1) {
|
||||
logMapDebug('waitForContainer:attempt', {
|
||||
attempt: i + 1,
|
||||
maxAttempts,
|
||||
width: container?.clientWidth || 0,
|
||||
height: container?.clientHeight || 0,
|
||||
})
|
||||
}
|
||||
|
||||
if (container && container.clientWidth > 0 && container.clientHeight > 0) {
|
||||
logMapDebug('waitForContainer:ready', {
|
||||
attempt: i + 1,
|
||||
width: container.clientWidth,
|
||||
height: container.clientHeight,
|
||||
})
|
||||
return container
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs))
|
||||
}
|
||||
|
||||
logMapDebug('waitForContainer:failed')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function clearAttachRetryTimer() {
|
||||
if (attachRetryTimer) {
|
||||
clearTimeout(attachRetryTimer)
|
||||
attachRetryTimer = undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function recoverMapByReinitialize() {
|
||||
if (isRecoveryReinitializing) {
|
||||
logMapDebug('recover:skipAlreadyRunning')
|
||||
return
|
||||
}
|
||||
|
||||
isRecoveryReinitializing = true
|
||||
logMapDebug('recover:start', { mapInitVersion })
|
||||
|
||||
try {
|
||||
mapInitVersion += 1
|
||||
isInitializing.value = false
|
||||
clearAttachRetryTimer()
|
||||
|
||||
if (mapView.value && !mapView.value.destroyed) {
|
||||
mapView.value.destroy()
|
||||
mapView.value = null
|
||||
}
|
||||
|
||||
isMapInitialized.value = false
|
||||
await nextTick()
|
||||
await initializeMap()
|
||||
logMapDebug('recover:initializeMapCompleted', { mapInitVersion })
|
||||
} finally {
|
||||
isRecoveryReinitializing = false
|
||||
logMapDebug('recover:end', { mapInitVersion })
|
||||
}
|
||||
}
|
||||
|
||||
function reattachAndResizeMap(retry = 0) {
|
||||
if (!mapView.value || mapView.value.destroyed) {
|
||||
logMapDebug('reattach:skipNoMapView', { retry })
|
||||
return
|
||||
}
|
||||
|
||||
const activeContainer = getActiveMapContainer()
|
||||
|
||||
if (
|
||||
!activeContainer ||
|
||||
activeContainer.clientWidth === 0 ||
|
||||
activeContainer.clientHeight === 0
|
||||
) {
|
||||
logMapDebug('reattach:containerNotReady', {
|
||||
retry,
|
||||
width: activeContainer?.clientWidth || 0,
|
||||
height: activeContainer?.clientHeight || 0,
|
||||
})
|
||||
if (retry < 15) {
|
||||
clearAttachRetryTimer()
|
||||
attachRetryTimer = window.setTimeout(() => {
|
||||
reattachAndResizeMap(retry + 1)
|
||||
}, 140)
|
||||
} else if (!isRecoveryReinitializing) {
|
||||
logMapDebug('reattach:triggerRecovery')
|
||||
void recoverMapByReinitialize()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
clearAttachRetryTimer()
|
||||
|
||||
nextTick(() => {
|
||||
if (!mapView.value || mapView.value.destroyed) {
|
||||
logMapDebug('reattach:skipDestroyedAfterTick', { retry })
|
||||
return
|
||||
}
|
||||
|
||||
if (mapView.value.container !== activeContainer) {
|
||||
logMapDebug('reattach:rebindContainer', {
|
||||
width: activeContainer.clientWidth,
|
||||
height: activeContainer.clientHeight,
|
||||
})
|
||||
mapView.value.container = null
|
||||
mapView.value.container = activeContainer
|
||||
}
|
||||
|
||||
logMapDebug('reattach:resize', {
|
||||
retry,
|
||||
width: activeContainer.clientWidth,
|
||||
height: activeContainer.clientHeight,
|
||||
center: mapView.value.center,
|
||||
zoom: mapView.value.zoom,
|
||||
})
|
||||
mapView.value.resize()
|
||||
mapView.value.requestRender?.()
|
||||
mapView.value
|
||||
.goTo(
|
||||
{
|
||||
center: mapView.value.center,
|
||||
zoom: mapView.value.zoom,
|
||||
},
|
||||
{ animate: false }
|
||||
)
|
||||
.catch(() => {
|
||||
// Ignore interrupted goTo during rapid orientation changes.
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function initializeMap() {
|
||||
if (isInitializing.value || (mapView.value && !mapView.value.destroyed)) {
|
||||
logMapDebug('initialize:skipAlreadyRunningOrReady')
|
||||
return
|
||||
}
|
||||
|
||||
const initVersion = ++mapInitVersion
|
||||
logMapDebug('initialize:start', { initVersion })
|
||||
|
||||
isInitializing.value = true
|
||||
|
||||
try {
|
||||
// Load modules of ArcGIS
|
||||
loadModules([
|
||||
'esri/config',
|
||||
'esri/Map',
|
||||
'esri/views/MapView',
|
||||
'esri/geometry/Point',
|
||||
'esri/Graphic',
|
||||
'esri/layers/TileLayer',
|
||||
]).then(async ([esriConfig, Map, MapView, Point, Graphic, TileLayer]) => {
|
||||
// Set apiKey
|
||||
// esriConfig.apiKey =
|
||||
// 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP'
|
||||
const [esriConfig, Map, MapView, Point, Graphic, TileLayer] =
|
||||
await loadModules([
|
||||
'esri/config',
|
||||
'esri/Map',
|
||||
'esri/views/MapView',
|
||||
'esri/geometry/Point',
|
||||
'esri/Graphic',
|
||||
'esri/layers/TileLayer',
|
||||
])
|
||||
logMapDebug('initialize:modulesLoaded', { initVersion })
|
||||
// Set apiKey
|
||||
// esriConfig.apiKey =
|
||||
// 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP'
|
||||
|
||||
// Create a FeatureLayer using a custom server URL
|
||||
// const hillshadeLayer = new TileLayer({
|
||||
// url: `https://bmagis.bangkok.go.th/arcgis/rest/services/cache/BMA_3D_2D_Cache/MapServer`,
|
||||
// })
|
||||
// Create a FeatureLayer using a custom server URL
|
||||
// const hillshadeLayer = new TileLayer({
|
||||
// url: `https://bmagis.bangkok.go.th/arcgis/rest/services/cache/BMA_3D_2D_Cache/MapServer`,
|
||||
// })
|
||||
|
||||
const map = new Map({
|
||||
basemap: 'streets',
|
||||
// basemap: 'arcgis-topographic',
|
||||
// layers: [hillshadeLayer],
|
||||
const map = new Map({
|
||||
basemap: 'streets',
|
||||
// basemap: 'arcgis-topographic',
|
||||
// layers: [hillshadeLayer],
|
||||
})
|
||||
|
||||
if (initVersion !== mapInitVersion) {
|
||||
logMapDebug('initialize:cancelledAfterMapCreated', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(async (position) => {
|
||||
const { latitude, longitude } = position.coords
|
||||
|
||||
mapView.value = new MapView({
|
||||
container: 'mapViewDisplay',
|
||||
map: map,
|
||||
center: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
}, // Set the initial map center current position
|
||||
|
||||
zoom: zoomMap.value,
|
||||
constraints: {
|
||||
snapToZoom: false, // Disables snapping to the zoom level
|
||||
minZoom: zoomMap.value, // Set minimum zoom level
|
||||
maxZoom: zoomMap.value, // Set maximum zoom level (same as minZoom for fixed zoom)
|
||||
},
|
||||
|
||||
ui: {
|
||||
components: [], // Empty array to remove all default UI components
|
||||
},
|
||||
const position = await new Promise<GeolocationPosition>(
|
||||
(resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// ตำแหน่งของผู้ใช้
|
||||
const userPoint = new Point({ longitude, latitude })
|
||||
const userSymbol = {
|
||||
if (initVersion !== mapInitVersion) {
|
||||
logMapDebug('initialize:cancelledAfterGeolocation', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const { latitude, longitude } = position.coords
|
||||
logMapDebug('initialize:geolocationSuccess', {
|
||||
initVersion,
|
||||
latitude,
|
||||
longitude,
|
||||
})
|
||||
|
||||
const mapContainer = await waitForActiveMapContainer()
|
||||
|
||||
if (!mapContainer || initVersion !== mapInitVersion) {
|
||||
logMapDebug('initialize:containerUnavailableOrCancelled', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
isMapInitialized.value = false
|
||||
return
|
||||
}
|
||||
|
||||
logMapDebug('initialize:containerReady', {
|
||||
initVersion,
|
||||
width: mapContainer.clientWidth,
|
||||
height: mapContainer.clientHeight,
|
||||
})
|
||||
|
||||
mapView.value = new MapView({
|
||||
container: mapContainer,
|
||||
map: map,
|
||||
center: {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
}, // Set the initial map center current position
|
||||
|
||||
zoom: zoomMap.value,
|
||||
constraints: {
|
||||
snapToZoom: false, // Disables snapping to the zoom level
|
||||
minZoom: zoomMap.value, // Set minimum zoom level
|
||||
maxZoom: zoomMap.value, // Set maximum zoom level (same as minZoom for fixed zoom)
|
||||
},
|
||||
|
||||
ui: {
|
||||
components: [], // Empty array to remove all default UI components
|
||||
},
|
||||
})
|
||||
|
||||
await mapView.value.when()
|
||||
logMapDebug('initialize:mapViewReady', { initVersion })
|
||||
|
||||
if (initVersion !== mapInitVersion) {
|
||||
logMapDebug('initialize:cancelledAfterMapViewReady', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
if (mapView.value && !mapView.value.destroyed) {
|
||||
mapView.value.destroy()
|
||||
}
|
||||
mapView.value = null
|
||||
return
|
||||
}
|
||||
|
||||
isMapInitialized.value = true
|
||||
reattachAndResizeMap()
|
||||
|
||||
// ตำแหน่งของผู้ใช้
|
||||
const userPoint = new Point({ longitude, latitude })
|
||||
const userSymbol = {
|
||||
type: 'picture-marker',
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/red.png',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
}
|
||||
const userGraphic = new Graphic({
|
||||
geometry: userPoint,
|
||||
symbol: userSymbol,
|
||||
})
|
||||
mapView.value.graphics.add(userGraphic)
|
||||
// Get POI place ยิงไปขอที่ server ของกทม.ก่อน
|
||||
// await axios
|
||||
// .get(
|
||||
// 'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
// {
|
||||
// params: {
|
||||
// f: 'json', // Format JSON response
|
||||
// distance: 2000,
|
||||
// category: 'POI',
|
||||
// location: {
|
||||
// spatialReference: { wkid: 4326 },
|
||||
// x: longitude,
|
||||
// y: latitude,
|
||||
// },
|
||||
// token: apiKey.value,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// .then((response) => {
|
||||
// // console.log('poi', response.data.location)
|
||||
// poiPlaceName.value = response.data.address
|
||||
// ? response.data.address.PlaceName === ''
|
||||
// ? response.data.address.ShortLabel
|
||||
// : response.data.address.PlaceName
|
||||
// : 'ไม่พบข้อมูล'
|
||||
// const poiPoint = new Point({
|
||||
// longitude: response.data.location.x,
|
||||
// latitude: response.data.location.y,
|
||||
// })
|
||||
// const poiSymbol = {
|
||||
// type: 'picture-marker',
|
||||
// url: 'http://maps.google.com/mapfiles/ms/icons/blue.png',
|
||||
// width: '32px',
|
||||
// height: '32px',
|
||||
// }
|
||||
// const poiGraphic = new Graphic({
|
||||
// geometry: poiPoint,
|
||||
// symbol: poiSymbol,
|
||||
// })
|
||||
// mapView.value.graphics.add(poiGraphic)
|
||||
// // อัปเดตการแสดงผลให้แสดงทั้งตำแหน่งของผู้ใช้และ POI
|
||||
// mapView.value.goTo({
|
||||
// target: [userPoint, poiPoint],
|
||||
// zoom: zoomMap.value,
|
||||
// })
|
||||
|
||||
// // Mark map as initialized
|
||||
// isMapInitialized.value = true
|
||||
|
||||
// updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
// })
|
||||
// .catch(async (error) => {
|
||||
// console.error('Error fetching points of interest:', error)
|
||||
// Get POI place ยิงไปขอที่ server arcgis ไม่ต้องใช้ token
|
||||
await axios
|
||||
.get(
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (initVersion !== mapInitVersion || !mapView.value) {
|
||||
logMapDebug('initialize:poiIgnoredByVersionOrMap', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
const poiSymbol = {
|
||||
type: 'picture-marker',
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/red.png',
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/blue.png',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
}
|
||||
const userGraphic = new Graphic({
|
||||
geometry: userPoint,
|
||||
symbol: userSymbol,
|
||||
const poiGraphic = new Graphic({
|
||||
geometry: poiPoint,
|
||||
symbol: poiSymbol,
|
||||
})
|
||||
mapView.value.graphics.add(poiGraphic)
|
||||
// อัปเดตการแสดงผลให้แสดงทั้งตำแหน่งของผู้ใช้และ POI
|
||||
mapView.value.goTo({
|
||||
target: [userPoint, poiPoint],
|
||||
zoom: zoomMap.value,
|
||||
})
|
||||
mapView.value.graphics.add(userGraphic)
|
||||
// Get POI place ยิงไปขอที่ server ของกทม.ก่อน
|
||||
await axios
|
||||
.get(
|
||||
'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
token: apiKey.value,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
const poiSymbol = {
|
||||
type: 'picture-marker',
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/blue.png',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
}
|
||||
const poiGraphic = new Graphic({
|
||||
geometry: poiPoint,
|
||||
symbol: poiSymbol,
|
||||
})
|
||||
mapView.value.graphics.add(poiGraphic)
|
||||
// อัปเดตการแสดงผลให้แสดงทั้งตำแหน่งของผู้ใช้และ POI
|
||||
mapView.value.goTo({
|
||||
target: [userPoint, poiPoint],
|
||||
zoom: zoomMap.value,
|
||||
})
|
||||
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch(async (error) => {
|
||||
// console.error('Error fetching points of interest:', error)
|
||||
// Get POI place ยิงไปขอที่ server arcgis ไม่ต้องใช้ token
|
||||
await axios
|
||||
.get(
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
const poiSymbol = {
|
||||
type: 'picture-marker',
|
||||
url: 'http://maps.google.com/mapfiles/ms/icons/blue.png',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
}
|
||||
const poiGraphic = new Graphic({
|
||||
geometry: poiPoint,
|
||||
symbol: poiSymbol,
|
||||
})
|
||||
mapView.value.graphics.add(poiGraphic)
|
||||
// อัปเดตการแสดงผลให้แสดงทั้งตำแหน่งของผู้ใช้และ POI
|
||||
mapView.value.goTo({
|
||||
target: [userPoint, poiPoint],
|
||||
zoom: zoomMap.value,
|
||||
})
|
||||
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch((error) => {
|
||||
// console.error('Error fetching points of interest:', error)
|
||||
})
|
||||
})
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
logMapDebug('initialize:poiSuccess', {
|
||||
initVersion,
|
||||
poiPlaceName: poiPlaceName.value,
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
if (initVersion !== mapInitVersion) {
|
||||
logMapDebug('initialize:poiErrorIgnoredByVersion', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
})
|
||||
return
|
||||
}
|
||||
poiPlaceName.value = poiPlaceName.value || 'ไม่พบข้อมูล'
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
logMapDebug('initialize:poiErrorFallback', {
|
||||
initVersion,
|
||||
poiPlaceName: poiPlaceName.value,
|
||||
})
|
||||
})
|
||||
|
||||
if (initVersion === mapInitVersion) {
|
||||
reattachAndResizeMap()
|
||||
}
|
||||
} catch (error) {
|
||||
if (initVersion === mapInitVersion) {
|
||||
isMapInitialized.value = false
|
||||
}
|
||||
logMapDebug('initialize:error', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
})
|
||||
console.error('Error loading the map', error)
|
||||
} finally {
|
||||
if (initVersion === mapInitVersion) {
|
||||
isInitializing.value = false
|
||||
}
|
||||
logMapDebug('initialize:end', {
|
||||
initVersion,
|
||||
mapInitVersion,
|
||||
isMapInitialized: isMapInitialized.value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -281,12 +590,137 @@ const requestLocationPermission = () => {
|
|||
)
|
||||
}
|
||||
|
||||
// Also add a resize event listener as a fallback for orientation changes
|
||||
// that might not trigger the Quasar screen watcher
|
||||
let resizeTimeout: number | undefined
|
||||
const handleWindowResize = () => {
|
||||
// Debounce resize events to avoid excessive calls
|
||||
if (resizeTimeout) {
|
||||
clearTimeout(resizeTimeout)
|
||||
}
|
||||
resizeTimeout = window.setTimeout(() => {
|
||||
handleMapResize()
|
||||
}, 300)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle map resize when screen orientation changes
|
||||
* ArcGIS MapView needs to be manually resized when its container changes dimensions
|
||||
* Only call resize() if the map is fully initialized
|
||||
*/
|
||||
function handleMapResize() {
|
||||
logMapDebug('handleMapResize:called')
|
||||
if (mapView.value && !mapView.value.destroyed && isMapInitialized.value) {
|
||||
// Use nextTick to ensure the DOM has finished updating before resizing
|
||||
reattachAndResizeMap()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for screen size/orientation changes
|
||||
* When the screen size changes (e.g., portrait to landscape), we need to re-initialize the map
|
||||
* because the container div changes between desktop and mobile versions
|
||||
*/
|
||||
watch(
|
||||
() => $q.screen.gt.xs,
|
||||
async (newValue) => {
|
||||
// Skip if no actual change (same layout)
|
||||
if (newValue === currentScreenSize.value) {
|
||||
logMapDebug('screenWatch:skipNoChange', { newValue })
|
||||
return
|
||||
}
|
||||
|
||||
logMapDebug('screenWatch:changed', {
|
||||
oldValue: currentScreenSize.value,
|
||||
newValue,
|
||||
})
|
||||
currentScreenSize.value = newValue
|
||||
if (!newValue) {
|
||||
mobileMapExpanded.value = true
|
||||
}
|
||||
mapInitVersion += 1
|
||||
isInitializing.value = false
|
||||
clearAttachRetryTimer()
|
||||
|
||||
// Always destroy and re-initialize when screen size changes
|
||||
// This ensures the map is attached to the correct container
|
||||
if (mapView.value && !mapView.value.destroyed) {
|
||||
logMapDebug('screenWatch:destroyMapView')
|
||||
mapView.value.destroy()
|
||||
mapView.value = null
|
||||
}
|
||||
|
||||
// Reset initialization state
|
||||
isMapInitialized.value = false
|
||||
|
||||
// Wait for DOM to update with new layout
|
||||
await nextTick()
|
||||
await waitForActiveMapContainer(30, 100)
|
||||
|
||||
// Re-initialize the map
|
||||
if (privacyStore.isAccepted) {
|
||||
await initializeMap()
|
||||
|
||||
if (!mapView.value || mapView.value.destroyed) {
|
||||
logMapDebug('screenWatch:missingMapAfterInitialize, triggerRecovery')
|
||||
await recoverMapByReinitialize()
|
||||
}
|
||||
|
||||
reattachAndResizeMap()
|
||||
logMapDebug('screenWatch:reinitialized', {
|
||||
hasMapViewAfterReinit: !!mapView.value,
|
||||
mapDestroyedAfterReinit: !!mapView.value?.destroyed,
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Watch for initialPOI prop changes to update poiPlaceName
|
||||
watch(
|
||||
() => props.initialPOI,
|
||||
(newValue) => {
|
||||
if (newValue && newValue !== poiPlaceName.value) {
|
||||
poiPlaceName.value = newValue
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// Set up resize event listener on mount
|
||||
onMounted(async () => {
|
||||
logMapDebug('lifecycle:mounted')
|
||||
window.addEventListener('resize', handleWindowResize)
|
||||
|
||||
// Component can be remounted after orientation/layout switches.
|
||||
// Recreate map automatically when consent is already granted.
|
||||
if (privacyStore.isAccepted && (!mapView.value || mapView.value.destroyed)) {
|
||||
logMapDebug('lifecycle:mounted:triggerInitialize')
|
||||
await waitForActiveMapContainer(30, 100)
|
||||
await initializeMap()
|
||||
reattachAndResizeMap()
|
||||
} else {
|
||||
logMapDebug('lifecycle:mounted:skipInitialize', {
|
||||
privacyAccepted: privacyStore.isAccepted,
|
||||
hasMapView: !!mapView.value,
|
||||
mapDestroyed: !!mapView.value?.destroyed,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Cleanup map resources when component unmounts
|
||||
onBeforeUnmount(() => {
|
||||
logMapDebug('lifecycle:beforeUnmount')
|
||||
clearAttachRetryTimer()
|
||||
|
||||
if (mapView.value) {
|
||||
mapView.value.destroy()
|
||||
mapView.value = null
|
||||
}
|
||||
// Clean up resize event listener
|
||||
window.removeEventListener('resize', handleWindowResize)
|
||||
// Clear timeout if it exists
|
||||
if (resizeTimeout) {
|
||||
clearTimeout(resizeTimeout)
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
|
|
@ -295,8 +729,8 @@ defineExpose({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Loading skeleton -->
|
||||
<div v-if="!poiPlaceName" class="col-12">
|
||||
<!-- Loading skeleton - show while map is initializing -->
|
||||
<div v-if="isInitializing && !isMapInitialized" class="col-12">
|
||||
<q-skeleton
|
||||
:height="$q.screen.gt.xs ? '35vh' : '45px'"
|
||||
width="100%"
|
||||
|
|
@ -306,7 +740,6 @@ defineExpose({
|
|||
</div>
|
||||
|
||||
<q-card
|
||||
v-show="poiPlaceName"
|
||||
bordered
|
||||
flat
|
||||
class="col-12 bg-grey-2 shadow-0"
|
||||
|
|
@ -314,8 +747,8 @@ defineExpose({
|
|||
>
|
||||
<div v-if="$q.screen.gt.xs">
|
||||
<div
|
||||
id="mapViewDisplay"
|
||||
ref="mapElement"
|
||||
:id="mapContainerId"
|
||||
ref="desktopMapContainerRef"
|
||||
style="height: 35vh; pointer-events: none"
|
||||
></div>
|
||||
|
||||
|
|
@ -339,6 +772,7 @@ defineExpose({
|
|||
|
||||
<q-card v-else style="border-radius: 20px">
|
||||
<q-expansion-item
|
||||
v-model="mobileMapExpanded"
|
||||
class="shadow-1 overflow-hidden bg-grey-4 text-left q-pa-xs"
|
||||
style="border-radius: 20px"
|
||||
dense
|
||||
|
|
@ -355,16 +789,20 @@ defineExpose({
|
|||
name="mdi-information-outline"
|
||||
size="xs"
|
||||
class="q-mr-xs cursor-pointer"
|
||||
@click.stop="$q.dialog({ message: textTooltip, ok: 'ปิด' })"
|
||||
@click.stop="$q.dialog({ message: textTooltip, ok: 'ตกลง' })"
|
||||
/>
|
||||
</q-item-label>
|
||||
<q-item-label class="text-weight-medium text-grey-9">
|
||||
{{ poiPlaceName }}
|
||||
{{ poiPlaceName || '-' }}
|
||||
</q-item-label>
|
||||
</q-item-section>
|
||||
</template>
|
||||
|
||||
<div id="mapViewDisplay" style="height: 20vh"></div>
|
||||
<div
|
||||
:id="mapContainerId"
|
||||
ref="mobileMapContainerRef"
|
||||
style="height: 20vh"
|
||||
></div>
|
||||
</q-expansion-item>
|
||||
</q-card>
|
||||
</q-card>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { nextTick, onBeforeUnmount, onMounted, shallowRef, ref } from 'vue'
|
||||
import { loadModules } from 'esri-loader'
|
||||
import axios from 'axios'
|
||||
|
||||
import type { LocationObject } from '@/interface/index/Main'
|
||||
|
||||
const emit = defineEmits(['update:location'])
|
||||
|
||||
function updateLocation(latitude: number, longitude: number, namePOI: string) {
|
||||
|
|
@ -20,8 +18,67 @@ const apiKey = ref<string>(
|
|||
// 'AAPK4f700a4324d04e9f8a1a134e0771ac45FXWawdCl-OotFfr52gz9XKxTDJTpDzw_YYcwbmKDDyAJswf14FoPyw0qBkN64DvP'
|
||||
)
|
||||
const zoomMap = ref<number>(18)
|
||||
const mapViewRef = shallowRef<any>(null)
|
||||
const mapContainerRef = ref<HTMLElement | null>(null)
|
||||
const mapRenderKey = ref<number>(0)
|
||||
let resizeTimer: ReturnType<typeof setTimeout> | null = null
|
||||
let resizeRetryCount = 0
|
||||
const MAX_RESIZE_RETRY = 8
|
||||
const isRecreatingMap = ref<boolean>(false)
|
||||
|
||||
function scheduleMapResize() {
|
||||
if (resizeTimer) {
|
||||
clearTimeout(resizeTimer)
|
||||
}
|
||||
|
||||
// Delay a little to wait for viewport + CSS layout to settle after rotation.
|
||||
resizeTimer = setTimeout(async () => {
|
||||
await nextTick()
|
||||
|
||||
if (!mapViewRef.value || !mapContainerRef.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const container = mapContainerRef.value
|
||||
const mapView = mapViewRef.value
|
||||
|
||||
if (container.clientWidth === 0 || container.clientHeight === 0) {
|
||||
if (resizeRetryCount < MAX_RESIZE_RETRY) {
|
||||
resizeRetryCount += 1
|
||||
scheduleMapResize()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
resizeRetryCount = 0
|
||||
|
||||
// Force canvas to bind to the current element after repeated rotations.
|
||||
if (mapView.container !== container) {
|
||||
mapView.container = null
|
||||
await nextTick()
|
||||
mapView.container = container
|
||||
}
|
||||
|
||||
mapView.resize()
|
||||
mapView
|
||||
.goTo(
|
||||
{
|
||||
center: mapView.center,
|
||||
zoom: mapView.zoom,
|
||||
},
|
||||
{ animate: false }
|
||||
)
|
||||
.catch(() => {
|
||||
// Ignore interruption errors from rapid orientation changes.
|
||||
})
|
||||
}, 300)
|
||||
}
|
||||
|
||||
async function initializeMap() {
|
||||
if (mapViewRef.value || !mapContainerRef.value) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Load modules of ArcGIS
|
||||
loadModules([
|
||||
|
|
@ -67,7 +124,7 @@ async function initializeMap() {
|
|||
|
||||
// สร้าง MapView
|
||||
const mapView = new MapView({
|
||||
container: 'mapViewDisplay', // div ที่จะแสดงแผนที่
|
||||
container: mapContainerRef.value, // div ที่จะแสดงแผนที่
|
||||
map: map,
|
||||
center: {
|
||||
latitude: latitude,
|
||||
|
|
@ -79,6 +136,7 @@ async function initializeMap() {
|
|||
components: [], // Empty array to remove all default UI components
|
||||
},
|
||||
})
|
||||
mapViewRef.value = mapView
|
||||
|
||||
// สร้างสัญลักษณ์ของหมุด
|
||||
const markerSymbol = new PictureMarkerSymbol({
|
||||
|
|
@ -98,9 +156,42 @@ async function initializeMap() {
|
|||
position: 'top-right', // ตำแหน่งของ Search widget
|
||||
})
|
||||
|
||||
// ถ้าได้ token จาก กทม. มา
|
||||
// await axios
|
||||
// .get(
|
||||
// 'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
// {
|
||||
// params: {
|
||||
// f: 'json', // Format JSON response
|
||||
// distance: 2000,
|
||||
// category: 'POI',
|
||||
// location: {
|
||||
// spatialReference: { wkid: 4326 },
|
||||
// x: longitude,
|
||||
// y: latitude,
|
||||
// },
|
||||
// token: apiKey.value,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// .then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
// poiPlaceName.value = response.data.address
|
||||
// ? response.data.address.PlaceName === ''
|
||||
// ? response.data.address.ShortLabel
|
||||
// : response.data.address.PlaceName
|
||||
// : 'ไม่พบข้อมูล'
|
||||
// const poiPoint = new Point({
|
||||
// longitude: response.data.location.x,
|
||||
// latitude: response.data.location.y,
|
||||
// })
|
||||
|
||||
// updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
// })
|
||||
// .catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
|
|
@ -111,52 +202,20 @@ async function initializeMap() {
|
|||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
token: apiKey.value,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
console.log('poi', response.data.location)
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
|
||||
updateLocation(longitude, latitude, poiPlaceName.value)
|
||||
})
|
||||
updateLocation(longitude, latitude, poiPlaceName.value)
|
||||
})
|
||||
// })
|
||||
|
||||
let searchMarker: any = null
|
||||
|
||||
|
|
@ -186,9 +245,41 @@ async function initializeMap() {
|
|||
symbol: markerSymbol,
|
||||
})
|
||||
if (searchMarker) {
|
||||
// await axios
|
||||
// .get(
|
||||
// 'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
// {
|
||||
// params: {
|
||||
// f: 'json', // Format JSON response
|
||||
// distance: 2000,
|
||||
// category: 'POI',
|
||||
// location: {
|
||||
// spatialReference: { wkid: 4326 },
|
||||
// x: longitude,
|
||||
// y: latitude,
|
||||
// },
|
||||
// token: apiKey.value,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// .then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
// poiPlaceName.value = response.data.address
|
||||
// ? response.data.address.PlaceName === ''
|
||||
// ? response.data.address.ShortLabel
|
||||
// : response.data.address.PlaceName
|
||||
// : 'ไม่พบข้อมูล'
|
||||
// const poiPoint = new Point({
|
||||
// longitude: response.data.location.x,
|
||||
// latitude: response.data.location.y,
|
||||
// })
|
||||
|
||||
// updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
// })
|
||||
// .catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
|
|
@ -199,52 +290,20 @@ async function initializeMap() {
|
|||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
token: apiKey.value,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
console.log('poi', response.data.location)
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: longitude,
|
||||
y: latitude,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
|
||||
updateLocation(longitude, latitude, poiPlaceName.value)
|
||||
})
|
||||
updateLocation(longitude, latitude, poiPlaceName.value)
|
||||
})
|
||||
// })
|
||||
}
|
||||
|
||||
mapView.graphics.add(searchMarker)
|
||||
|
|
@ -275,9 +334,41 @@ async function initializeMap() {
|
|||
} else {
|
||||
searchMarker.geometry = point // ย้ายหมุดไปยังตำแหน่งใหม่
|
||||
if (searchMarker) {
|
||||
// await axios
|
||||
// .get(
|
||||
// 'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
// {
|
||||
// params: {
|
||||
// f: 'json', // Format JSON response
|
||||
// distance: 2000,
|
||||
// category: 'POI',
|
||||
// location: {
|
||||
// spatialReference: { wkid: 4326 },
|
||||
// x: lon,
|
||||
// y: lat,
|
||||
// },
|
||||
// token: apiKey.value,
|
||||
// },
|
||||
// }
|
||||
// )
|
||||
// .then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
// poiPlaceName.value = response.data.address
|
||||
// ? response.data.address.PlaceName === ''
|
||||
// ? response.data.address.ShortLabel
|
||||
// : response.data.address.PlaceName
|
||||
// : 'ไม่พบข้อมูล'
|
||||
// const poiPoint = new Point({
|
||||
// longitude: response.data.location.x,
|
||||
// latitude: response.data.location.y,
|
||||
// })
|
||||
|
||||
// updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
// })
|
||||
// .catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://bmagis.bangkok.go.th/portal/sharing/servers/e4732c3a9fe549ab8bc697573b468f68/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
|
|
@ -288,52 +379,20 @@ async function initializeMap() {
|
|||
x: lon,
|
||||
y: lat,
|
||||
},
|
||||
token: apiKey.value,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
console.log('poi', response.data.location)
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
const poiPoint = new Point({
|
||||
longitude: response.data.location.x,
|
||||
latitude: response.data.location.y,
|
||||
})
|
||||
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch(async () => {
|
||||
await axios
|
||||
.get(
|
||||
'https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode/',
|
||||
{
|
||||
params: {
|
||||
f: 'json', // Format JSON response
|
||||
distance: 2000,
|
||||
category: 'POI',
|
||||
location: {
|
||||
spatialReference: { wkid: 4326 },
|
||||
x: lon,
|
||||
y: lat,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
// console.log('poi', response.data.location)
|
||||
poiPlaceName.value = response.data.address
|
||||
? response.data.address.PlaceName === ''
|
||||
? response.data.address.ShortLabel
|
||||
: response.data.address.PlaceName
|
||||
: 'ไม่พบข้อมูล'
|
||||
|
||||
updateLocation(lat, lon, poiPlaceName.value)
|
||||
})
|
||||
updateLocation(lat, lon, poiPlaceName.value)
|
||||
})
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,14 +409,64 @@ async function initializeMap() {
|
|||
}
|
||||
}
|
||||
|
||||
async function recreateMapOnOrientationChange() {
|
||||
if (isRecreatingMap.value) {
|
||||
return
|
||||
}
|
||||
|
||||
isRecreatingMap.value = true
|
||||
|
||||
try {
|
||||
if (resizeTimer) {
|
||||
clearTimeout(resizeTimer)
|
||||
resizeTimer = null
|
||||
}
|
||||
|
||||
if (mapViewRef.value) {
|
||||
mapViewRef.value.destroy()
|
||||
mapViewRef.value = null
|
||||
}
|
||||
|
||||
mapRenderKey.value += 1
|
||||
await nextTick()
|
||||
await initializeMap()
|
||||
scheduleMapResize()
|
||||
} finally {
|
||||
isRecreatingMap.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initializeMap()
|
||||
|
||||
window.addEventListener('resize', scheduleMapResize)
|
||||
window.addEventListener('orientationchange', recreateMapOnOrientationChange)
|
||||
window.addEventListener('pageshow', scheduleMapResize)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', scheduleMapResize)
|
||||
window.removeEventListener(
|
||||
'orientationchange',
|
||||
recreateMapOnOrientationChange
|
||||
)
|
||||
window.removeEventListener('pageshow', scheduleMapResize)
|
||||
|
||||
if (resizeTimer) {
|
||||
clearTimeout(resizeTimer)
|
||||
resizeTimer = null
|
||||
}
|
||||
|
||||
if (mapViewRef.value) {
|
||||
mapViewRef.value.destroy()
|
||||
mapViewRef.value = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<q-card bordered flat class="col-12 bg-grey-2 shadow-0">
|
||||
<div id="mapViewDisplay" style="height: 35vh"></div>
|
||||
<div :key="mapRenderKey" ref="mapContainerRef" style="height: 35vh"></div>
|
||||
|
||||
<div
|
||||
:class="
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, watch, onBeforeUnmount } from 'vue'
|
||||
import { ref, reactive, onMounted, watch, onBeforeUnmount, computed } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { format } from 'date-fns'
|
||||
import Camera from 'simple-vue-camera'
|
||||
|
|
@ -466,7 +466,12 @@ async function validateForm() {
|
|||
}
|
||||
}
|
||||
|
||||
const mapRef = ref<InstanceType<typeof MapCheck> | null>(null)
|
||||
const mapRefDesktop = ref<InstanceType<typeof MapCheck> | null>(null)
|
||||
const mapRefMobile = ref<InstanceType<typeof MapCheck> | null>(null)
|
||||
|
||||
// Computed ref that returns the correct map ref based on screen size
|
||||
const mapRef = computed(() => ($q.screen.gt.xs ? mapRefDesktop.value : mapRefMobile.value))
|
||||
|
||||
const timeChickin = ref<string>('') //เวลาเข้างาน,เวลาออกงาน
|
||||
|
||||
/** function ยืนยันการลงเวลาเข้า - ออก*/
|
||||
|
|
@ -777,13 +782,13 @@ watch(
|
|||
<div class="col-xs-12 col-sm-8 gt-xs">
|
||||
<div class="col-12">
|
||||
<MapCheck
|
||||
v-if="$q.screen.gt.xs"
|
||||
ref="mapRef"
|
||||
ref="mapRefDesktop"
|
||||
:initialPOI="formLocation.POI"
|
||||
@update:location="updateLocation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4">
|
||||
<div class="col-xs-12 col-sm-4 gt-xs">
|
||||
<q-card
|
||||
:class="
|
||||
$q.screen.xs ? 'card-container-xs' : 'card-container'
|
||||
|
|
@ -893,6 +898,81 @@ watch(
|
|||
|
||||
<!-- กรอกข้อมูล หน้ามือถือ -->
|
||||
<div class="col-12 row q-col-gutter-y-md" v-if="$q.screen.xs">
|
||||
<!-- กล้อง หน้ามือถือ -->
|
||||
<div class="col-12">
|
||||
<q-card
|
||||
:class="
|
||||
$q.screen.xs ? 'card-container-xs' : 'card-container'
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-if="!cameraIsOn && img == null"
|
||||
class="preview-placeholder"
|
||||
@click="() => !isDisabledCheckTime && openCamera()"
|
||||
>
|
||||
<div class="text-center">
|
||||
<q-icon
|
||||
name="mdi-camera"
|
||||
color="blue-grey-3"
|
||||
size="100px"
|
||||
class="center-icon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row items-center">
|
||||
<!-- แสดงกล้องตอนกดถ่ายภาพ -->
|
||||
|
||||
<Camera
|
||||
:resolution="{ width: photoWidth, height: photoHeight }"
|
||||
ref="camera"
|
||||
:autoplay="false"
|
||||
:style="!img ? 'display: block' : 'display: none'"
|
||||
/>
|
||||
|
||||
<!-- แสดงรูปเมื่อกด capture -->
|
||||
<div v-if="img" class="image-container">
|
||||
<q-img :src="img" class="image-element"></q-img>
|
||||
</div>
|
||||
|
||||
<div v-if="cameraIsOn">
|
||||
<div
|
||||
class="absolute-bottom text-subtitle2 text-center q-py-sm"
|
||||
style="background: #00000021"
|
||||
>
|
||||
<q-btn
|
||||
v-if="availableCameras.length > 1 && img == null"
|
||||
round
|
||||
icon="flip_camera_ios"
|
||||
size="16px"
|
||||
style="background: #424242; color: white"
|
||||
@click="switchCamera"
|
||||
unelevated
|
||||
class="q-mr-xs"
|
||||
/>
|
||||
<q-btn
|
||||
round
|
||||
v-if="img == null"
|
||||
icon="photo_camera"
|
||||
size="18px"
|
||||
style="background: #263238; color: white"
|
||||
@click="capturePhoto"
|
||||
unelevated
|
||||
/>
|
||||
<q-btn
|
||||
v-else
|
||||
round
|
||||
icon="refresh"
|
||||
size="18px"
|
||||
style="background: #263238; color: white"
|
||||
@click="refreshPhoto"
|
||||
unelevated
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<div class="col-12" v-if="!isDisabledCheckTime">
|
||||
<q-card
|
||||
flat
|
||||
|
|
@ -965,8 +1045,13 @@ watch(
|
|||
</q-card>
|
||||
</div>
|
||||
|
||||
<div class="col-12" v-if="$q.screen.xs">
|
||||
<MapCheck ref="mapRef" @update:location="updateLocation" />
|
||||
<!-- Map หน้ามือถือ -->
|
||||
<div class="col-12">
|
||||
<MapCheck
|
||||
ref="mapRefMobile"
|
||||
:initialPOI="formLocation.POI"
|
||||
@update:location="updateLocation"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- กรอกข้อมูล หน้ามือถือ -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue