useVisitLocation.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { ref } from 'vue'
  2. import { onHide, onShow, onUnload } from '@dcloudio/uni-app'
  3. import { getLocation, type LocationResult } from '@/lib/location'
  4. import { getDictTypeApi } from '@/services/modules/common'
  5. import { DEFAULT_VISIT_RANGE_RADIUS } from '../taskVisit.config'
  6. import type { VisitLocationStatus } from '../types'
  7. const LOCATION_POLL_INTERVAL = 5 * 60_000
  8. export const useVisitLocation = () => {
  9. const currentLocation = ref<LocationResult | null>(null)
  10. const locationStatus = ref<VisitLocationStatus>('idle')
  11. const rangeRadius = ref(DEFAULT_VISIT_RANGE_RADIUS)
  12. let locationTimer: ReturnType<typeof setInterval> | undefined
  13. let locating = false
  14. const stopLocationPolling = () => {
  15. if (!locationTimer) return
  16. clearInterval(locationTimer)
  17. locationTimer = undefined
  18. }
  19. const refreshCurrentLocation = async () => {
  20. if (locating) return
  21. locating = true
  22. if (!currentLocation.value) {
  23. locationStatus.value = 'locating'
  24. }
  25. try {
  26. const location = await getLocation()
  27. if (!isValidLocation(location)) {
  28. throw new Error('Invalid location result')
  29. }
  30. currentLocation.value = location
  31. locationStatus.value = 'ready'
  32. } catch {
  33. locationStatus.value = 'error'
  34. // 授权失败后暂停自动轮询,避免反复唤起授权提示;用户可手动重试。
  35. stopLocationPolling()
  36. } finally {
  37. locating = false
  38. }
  39. }
  40. const startLocationPolling = () => {
  41. stopLocationPolling()
  42. void refreshCurrentLocation()
  43. locationTimer = setInterval(() => {
  44. void refreshCurrentLocation()
  45. }, LOCATION_POLL_INTERVAL)
  46. }
  47. const loadRangeRadius = async () => {
  48. try {
  49. const res = await getDictTypeApi('point_jwd')
  50. const radius = Number(res.data?.[0]?.value)
  51. if (Number.isFinite(radius) && radius > 0) {
  52. rangeRadius.value = radius
  53. }
  54. } catch {
  55. rangeRadius.value = DEFAULT_VISIT_RANGE_RADIUS
  56. }
  57. }
  58. onShow(() => {
  59. startLocationPolling()
  60. void loadRangeRadius()
  61. })
  62. onHide(stopLocationPolling)
  63. onUnload(stopLocationPolling)
  64. return {
  65. currentLocation,
  66. locationStatus,
  67. rangeRadius,
  68. refreshCurrentLocation: startLocationPolling,
  69. }
  70. }
  71. const isValidLocation = (location: LocationResult) => {
  72. return (
  73. Number.isFinite(location.latitude) &&
  74. Number.isFinite(location.longitude) &&
  75. !(location.latitude === 0 && location.longitude === 0)
  76. )
  77. }