alsaze 8e68d5b162
All checks were successful
Deploy / build (push) Successful in 12m45s
карта ПВЗ
2025-10-22 22:57:29 +03:00

144 lines
3.2 KiB
Vue

<template>
<div v-if="!hasCoords" class="p-4 text-center">
Определяем ваше местоположение...
</div>
<YandexMap
v-else
v-model="map"
:settings="{
location: {
center: [coords?.longitude, coords?.latitude],
zoom: 9,
},
}"
class="w-full"
style="height: calc(100dvh - 54px)"
>
<YandexMapDefaultSchemeLayer />
<YandexMapDefaultFeaturesLayer />
<YandexMapClusterer
v-model="clusterer"
:grid-size="64"
zoom-on-cluster-click
@true-bounds="trueBounds = $event"
>
<YandexMapMarker
v-for="marker in pickupPoints"
:key="marker.id"
position="top-center left-center"
:settings="{ coordinates: [marker.position.longitude, marker.position.latitude] }"
>
<div class="marker">
<Icon name="i-lucide-map-pin" class="marker__icon" />
</div>
</YandexMapMarker>
<template #cluster="{ length }">
<div class="cluster fade-in">
{{ length }}
</div>
</template>
</YandexMapClusterer>
</YandexMap>
</template>
<script setup lang="ts">
import type { LngLatBounds, YMap } from '@yandex/ymaps3-types'
import type { YMapClusterer } from '@yandex/ymaps3-types/packages/clusterer'
import { useGeolocation } from '@vueuse/core'
import { computed, shallowRef } from 'vue'
import {
YandexMap,
YandexMapClusterer,
YandexMapDefaultFeaturesLayer,
YandexMapDefaultSchemeLayer,
YandexMapMarker,
} from 'vue-yandex-maps'
const props = defineProps<{
pickupPoints: {
id: string
address: string
position: { latitude: number, longitude: number }
}[]
}>()
const { coords } = useGeolocation()
const hasCoords = computed(() => coords.value?.latitude !== Infinity && coords.value?.longitude !== Infinity)
const map = shallowRef<null | YMap>(null)
const clusterer = shallowRef<YMapClusterer | null>(null)
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]])
</script>
<style lang="scss" scoped>
.marker {
position: relative;
background-color: #0f172b;
border: 1px solid #0f172b;
border-radius: 12px;
padding: 6px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
transition:
transform 0.2s ease,
background-color 0.3s ease;
&:hover {
transform: scale(1.1);
background-color: #1e293b;
}
&::after {
content: '';
position: absolute;
bottom: -6px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid #0f172b;
filter: drop-shadow(0 2px 1px rgba(0, 0, 0, 0.15));
}
&__icon {
color: white;
width: 20px;
height: 20px;
}
}
.cluster {
background-color: #0f172b;
color: white;
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
border: 2px solid #fff;
transition:
transform 0.2s ease,
background-color 0.3s ease;
&:hover {
transform: scale(1.1);
background-color: #1e293b;
}
}
</style>