карты пвз
All checks were successful
Deploy / build (push) Successful in 12m27s

This commit is contained in:
alsaze 2025-10-23 01:03:00 +03:00
parent 8e68d5b162
commit 6cd7bd1dec
4 changed files with 1666 additions and 575 deletions

View File

@ -6,13 +6,8 @@
<YandexMap <YandexMap
v-else v-else
v-model="map" v-model="map"
:settings="{
location: {
center: [coords?.longitude, coords?.latitude],
zoom: 9,
},
}"
class="w-full" class="w-full"
:settings="{ location }"
style="height: calc(100dvh - 54px)" style="height: calc(100dvh - 54px)"
> >
<YandexMapDefaultSchemeLayer /> <YandexMapDefaultSchemeLayer />
@ -29,6 +24,7 @@
:key="marker.id" :key="marker.id"
position="top-center left-center" position="top-center left-center"
:settings="{ coordinates: [marker.position.longitude, marker.position.latitude] }" :settings="{ coordinates: [marker.position.longitude, marker.position.latitude] }"
@click="centerMap([marker.position.longitude, marker.position.latitude])"
> >
<div class="marker"> <div class="marker">
<Icon name="i-lucide-map-pin" class="marker__icon" /> <Icon name="i-lucide-map-pin" class="marker__icon" />
@ -45,7 +41,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { LngLatBounds, YMap } from '@yandex/ymaps3-types' import type { LngLat, LngLatBounds, YMap } from '@yandex/ymaps3-types'
import type { YMapLocationRequest } from '@yandex/ymaps3-types/imperative/YMap'
import type { YMapClusterer } from '@yandex/ymaps3-types/packages/clusterer' import type { YMapClusterer } from '@yandex/ymaps3-types/packages/clusterer'
import { useGeolocation } from '@vueuse/core' import { useGeolocation } from '@vueuse/core'
import { computed, shallowRef } from 'vue' import { computed, shallowRef } from 'vue'
@ -57,7 +54,7 @@ import {
YandexMapMarker, YandexMapMarker,
} from 'vue-yandex-maps' } from 'vue-yandex-maps'
const props = defineProps<{ defineProps<{
pickupPoints: { pickupPoints: {
id: string id: string
address: string address: string
@ -66,13 +63,35 @@ const props = defineProps<{
}>() }>()
const { coords } = useGeolocation() const { coords } = useGeolocation()
const clusterer = shallowRef<YMapClusterer | null>(null)
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]])
const map = shallowRef<null | YMap>(null)
const hasCoords = computed(() => coords.value?.latitude !== Infinity && coords.value?.longitude !== Infinity) const hasCoords = computed(() => coords.value?.latitude !== Infinity && coords.value?.longitude !== Infinity)
const map = shallowRef<null | YMap>(null) const location = ref<YMapLocationRequest>({
zoom: 2,
})
const clusterer = shallowRef<YMapClusterer | null>(null) function centerMap(coordinates: LngLat) {
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]]) location.value = {
center: coordinates,
zoom: 18,
duration: 2500,
}
}
watch(coords, (newCoords) => {
if (newCoords && hasCoords.value) {
location.value = {
center: [newCoords.longitude, newCoords.latitude],
zoom: 9,
duration: 2500,
}
}
}, { once: true })
defineExpose({ centerMap, location })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -13,6 +13,7 @@
"typegen": "node .typegen" "typegen": "node .typegen"
}, },
"dependencies": { "dependencies": {
"@nuxt/content": "^3.7.1",
"@nuxt/fonts": "0.11.4", "@nuxt/fonts": "0.11.4",
"@nuxt/icon": "1.15.0", "@nuxt/icon": "1.15.0",
"@nuxt/image": "1.10.0", "@nuxt/image": "1.10.0",

View File

@ -1,17 +1,22 @@
<template> <template>
<div v-if="coords" class="delivery"> <div v-if="coords" class="delivery">
<div class="delivery__sidebar"> <div class="delivery__sidebar">
<UInput v-model="searchTerm" size="xl" class="w-full" placeholder="pvz" />
<div class="delivery__items">
<div <div
v-for="point in yandexPvz?.points" v-for="point in filteredPvz?.points"
:key="point.id" :key="point.id"
class="pickup-point-item" class="pickup-point-item"
@click="onPickupClick(point)" @click="centerMap(point)"
> >
<h3>Yandex</h3> <h3>Yandex</h3>
{{ `${point?.address?.street} ${point?.address?.house}` }} {{ `${point?.address?.street} ${point?.address?.house}` }}
<Icon class="pickup-point-item__action" name="lucide:chevron-right" /> <Icon class="pickup-point-item__action" name="lucide:chevron-right" />
</div> </div>
</div> </div>
</div>
<PvzMap ref="mapRef" :pickup-points="yandexPvz?.points" /> <PvzMap ref="mapRef" :pickup-points="yandexPvz?.points" />
</div> </div>
</template> </template>
@ -21,9 +26,10 @@ import { useGeolocation } from '@vueuse/core'
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import PvzMap from '~/components/PvzMap.vue' import PvzMap from '~/components/PvzMap.vue'
const yandexPvz = ref('')
const { coords } = useGeolocation() const { coords } = useGeolocation()
const city = ref('') const mapRef = ref<InstanceType<typeof PvzMap> | null>(null)
const yandexPvz = ref('')
const searchTerm = ref('')
const waitForCoords = () => const waitForCoords = () =>
new Promise<void>((resolve) => { new Promise<void>((resolve) => {
@ -38,11 +44,13 @@ const waitForCoords = () =>
onMounted(async () => { onMounted(async () => {
await waitForCoords() await waitForCoords()
// обратное геокодирование (т.е. получаение города из координат)
const response = await fetch( const response = await fetch(
`https://nominatim.openstreetmap.org/reverse?lat=${coords.value.latitude}&lon=${coords.value.longitude}&format=json&accept-language=ru`, `https://nominatim.openstreetmap.org/reverse?lat=${coords.value.latitude}&lon=${coords.value.longitude}&format=json&accept-language=ru`,
) )
const openstreetmap = await response.json() const openstreetmap = await response.json()
// получение geo_id из названию города
const { data: yandexLocation } = await useFetch('/api/yandex_location', { const { data: yandexLocation } = await useFetch('/api/yandex_location', {
method: 'POST', method: 'POST',
body: { body: {
@ -50,6 +58,7 @@ onMounted(async () => {
}, },
}) })
// получения пунктов выдачи города из geo_id
const { data: yandexPvzApi } = await useFetch('/api/yandex_pvz', { const { data: yandexPvzApi } = await useFetch('/api/yandex_pvz', {
method: 'POST', method: 'POST',
body: { body: {
@ -60,14 +69,19 @@ onMounted(async () => {
yandexPvz.value = yandexPvzApi.value yandexPvz.value = yandexPvzApi.value
}) })
const mapRef = ref<InstanceType<typeof PvzMap> | null>(null) // TODO сделать отдельный компонент UISearch
const filteredPvz = computed(() => {
if (!searchTerm.value && searchTerm.value === '')
return yandexPvz.value
const onPickupClick = (point: any) => { const result = yandexPvz?.value?.points?.filter(value => value?.address?.full_address?.toLowerCase().includes(searchTerm.value.toLowerCase()))
const lat = point?.position?.latitude return {
const lon = point?.position?.longitude points: [...result],
if (typeof lat === 'number' && typeof lon === 'number') {
mapRef.value?.centerMap(lat, lon)
} }
})
const centerMap = (point: any) => {
mapRef.value?.centerMap([point?.position?.longitude, point?.position?.latitude])
} }
definePageMeta({ definePageMeta({
@ -82,9 +96,13 @@ definePageMeta({
&__sidebar { &__sidebar {
width: 410px; width: 410px;
height: calc(100dvh - 54px); padding: 24px;
flex-shrink: 0; }
&__items {
overflow-y: auto; overflow-y: auto;
flex-shrink: 0;
height: calc(100dvh - 54px - 40px - 24px);
} }
} }
@ -92,7 +110,7 @@ definePageMeta({
position: relative; position: relative;
width: 400px; width: 400px;
height: 100px; height: 100px;
padding: 24px; padding-block: 24px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;

2141
yarn.lock

File diff suppressed because it is too large Load Diff