fix fack location complated
All checks were successful
Build & Deploy on Dev / build (push) Successful in 2m22s
All checks were successful
Build & Deploy on Dev / build (push) Successful in 2m22s
This commit is contained in:
parent
859d74056a
commit
3ae6e6eeac
4 changed files with 287 additions and 115 deletions
|
|
@ -16,7 +16,32 @@ const mapElement = ref<HTMLElement | null>(null)
|
|||
const emit = defineEmits(['update:location', 'locationStatus', 'mockDetected'])
|
||||
const $q = useQuasar()
|
||||
|
||||
const { validateLocation, showMockWarning } = useLocationValidation()
|
||||
const { validateLocation } = useLocationValidation()
|
||||
const MOCK_CHECK_DELAY_MS = 800
|
||||
|
||||
function wait(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
function getCurrentPositionAsync(options?: PositionOptions) {
|
||||
return new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, options)
|
||||
})
|
||||
}
|
||||
|
||||
async function getPositionForValidation(fallback: GeolocationPosition) {
|
||||
await wait(MOCK_CHECK_DELAY_MS)
|
||||
try {
|
||||
return await getCurrentPositionAsync({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0,
|
||||
})
|
||||
} catch (error) {
|
||||
// Keep the original reading if the second sampling fails.
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
function updateLocation(latitude: number, longitude: number, namePOI: string) {
|
||||
// ส่ง event ไปยัง parent component เพื่ออัพเดทค่า props
|
||||
|
|
@ -24,6 +49,7 @@ function updateLocation(latitude: number, longitude: number, namePOI: string) {
|
|||
}
|
||||
|
||||
const poiPlaceName = ref<string>('') // ชื่อพื้นที่ใกล้เคียง
|
||||
const isMapReady = ref<boolean>(false)
|
||||
|
||||
// Replace ArcGIS api key
|
||||
const apiKey = ref<string>(
|
||||
|
|
@ -68,6 +94,7 @@ async function initializeMap(position: GeolocationPosition) {
|
|||
components: [], // Empty array to remove all default UI components
|
||||
},
|
||||
})
|
||||
isMapReady.value = true
|
||||
|
||||
// ตำแหน่งของผู้ใช้
|
||||
const userPoint = new Point({ longitude, latitude })
|
||||
|
|
@ -180,7 +207,9 @@ async function initializeMap(position: GeolocationPosition) {
|
|||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
.catch((error) => {
|
||||
// console.error('Error fetching points of interest:', error)
|
||||
// Keep map visible even when POI lookup fails.
|
||||
poiPlaceName.value = 'ไม่พบข้อมูล'
|
||||
updateLocation(latitude, longitude, poiPlaceName.value)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -213,13 +242,16 @@ const requestLocationPermission = () => {
|
|||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
async (position) => {
|
||||
// Validate location first
|
||||
const validationResult = validateLocation(position)
|
||||
const sampledPosition = await getPositionForValidation(position)
|
||||
|
||||
// Always emit mockDetected event (regardless of result)
|
||||
// Validate location first
|
||||
const validationResult = validateLocation(sampledPosition)
|
||||
|
||||
// Do not block map preview on initial mock detection.
|
||||
// The hard-stop warning is enforced at submit time in HomeView.
|
||||
if (validationResult.isMockDetected) {
|
||||
showMockWarning(validationResult)
|
||||
emit('mockDetected', validationResult)
|
||||
locationGranted.value = true
|
||||
emit('locationStatus', true)
|
||||
}
|
||||
|
||||
// Check for critical errors (invalid coordinates) that prevent showing location
|
||||
|
|
@ -234,11 +266,11 @@ const requestLocationPermission = () => {
|
|||
return
|
||||
}
|
||||
|
||||
// Permission granted based on mock detection
|
||||
locationGranted.value = !validationResult.isMockDetected
|
||||
emit('locationStatus', !validationResult.isMockDetected)
|
||||
// Permission granted for map preview state.
|
||||
locationGranted.value = true
|
||||
emit('locationStatus', true)
|
||||
|
||||
const { latitude, longitude } = position.coords
|
||||
const { latitude, longitude } = sampledPosition.coords
|
||||
// console.log('Current position:', latitude, longitude)
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
|
|
@ -248,7 +280,7 @@ const requestLocationPermission = () => {
|
|||
|
||||
// Center map on user's location if map is initialized
|
||||
if (privacyStore.isAccepted) {
|
||||
await initializeMap(position)
|
||||
await initializeMap(sampledPosition)
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
|
|
@ -279,7 +311,7 @@ const requestLocationPermission = () => {
|
|||
break
|
||||
}
|
||||
},
|
||||
{ enableHighAccuracy: true }
|
||||
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +323,7 @@ defineExpose({
|
|||
|
||||
<template>
|
||||
<!-- Loading skeleton -->
|
||||
<div v-if="!poiPlaceName" class="col-12">
|
||||
<div v-if="!isMapReady" class="col-12">
|
||||
<q-skeleton
|
||||
:height="$q.screen.gt.xs ? '35vh' : '45px'"
|
||||
width="100%"
|
||||
|
|
@ -301,7 +333,7 @@ defineExpose({
|
|||
</div>
|
||||
|
||||
<q-card
|
||||
v-show="poiPlaceName"
|
||||
v-show="isMapReady"
|
||||
bordered
|
||||
flat
|
||||
class="col-12 bg-grey-2 shadow-0"
|
||||
|
|
@ -323,7 +355,7 @@ defineExpose({
|
|||
>
|
||||
พื้นที่ใกล้เคียง
|
||||
<span class="q-px-sm">:</span>
|
||||
{{ poiPlaceName }}
|
||||
{{ poiPlaceName || 'ไม่พบข้อมูล' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -340,7 +372,7 @@ defineExpose({
|
|||
</q-item-section>
|
||||
|
||||
<q-item-section>
|
||||
{{ poiPlaceName }}
|
||||
{{ poiPlaceName || 'ไม่พบข้อมูล' }}
|
||||
</q-item-section>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@ export const VALIDATION_CONFIG = {
|
|||
POSITION_HISTORY_SIZE: 5, // number of positions to keep for pattern detection
|
||||
MOCK_INDICATOR_THRESHOLD: 3, // threshold for mock detection (indicators >= 3 = mock)
|
||||
SUSPICIOUS_ACCURACY_MAX: 5, // accuracy ≤ 5m AND integer = suspicious (real GPS never rounds to whole number)
|
||||
// Geographic service area (Thailand) to reduce spoofing risk from remote fake coordinates.
|
||||
SERVICE_AREA: {
|
||||
MIN_LAT: 5.0,
|
||||
MAX_LAT: 21.0,
|
||||
MIN_LON: 97.0,
|
||||
MAX_LON: 106.0,
|
||||
},
|
||||
} as const
|
||||
|
||||
export function useLocationValidation() {
|
||||
|
|
@ -41,6 +48,8 @@ export function useLocationValidation() {
|
|||
'ตรวจพบการเคลื่อนที่ด้วยความเร็วผิดปกติ อาจเป็นการจำลองตำแหน่ง',
|
||||
SUSPICIOUS_ACCURACY: 'ตรวจพบค่าความแม่นยำที่ผิดปกติ อาจเป็นการจำลองตำแหน่ง',
|
||||
DUPLICATE_POSITION: 'ตรวจพบพิกัดตำแหน่งซ้ำกัน อาจเป็นการจำลองตำแหน่ง',
|
||||
OUT_OF_SERVICE_AREA:
|
||||
'ตรวจพบตำแหน่งนอกพื้นที่ใช้งานของระบบ กรุณาปิดแอปจำลองตำแหน่งและลองใหม่',
|
||||
}
|
||||
|
||||
const previousPositions = ref<PositionSnapshot[]>([])
|
||||
|
|
@ -132,6 +141,16 @@ export function useLocationValidation() {
|
|||
)
|
||||
}
|
||||
|
||||
const isWithinServiceArea = (lat: number, lon: number): boolean => {
|
||||
const area = VALIDATION_CONFIG.SERVICE_AREA
|
||||
return (
|
||||
lat >= area.MIN_LAT &&
|
||||
lat <= area.MAX_LAT &&
|
||||
lon >= area.MIN_LON &&
|
||||
lon <= area.MAX_LON
|
||||
)
|
||||
}
|
||||
|
||||
// Main validation function
|
||||
const validateLocation = (
|
||||
position: GeolocationPosition
|
||||
|
|
@ -149,6 +168,12 @@ export function useLocationValidation() {
|
|||
mockIndicators += 3
|
||||
}
|
||||
|
||||
// 1.1 Service-area validation (critical for remote spoofed coordinates)
|
||||
if (!isWithinServiceArea(latitude, longitude)) {
|
||||
errors.push(errorMessages.OUT_OF_SERVICE_AREA)
|
||||
mockIndicators += 3
|
||||
}
|
||||
|
||||
// 2. Timestamp validation
|
||||
if (!validateTimestamp(timestamp)) {
|
||||
errors.push(errorMessages.STALE_TIMESTAMP)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,56 @@ import { logout } from '@/plugins/auth'
|
|||
import { format, utcToZonedTime } from 'date-fns-tz'
|
||||
|
||||
export const useCounterMixin = defineStore('mixin', () => {
|
||||
let activeErrorDialog: any = null
|
||||
|
||||
const clearActiveErrorDialog = () => {
|
||||
if (!activeErrorDialog) return
|
||||
try {
|
||||
if (typeof activeErrorDialog.hide === 'function') {
|
||||
activeErrorDialog.hide()
|
||||
} else if (typeof activeErrorDialog.destroy === 'function') {
|
||||
activeErrorDialog.destroy()
|
||||
}
|
||||
} finally {
|
||||
activeErrorDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
const openSingleErrorDialog = (
|
||||
q: any,
|
||||
message: string,
|
||||
onCancel?: () => void | Promise<void>
|
||||
) => {
|
||||
clearActiveErrorDialog()
|
||||
|
||||
const dialog = q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
|
||||
activeErrorDialog = dialog
|
||||
|
||||
dialog.onDismiss(() => {
|
||||
if (activeErrorDialog === dialog) {
|
||||
activeErrorDialog = null
|
||||
}
|
||||
})
|
||||
|
||||
if (onCancel) {
|
||||
dialog.onCancel(() => {
|
||||
void onCancel()
|
||||
})
|
||||
}
|
||||
|
||||
return dialog
|
||||
}
|
||||
|
||||
function date2Thai(srcDate: Date, isFullMonth = false, isTime = false) {
|
||||
if (srcDate == null) {
|
||||
return null
|
||||
|
|
@ -133,53 +183,30 @@ export const useCounterMixin = defineStore('mixin', () => {
|
|||
}
|
||||
|
||||
const messageError = (q: any, e: any = '', msg: string = '') => {
|
||||
// q.dialog.hide();
|
||||
// Keep only one active warning popup to prevent dialog overlap.
|
||||
if (e.response !== undefined) {
|
||||
if (e.response.data.status !== undefined) {
|
||||
if (e.response.data.status == 401) {
|
||||
//invalid_token
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: `ล็อกอินหมดอายุ กรุณาล็อกอินใหม่อีกครั้ง`,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
}).onCancel(async () => {
|
||||
showLoader()
|
||||
await logout()
|
||||
setTimeout(() => {
|
||||
hideLoader()
|
||||
}, 1000)
|
||||
})
|
||||
openSingleErrorDialog(
|
||||
q,
|
||||
'ล็อกอินหมดอายุ กรุณาล็อกอินใหม่อีกครั้ง',
|
||||
async () => {
|
||||
showLoader()
|
||||
await logout()
|
||||
setTimeout(() => {
|
||||
hideLoader()
|
||||
}, 1000)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
const message = e.response.data.result ?? e.response.data.message
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: `${message}`,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
openSingleErrorDialog(q, `${message}`)
|
||||
}
|
||||
} else {
|
||||
if (e.response.status == 401) {
|
||||
if (msg !== '') {
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: msg,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
}).onCancel(async () => {
|
||||
openSingleErrorDialog(q, msg, async () => {
|
||||
showLoader()
|
||||
await logout()
|
||||
setTimeout(() => {
|
||||
|
|
@ -188,70 +215,35 @@ export const useCounterMixin = defineStore('mixin', () => {
|
|||
})
|
||||
} else {
|
||||
//invalid_token
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: `ล็อกอินหมดอายุ กรุณาล็อกอินใหม่อีกครั้ง`,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
}).onCancel(async () => {
|
||||
showLoader()
|
||||
await logout()
|
||||
setTimeout(() => {
|
||||
hideLoader()
|
||||
}, 1000)
|
||||
})
|
||||
openSingleErrorDialog(
|
||||
q,
|
||||
'ล็อกอินหมดอายุ กรุณาล็อกอินใหม่อีกครั้ง',
|
||||
async () => {
|
||||
showLoader()
|
||||
await logout()
|
||||
setTimeout(() => {
|
||||
hideLoader()
|
||||
}, 1000)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if (e.response.data.successful === false) {
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: e.response.data.message,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
openSingleErrorDialog(q, e.response.data.message)
|
||||
} else {
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: `ข้อมูลผิดพลาดทำให้เกิดการไม่ตอบสนองต่อการเรียกใช้งานดูเว็บไซต์`,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
openSingleErrorDialog(
|
||||
q,
|
||||
'ข้อมูลผิดพลาดทำให้เกิดการไม่ตอบสนองต่อการเรียกใช้งานดูเว็บไซต์'
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (msg !== '') {
|
||||
return q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: msg,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
return openSingleErrorDialog(q, msg)
|
||||
}
|
||||
q.dialog({
|
||||
component: CustomComponent,
|
||||
componentProps: {
|
||||
title: `พบข้อผิดพลาด`,
|
||||
message: `ข้อมูลผิดพลาดทำให้เกิดการไม่ตอบสนองต่อการเรียกใช้งานดูเว็บไซต์`,
|
||||
icon: 'warning',
|
||||
color: 'red',
|
||||
onlycancel: true,
|
||||
},
|
||||
})
|
||||
openSingleErrorDialog(
|
||||
q,
|
||||
'ข้อมูลผิดพลาดทำให้เกิดการไม่ตอบสนองต่อการเรียกใช้งานดูเว็บไซต์'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import config from '@/app.config'
|
|||
import http from '@/plugins/http'
|
||||
import { useCounterMixin } from '@/stores/mixin'
|
||||
import { usePermissions } from '@/composables/usePermissions'
|
||||
import { useLocationValidation } from '@/composables/useLocationValidation'
|
||||
import { usePrivacyStore } from '@/stores/privacy'
|
||||
|
||||
import type { FormRef, OptionReason } from '@/interface/response/checkin'
|
||||
|
|
@ -18,7 +19,10 @@ const mixin = useCounterMixin()
|
|||
const { date2Thai, showLoader, hideLoader, messageError, dialogConfirm } = mixin
|
||||
const $q = useQuasar()
|
||||
const { checkPrivacyAccepted } = usePermissions()
|
||||
const { validateLocation, showMockWarning, resetValidation } =
|
||||
useLocationValidation()
|
||||
const privacyStore = usePrivacyStore()
|
||||
const MOCK_CHECK_DELAY_MS = 800
|
||||
|
||||
const modalTime = ref<boolean>(false) // Dailog ลงเวลาเข้างานของคุณ
|
||||
const checkStatus = ref<string>('')
|
||||
|
|
@ -120,14 +124,110 @@ async function updateLocation(
|
|||
*/
|
||||
function onLocationStatus(status: boolean) {
|
||||
locationGranted.value = status
|
||||
if (status) {
|
||||
isMockLocationDetected.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* รับค่า mock location detection จาก AscGISMap
|
||||
*/
|
||||
function onMockDetected(result: any) {
|
||||
isMockLocationDetected.value = true
|
||||
disabledBtn.value = true
|
||||
isMockLocationDetected.value = !!result?.isMockDetected
|
||||
disabledBtn.value = false
|
||||
}
|
||||
|
||||
function resetLocationForRetry() {
|
||||
locationGranted.value = false
|
||||
formLocation.lat = 0
|
||||
formLocation.lng = 0
|
||||
formLocation.POI = ''
|
||||
}
|
||||
|
||||
function getCurrentPositionAsync(options?: PositionOptions) {
|
||||
return new Promise<GeolocationPosition>((resolve, reject) => {
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, options)
|
||||
})
|
||||
}
|
||||
|
||||
function wait(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async function getDelayedFreshPosition() {
|
||||
const firstPosition = await getCurrentPositionAsync({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0,
|
||||
})
|
||||
|
||||
await wait(MOCK_CHECK_DELAY_MS)
|
||||
|
||||
try {
|
||||
return await getCurrentPositionAsync({
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 0,
|
||||
})
|
||||
} catch (error) {
|
||||
return firstPosition
|
||||
}
|
||||
}
|
||||
|
||||
async function revalidateLocationBeforeSubmit() {
|
||||
if (!navigator.geolocation) {
|
||||
messageError(
|
||||
$q,
|
||||
'',
|
||||
'ไม่สามารถระบุตำแหน่งปัจจุบันได้ เบราว์เซอร์ของคุณไม่รองรับ Geolocation'
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
// If previous attempt was mock, clear history so fresh GPS is not compared
|
||||
// against spoofed coordinates and incorrectly flagged as impossible speed.
|
||||
if (isMockLocationDetected.value) {
|
||||
resetValidation()
|
||||
}
|
||||
|
||||
const position = await getDelayedFreshPosition()
|
||||
|
||||
const validationResult = validateLocation(position)
|
||||
|
||||
if (validationResult.isMockDetected) {
|
||||
isMockLocationDetected.value = true
|
||||
disabledBtn.value = false
|
||||
resetValidation()
|
||||
resetLocationForRetry()
|
||||
showMockWarning(validationResult)
|
||||
mapRef.value?.requestLocationPermission()
|
||||
return false
|
||||
}
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
disabledBtn.value = false
|
||||
resetValidation()
|
||||
resetLocationForRetry()
|
||||
messageError($q, '', validationResult.errors[0])
|
||||
mapRef.value?.requestLocationPermission()
|
||||
return false
|
||||
}
|
||||
|
||||
locationGranted.value = true
|
||||
isMockLocationDetected.value = false
|
||||
return true
|
||||
} catch (error) {
|
||||
disabledBtn.value = false
|
||||
resetLocationForRetry()
|
||||
messageError(
|
||||
$q,
|
||||
'',
|
||||
'ไม่สามารถตรวจสอบตำแหน่งล่าสุดก่อนลงเวลาได้ กรุณาลองใหม่อีกครั้ง'
|
||||
)
|
||||
mapRef.value?.requestLocationPermission()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const location = ref<string>('') // พื้นที่ใกล้เคียง
|
||||
|
|
@ -348,7 +448,7 @@ const objectRef: FormRef = {
|
|||
}
|
||||
|
||||
/** function ตรวจสอบค่าว่างของ input*/
|
||||
function validateForm() {
|
||||
async function validateForm() {
|
||||
const hasError = []
|
||||
for (const key in objectRef) {
|
||||
if (Object.prototype.hasOwnProperty.call(objectRef, key)) {
|
||||
|
|
@ -360,6 +460,11 @@ function validateForm() {
|
|||
}
|
||||
}
|
||||
if (hasError.every((result) => result === true)) {
|
||||
const isLocationValid = await revalidateLocationBeforeSubmit()
|
||||
if (!isLocationValid) {
|
||||
return
|
||||
}
|
||||
|
||||
if (statusCheckin.value == false) {
|
||||
getCheck()
|
||||
} else if (statusCheckin.value) {
|
||||
|
|
@ -397,6 +502,12 @@ async function confirm() {
|
|||
mapRef.value?.requestLocationPermission()
|
||||
return
|
||||
}
|
||||
|
||||
const isLocationValid = await revalidateLocationBeforeSubmit()
|
||||
if (!isLocationValid) {
|
||||
return
|
||||
}
|
||||
|
||||
disabledBtn.value = true
|
||||
showLoader()
|
||||
const isLocation = workplace.value === 'in-place' //*true คือ ณ สถานที่ตั้ง, false คือ นอกสถานที่ตั้ง
|
||||
|
|
@ -1016,7 +1127,13 @@ watch(
|
|||
push
|
||||
size="18px"
|
||||
:class="$q.screen.gt.xs ? 'q-px-md' : 'full-width q-pa-sm'"
|
||||
:disable="disabledBtn || !locationGranted || isMockLocationDetected ? true : camera && img ? false : true"
|
||||
:disable="
|
||||
disabledBtn || !locationGranted || isMockLocationDetected
|
||||
? true
|
||||
: camera && img
|
||||
? false
|
||||
: true
|
||||
"
|
||||
@click="validateForm"
|
||||
:loading="inQueue"
|
||||
/>
|
||||
|
|
@ -1131,7 +1248,13 @@ watch(
|
|||
push
|
||||
size="18px"
|
||||
:class="$q.screen.gt.xs ? 'q-px-md' : 'full-width q-pa-sm'"
|
||||
:disable="disabledBtn || !locationGranted || isMockLocationDetected ? true : camera && img ? false : true"
|
||||
:disable="
|
||||
disabledBtn || !locationGranted || isMockLocationDetected
|
||||
? true
|
||||
: camera && img
|
||||
? false
|
||||
: true
|
||||
"
|
||||
@click="validateForm"
|
||||
:loading="inQueue"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue