inclusão de mapas
This commit is contained in:
parent
b564c2c302
commit
6b9a9dc705
20 changed files with 4200 additions and 3696 deletions
|
|
@ -50,33 +50,27 @@
|
|||
>
|
||||
<v-icon>mdi-crosshairs-gps</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="possuiValor && !Boolean(opcoes?.apenasLeitura)"
|
||||
icon
|
||||
variant="tonal"
|
||||
color="error"
|
||||
size="small"
|
||||
title="Limpar localização"
|
||||
@click="limpar"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import L from "leaflet"
|
||||
import "leaflet/dist/leaflet.css"
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
type PropType,
|
||||
ref,
|
||||
watch,
|
||||
} from "vue"
|
||||
import { defineComponent, type PropType, ref } from "vue"
|
||||
import "./EliEntradaCoordenadas.css"
|
||||
import type { PadroesEntradas } from "./tiposEntradas"
|
||||
import {
|
||||
ALTURA_MAPA_PADRAO,
|
||||
COORDENADAS_PADRAO,
|
||||
coordenadaValida,
|
||||
formatarCoordenada,
|
||||
obterLocalizacao,
|
||||
parseCoordenada,
|
||||
ZOOM_PADRAO,
|
||||
} from "./utils/coordenadas"
|
||||
import { useCoordenadas } from "./utils/useCoordenadas"
|
||||
|
||||
type EntradaCoordenadas = PadroesEntradas["coordenadas"]
|
||||
|
||||
|
|
@ -86,7 +80,7 @@ export default defineComponent({
|
|||
props: {
|
||||
value: {
|
||||
type: Object as PropType<EntradaCoordenadas["value"]>,
|
||||
default: () => ({ ...COORDENADAS_PADRAO }),
|
||||
default: null,
|
||||
},
|
||||
opcoes: {
|
||||
type: Object as PropType<EntradaCoordenadas["opcoes"]>,
|
||||
|
|
@ -102,128 +96,15 @@ export default defineComponent({
|
|||
},
|
||||
setup(props, { attrs, emit }) {
|
||||
const mapaRef = ref<HTMLElement | null>(null)
|
||||
const geolocalizando = ref(false)
|
||||
let mapa: L.Map | null = null
|
||||
let marcador: L.Marker | null = null
|
||||
|
||||
const alturaEfetiva = computed(() => props.opcoes?.alturaMapa ?? ALTURA_MAPA_PADRAO)
|
||||
const zoomEfetivo = computed(() => props.opcoes?.zoom ?? ZOOM_PADRAO)
|
||||
const somenteLeitura = computed(() => Boolean(props.opcoes?.apenasLeitura))
|
||||
|
||||
const latDisplay = computed(() =>
|
||||
formatarCoordenada(props.value?.latitude ?? COORDENADAS_PADRAO.latitude),
|
||||
)
|
||||
const lngDisplay = computed(() =>
|
||||
formatarCoordenada(props.value?.longitude ?? COORDENADAS_PADRAO.longitude),
|
||||
)
|
||||
|
||||
function emitirValor(lat: number, lng: number) {
|
||||
if (!coordenadaValida(lat, lng)) return
|
||||
const novoValor: EntradaCoordenadas["value"] = {
|
||||
formato: "decimal",
|
||||
datum: "EPSG:4326",
|
||||
latitude: Number.parseFloat(lat.toFixed(6)),
|
||||
longitude: Number.parseFloat(lng.toFixed(6)),
|
||||
}
|
||||
emit("update:value", novoValor)
|
||||
emit("input", novoValor)
|
||||
emit("change", novoValor)
|
||||
}
|
||||
|
||||
function onLatChange(v: string | number) {
|
||||
const lat = parseCoordenada(v)
|
||||
if (lat === null) return
|
||||
emitirValor(lat, props.value?.longitude ?? COORDENADAS_PADRAO.longitude)
|
||||
}
|
||||
|
||||
function onLngChange(v: string | number) {
|
||||
const lng = parseCoordenada(v)
|
||||
if (lng === null) return
|
||||
emitirValor(props.value?.latitude ?? COORDENADAS_PADRAO.latitude, lng)
|
||||
}
|
||||
|
||||
function atualizarMarcador(lat: number, lng: number) {
|
||||
if (!marcador || !mapa) return
|
||||
const posAtual = marcador.getLatLng()
|
||||
if (posAtual.lat === lat && posAtual.lng === lng) return
|
||||
marcador.setLatLng([lat, lng])
|
||||
mapa.setView([lat, lng], mapa.getZoom())
|
||||
}
|
||||
|
||||
async function usarMinhaLocalizacao() {
|
||||
geolocalizando.value = true
|
||||
try {
|
||||
const pos = await obterLocalizacao()
|
||||
emitirValor(pos.latitude, pos.longitude)
|
||||
} finally {
|
||||
geolocalizando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function inicializarMapa() {
|
||||
if (!mapaRef.value) return
|
||||
const lat = props.value?.latitude ?? COORDENADAS_PADRAO.latitude
|
||||
const lng = props.value?.longitude ?? COORDENADAS_PADRAO.longitude
|
||||
|
||||
mapa = L.map(mapaRef.value).setView([lat, lng], zoomEfetivo.value)
|
||||
|
||||
const camadaMapa = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap",
|
||||
maxZoom: 19,
|
||||
})
|
||||
const camadaSatelite = L.tileLayer(
|
||||
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
{ attribution: "© Esri", maxZoom: 19 },
|
||||
)
|
||||
camadaMapa.addTo(mapa)
|
||||
L.control.layers({ Mapa: camadaMapa, Satélite: camadaSatelite }).addTo(mapa)
|
||||
|
||||
const arrastavel = !somenteLeitura.value
|
||||
marcador = L.marker([lat, lng], { draggable: arrastavel }).addTo(mapa)
|
||||
|
||||
marcador.on("dragend", () => {
|
||||
if (!marcador) return
|
||||
const pos = marcador.getLatLng()
|
||||
emitirValor(pos.lat, pos.lng)
|
||||
})
|
||||
|
||||
mapa.on("click", (e: L.LeafletMouseEvent) => {
|
||||
if (somenteLeitura.value) return
|
||||
emitirValor(e.latlng.lat, e.latlng.lng)
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
(v) => {
|
||||
if (!v) return
|
||||
atualizarMarcador(v.latitude, v.longitude)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
onMounted(() => inicializarMapa())
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (mapa) {
|
||||
mapa.remove()
|
||||
mapa = null
|
||||
marcador = null
|
||||
}
|
||||
const coordenadas = useCoordenadas({
|
||||
value: () => props.value,
|
||||
opcoes: () => props.opcoes,
|
||||
emit: emit as (event: string, ...args: unknown[]) => void,
|
||||
mapaRef,
|
||||
})
|
||||
|
||||
return {
|
||||
attrs,
|
||||
emit,
|
||||
mapaRef,
|
||||
alturaEfetiva,
|
||||
geolocalizando,
|
||||
latDisplay,
|
||||
lngDisplay,
|
||||
onLatChange,
|
||||
onLngChange,
|
||||
usarMinhaLocalizacao,
|
||||
}
|
||||
return { attrs, mapaRef, ...coordenadas }
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,7 @@
|
|||
/**
|
||||
* Tipos base para componentes de entrada (EliEntrada*)
|
||||
*
|
||||
* Objetivo:
|
||||
* - Padronizar o shape de dados de todos os componentes de entrada.
|
||||
* - Cada entrada possui sempre:
|
||||
* 1) `value`: o valor atual (tipado)
|
||||
* 2) `opcoes`: configuração do componente (rótulo, placeholder e extras por tipo)
|
||||
*
|
||||
* Como usar:
|
||||
* - `PadroesEntradas[tipo]` retorna a tipagem completa (value + opcoes) daquele tipo.
|
||||
* - `TipoEntrada` é a união com todos os tipos suportados (ex.: "texto" | "numero").
|
||||
* Tipos base para componentes de entrada (EliEntrada*).
|
||||
* PadroesEntradas[tipo] → tipagem completa (value + opcoes).
|
||||
* TipoEntrada → união de todos os tipos suportados.
|
||||
*/
|
||||
|
||||
import type { zCoordenadas } from "p-comuns"
|
||||
|
|
@ -221,13 +213,19 @@ export type PadroesEntradas = {
|
|||
>
|
||||
|
||||
coordenadas: tipoPadraoEntrada<
|
||||
z.infer<typeof zCoordenadas>,
|
||||
z.infer<typeof zCoordenadas> | null | undefined,
|
||||
{
|
||||
/** Nível de zoom inicial do mapa Leaflet (default: 4) */
|
||||
zoom?: number
|
||||
|
||||
/** Altura do container do mapa em pixels (default: 300) */
|
||||
alturaMapa?: number
|
||||
/** Camadas vetoriais GeoJSON (contornos do IBGE) */
|
||||
camadas?: {
|
||||
/** Exibir contornos dos estados */
|
||||
estados?: boolean
|
||||
/** Exibir contornos dos municípios (carregados por UF conforme viewport) */
|
||||
municipios?: boolean
|
||||
}
|
||||
}
|
||||
>
|
||||
}
|
||||
|
|
|
|||
160
src/componentes/EliEntrada/utils/camadas.ts
Normal file
160
src/componentes/EliEntrada/utils/camadas.ts
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import L from "leaflet"
|
||||
|
||||
/** URL base do bucket MinIO para arquivos GeoJSON */
|
||||
const URL_BASE = "https://paiol.idz.one/geo"
|
||||
|
||||
/** Zoom mínimo para exibir municípios */
|
||||
const ZOOM_MUNICIPIOS = 7
|
||||
/** Zoom a partir do qual usa resolução intermediária */
|
||||
const ZOOM_HD = 10
|
||||
/** Zoom a partir do qual usa resolução máxima */
|
||||
const ZOOM_UHD = 13
|
||||
|
||||
/**
|
||||
* Diretórios no MinIO por nível de detalhe:
|
||||
* - municipios/ → qualidade mínima (zoom 7-9)
|
||||
* - municipios_hd/ → qualidade média (zoom 10-12)
|
||||
* - municipios_uhd/ → qualidade máxima (zoom 13+)
|
||||
*/
|
||||
type NivelDetalhe = "municipios" | "municipios_hd" | "municipios_uhd"
|
||||
|
||||
/** Cache global — evita re-download entre instâncias do componente */
|
||||
const cacheGeoJSON = new Map<string, GeoJSON.GeoJsonObject>()
|
||||
|
||||
export interface OpcoesCamadas {
|
||||
estados?: boolean
|
||||
municipios?: boolean
|
||||
}
|
||||
|
||||
interface InfoEstado {
|
||||
codigo: string
|
||||
bounds: L.LatLngBounds
|
||||
}
|
||||
|
||||
async function buscarGeoJSON(url: string): Promise<GeoJSON.GeoJsonObject | null> {
|
||||
const cached = cacheGeoJSON.get(url)
|
||||
if (cached) return cached
|
||||
try {
|
||||
const resp = await fetch(url)
|
||||
if (!resp.ok) return null
|
||||
const dados = (await resp.json()) as GeoJSON.GeoJsonObject
|
||||
cacheGeoJSON.set(url, dados)
|
||||
return dados
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function estiloEstados(): L.PathOptions {
|
||||
return {
|
||||
color: "#1565C0",
|
||||
weight: 1.5,
|
||||
fillColor: "#42A5F5",
|
||||
fillOpacity: 0.05,
|
||||
dashArray: "4 4",
|
||||
}
|
||||
}
|
||||
|
||||
function estiloMunicipios(): L.PathOptions {
|
||||
return {
|
||||
color: "#78909C",
|
||||
weight: 0.8,
|
||||
fillColor: "#B0BEC5",
|
||||
fillOpacity: 0.03,
|
||||
}
|
||||
}
|
||||
|
||||
function nivelPorZoom(zoom: number): NivelDetalhe {
|
||||
if (zoom >= ZOOM_UHD) return "municipios_uhd"
|
||||
if (zoom >= ZOOM_HD) return "municipios_hd"
|
||||
return "municipios"
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicializa camadas vetoriais GeoJSON no mapa Leaflet.
|
||||
* - Estados: overlay imediato
|
||||
* - Municípios: sob demanda por UF, com 3 níveis de detalhe por zoom
|
||||
*/
|
||||
export function inicializarCamadas(mapa: L.Map, opcoes: OpcoesCamadas): void {
|
||||
if (!opcoes.estados && !opcoes.municipios) return
|
||||
carregarEstados(mapa, opcoes)
|
||||
}
|
||||
|
||||
async function carregarEstados(mapa: L.Map, opcoes: OpcoesCamadas): Promise<void> {
|
||||
const dados = await buscarGeoJSON(`${URL_BASE}/estados.geojson`)
|
||||
if (!dados) return
|
||||
|
||||
const estadosInfo: InfoEstado[] = []
|
||||
const camada = L.geoJSON(dados, {
|
||||
style: estiloEstados,
|
||||
onEachFeature: (feature, layer) => {
|
||||
const codigo = String(feature.properties?.codarea ?? "")
|
||||
if (!codigo) return
|
||||
estadosInfo.push({
|
||||
codigo,
|
||||
bounds: (layer as L.Polygon).getBounds(),
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
if (opcoes.estados) camada.addTo(mapa)
|
||||
if (opcoes.municipios) configurarMunicipios(mapa, estadosInfo)
|
||||
}
|
||||
|
||||
function configurarMunicipios(mapa: L.Map, estadosInfo: InfoEstado[]): void {
|
||||
/** Grupo de layers por nível de detalhe */
|
||||
const grupos: Record<NivelDetalhe, L.LayerGroup> = {
|
||||
municipios: L.layerGroup(),
|
||||
municipios_hd: L.layerGroup(),
|
||||
municipios_uhd: L.layerGroup(),
|
||||
}
|
||||
/** UFs já carregadas por nível */
|
||||
const carregados: Record<NivelDetalhe, Set<string>> = {
|
||||
municipios: new Set(),
|
||||
municipios_hd: new Set(),
|
||||
municipios_uhd: new Set(),
|
||||
}
|
||||
let nivelAtivo: NivelDetalhe | null = null
|
||||
|
||||
const verificarViewport = () => {
|
||||
const zoom = mapa.getZoom()
|
||||
if (zoom < ZOOM_MUNICIPIOS) {
|
||||
if (nivelAtivo && mapa.hasLayer(grupos[nivelAtivo])) {
|
||||
mapa.removeLayer(grupos[nivelAtivo])
|
||||
}
|
||||
nivelAtivo = null
|
||||
return
|
||||
}
|
||||
|
||||
const nivel = nivelPorZoom(zoom)
|
||||
if (nivel !== nivelAtivo) {
|
||||
if (nivelAtivo && mapa.hasLayer(grupos[nivelAtivo])) {
|
||||
mapa.removeLayer(grupos[nivelAtivo])
|
||||
}
|
||||
grupos[nivel].addTo(mapa)
|
||||
nivelAtivo = nivel
|
||||
}
|
||||
|
||||
const bounds = mapa.getBounds()
|
||||
for (const estado of estadosInfo) {
|
||||
if (carregados[nivel].has(estado.codigo)) continue
|
||||
if (!bounds.intersects(estado.bounds)) continue
|
||||
carregados[nivel].add(estado.codigo)
|
||||
carregarMunicipiosUF(nivel, estado.codigo, grupos[nivel])
|
||||
}
|
||||
}
|
||||
|
||||
mapa.on("moveend", verificarViewport)
|
||||
mapa.on("zoomend", verificarViewport)
|
||||
verificarViewport()
|
||||
}
|
||||
|
||||
async function carregarMunicipiosUF(
|
||||
nivel: NivelDetalhe,
|
||||
codigoUF: string,
|
||||
grupo: L.LayerGroup,
|
||||
): Promise<void> {
|
||||
const dados = await buscarGeoJSON(`${URL_BASE}/${nivel}/${codigoUF}.geojson`)
|
||||
if (!dados) return
|
||||
grupo.addLayer(L.geoJSON(dados, { style: estiloMunicipios }))
|
||||
}
|
||||
205
src/componentes/EliEntrada/utils/useCoordenadas.ts
Normal file
205
src/componentes/EliEntrada/utils/useCoordenadas.ts
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
import L from "leaflet"
|
||||
import { computed, onBeforeUnmount, onMounted, type Ref, ref, watch } from "vue"
|
||||
import type { PadroesEntradas } from "../tiposEntradas"
|
||||
import { inicializarCamadas } from "./camadas"
|
||||
import {
|
||||
ALTURA_MAPA_PADRAO,
|
||||
COORDENADAS_PADRAO,
|
||||
coordenadaValida,
|
||||
formatarCoordenada,
|
||||
obterLocalizacao,
|
||||
parseCoordenada,
|
||||
ZOOM_PADRAO,
|
||||
} from "./coordenadas"
|
||||
|
||||
type EntradaCoordenadas = PadroesEntradas["coordenadas"]
|
||||
type EmitFn = (event: string, ...args: unknown[]) => void
|
||||
|
||||
interface OpcoesComposable {
|
||||
value: () => EntradaCoordenadas["value"]
|
||||
opcoes: () => EntradaCoordenadas["opcoes"]
|
||||
emit: EmitFn
|
||||
mapaRef: Ref<HTMLElement | null>
|
||||
}
|
||||
|
||||
export function useCoordenadas({ value, opcoes, emit, mapaRef }: OpcoesComposable) {
|
||||
const geolocalizando = ref(false)
|
||||
let mapa: L.Map | null = null
|
||||
let marcador: L.Marker | null = null
|
||||
|
||||
const alturaEfetiva = computed(() => opcoes()?.alturaMapa ?? ALTURA_MAPA_PADRAO)
|
||||
const zoomEfetivo = computed(() => opcoes()?.zoom ?? ZOOM_PADRAO)
|
||||
const somenteLeitura = computed(() => Boolean(opcoes()?.apenasLeitura))
|
||||
const possuiValor = computed(() => value() != null)
|
||||
|
||||
const latDisplay = computed(() => {
|
||||
const v = value()
|
||||
return v ? formatarCoordenada(v.latitude) : ""
|
||||
})
|
||||
const lngDisplay = computed(() => {
|
||||
const v = value()
|
||||
return v ? formatarCoordenada(v.longitude) : ""
|
||||
})
|
||||
|
||||
function emitirValor(lat: number, lng: number) {
|
||||
if (!coordenadaValida(lat, lng)) return
|
||||
const novoValor: NonNullable<EntradaCoordenadas["value"]> = {
|
||||
formato: "decimal",
|
||||
datum: "EPSG:4326",
|
||||
latitude: Number.parseFloat(lat.toFixed(6)),
|
||||
longitude: Number.parseFloat(lng.toFixed(6)),
|
||||
}
|
||||
emit("update:value", novoValor)
|
||||
emit("input", novoValor)
|
||||
emit("change", novoValor)
|
||||
}
|
||||
|
||||
function limpar() {
|
||||
emit("update:value", null)
|
||||
emit("input", null)
|
||||
emit("change", null)
|
||||
removerMarcador()
|
||||
}
|
||||
|
||||
function removerMarcador() {
|
||||
if (marcador && mapa) {
|
||||
mapa.removeLayer(marcador)
|
||||
marcador = null
|
||||
}
|
||||
}
|
||||
|
||||
function adicionarMarcador(lat: number, lng: number) {
|
||||
if (!mapa) return
|
||||
marcador = L.marker([lat, lng], { draggable: !somenteLeitura.value }).addTo(mapa)
|
||||
marcador.on("dragend", () => {
|
||||
if (!marcador) return
|
||||
const pos = marcador.getLatLng()
|
||||
emitirValor(pos.lat, pos.lng)
|
||||
})
|
||||
mapa.setView([lat, lng], mapa.getZoom())
|
||||
}
|
||||
|
||||
function onLatChange(v: string | number) {
|
||||
const lat = parseCoordenada(v)
|
||||
if (lat === null) return
|
||||
emitirValor(lat, value()?.longitude ?? COORDENADAS_PADRAO.longitude)
|
||||
}
|
||||
|
||||
function onLngChange(v: string | number) {
|
||||
const lng = parseCoordenada(v)
|
||||
if (lng === null) return
|
||||
emitirValor(value()?.latitude ?? COORDENADAS_PADRAO.latitude, lng)
|
||||
}
|
||||
|
||||
function atualizarMarcador(lat: number, lng: number) {
|
||||
if (!mapa) return
|
||||
if (!marcador) {
|
||||
adicionarMarcador(lat, lng)
|
||||
return
|
||||
}
|
||||
const posAtual = marcador.getLatLng()
|
||||
if (posAtual.lat === lat && posAtual.lng === lng) return
|
||||
marcador.setLatLng([lat, lng])
|
||||
mapa.setView([lat, lng], mapa.getZoom())
|
||||
}
|
||||
|
||||
async function usarMinhaLocalizacao() {
|
||||
geolocalizando.value = true
|
||||
try {
|
||||
const pos = await obterLocalizacao()
|
||||
emitirValor(pos.latitude, pos.longitude)
|
||||
} finally {
|
||||
geolocalizando.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function inicializarMapa() {
|
||||
if (!mapaRef.value) return
|
||||
const v = value()
|
||||
const temValor = v != null
|
||||
|
||||
let centroLat: number
|
||||
let centroLng: number
|
||||
let zoomInicial = zoomEfetivo.value
|
||||
|
||||
if (temValor) {
|
||||
centroLat = v.latitude
|
||||
centroLng = v.longitude
|
||||
zoomInicial = Math.max(zoomInicial, 13)
|
||||
} else {
|
||||
try {
|
||||
const pos = await obterLocalizacao()
|
||||
centroLat = pos.latitude
|
||||
centroLng = pos.longitude
|
||||
zoomInicial = 13
|
||||
} catch {
|
||||
centroLat = COORDENADAS_PADRAO.latitude
|
||||
centroLng = COORDENADAS_PADRAO.longitude
|
||||
}
|
||||
}
|
||||
|
||||
mapa = L.map(mapaRef.value).setView([centroLat, centroLng], zoomInicial)
|
||||
|
||||
const camadaMapa = L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||
attribution: "© OpenStreetMap",
|
||||
maxZoom: 19,
|
||||
})
|
||||
const camadaSatelite = L.tileLayer(
|
||||
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
{ attribution: "© Esri", maxZoom: 19 },
|
||||
)
|
||||
camadaMapa.addTo(mapa)
|
||||
L.control.layers({ Mapa: camadaMapa, Satélite: camadaSatelite }).addTo(mapa)
|
||||
|
||||
if (temValor) adicionarMarcador(v.latitude, v.longitude)
|
||||
|
||||
mapa.on("click", (e: L.LeafletMouseEvent) => {
|
||||
if (somenteLeitura.value) return
|
||||
emitirValor(e.latlng.lat, e.latlng.lng)
|
||||
})
|
||||
|
||||
const cfgCamadas = opcoes()?.camadas
|
||||
if (cfgCamadas) inicializarCamadas(mapa, cfgCamadas)
|
||||
}
|
||||
|
||||
watch(
|
||||
value,
|
||||
(v) => {
|
||||
if (!v) {
|
||||
removerMarcador()
|
||||
return
|
||||
}
|
||||
atualizarMarcador(v.latitude, v.longitude)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
inicializarMapa()
|
||||
if (mapaRef.value) {
|
||||
const observer = new ResizeObserver(() => mapa?.invalidateSize())
|
||||
observer.observe(mapaRef.value)
|
||||
onBeforeUnmount(() => observer.disconnect())
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (mapa) {
|
||||
mapa.remove()
|
||||
mapa = null
|
||||
marcador = null
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
alturaEfetiva,
|
||||
geolocalizando,
|
||||
possuiValor,
|
||||
latDisplay,
|
||||
lngDisplay,
|
||||
onLatChange,
|
||||
onLngChange,
|
||||
usarMinhaLocalizacao,
|
||||
limpar,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue