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

This commit is contained in:
alsaze
2025-11-10 16:35:00 +03:00
parent 6cd7bd1dec
commit bff6833781
10 changed files with 372 additions and 100 deletions

146
components/DeliveryInfo.vue Normal file
View File

@@ -0,0 +1,146 @@
<template>
<ul class="delivery-info">
<li v-if="showFullContent" class="delivery-info__item">
<Icon class="delivery-info__icon" name="lucide:truck" />
<span class="delivery-info__text">Yandex</span>
</li>
<li class="delivery-info__item">
<Icon class="delivery-info__icon" name="lucide:map-pin" />
<span class="delivery-info__text">{{ checkoutPickupPoint?.address?.full_address }}</span>
</li>
<li class="delivery-info__item">
<Icon class="delivery-info__icon" name="lucide:shirt" size="xl" />
<span class="delivery-info__text">{{ checkoutPickupPoint?.pickup_services?.is_fitting_allowed ? 'с примеркой' : 'без примерки' }}</span>
</li>
<li class="delivery-info__item">
<Icon class="delivery-info__icon" name="lucide:package" />
<span class="delivery-info__text">срок хранения 7 дней</span>
</li>
<li class="delivery-info__item">
<UAccordion
:items="availableDays"
:ui="{
body: 'py-0',
panel: 'p-0',
wrapper: 'space-y-0',
}"
>
<template #schedule="{ item }">
<div>
<div
v-for="(scheduleItem, index) in item.content"
:key="index"
>
<Icon class="delivery-info__icon mr-2" name="lucide:clock" />
<span class="mr-2">{{ scheduleItem.dayName }}</span>
<span>{{ scheduleItem.timeRange }}</span>
</div>
</div>
</template>
</UAccordion>
</li>
<li>
<UAccordion :items="details" />
</li>
<UButton
v-if="showFullContent"
class="justify-content-center"
label="Привезти сюда"
size="xl"
@click="nextStep"
/>
</ul>
</template>
<script setup lang="ts">
import type { AccordionItem } from '@nuxt/ui'
import { computed, ref } from 'vue'
import { useCheckout } from '~/composables/useCheckout'
withDefaults(defineProps<{
showFullContent?: boolean
}>(), {
showFullContent: true,
})
const { checkoutPickupPoint, nextStep } = useCheckout()
const restrictionDays = {
1: 'пн',
2: 'вт',
3: 'ср',
4: 'чт',
5: 'пт',
6: 'сб',
7: 'вс',
}
// Функция для форматирования времени с ведущим нулем
const formatTime = (time: { hours: number | string, minutes: number | string }): string => {
const hours = String(time.hours).padStart(2, '0')
const minutes = String(time.minutes).padStart(2, '0')
return `${hours}:${minutes}`
}
// Функция для форматирования временного диапазона
const formatTimeRange = (timeFrom: { hours: number | string, minutes: number | string }, timeTo: { hours: number | string, minutes: number | string }): string => {
return `${formatTime(timeFrom)} - ${formatTime(timeTo)}`
}
// Аккордеон с расписанием - используем слот вместо HTML строки
const availableDays = computed(() => {
const scheduleItems = checkoutPickupPoint?.value?.schedule?.restrictions?.map((restriction) => {
const dayName = restrictionDays[restriction.days[0]]
const timeRange = formatTimeRange(restriction.time_from, restriction.time_to)
return { dayName, timeRange }
}) || []
return [
{
label: 'График работы',
icon: 'i-lucide:calendar',
slot: 'schedule',
content: scheduleItems,
},
]
})
const details = ref<AccordionItem[]>([
{
label: 'Подробнее',
icon: 'i-lucide:info',
content: checkoutPickupPoint?.value?.address?.comment || 'Дополнительная информация о пункте выдачи',
},
])
</script>
<style lang="scss">
.delivery-info {
display: flex;
flex-direction: column;
gap: 8px;
&__item {
display: flex;
align-items: flex-start;
gap: 6px;
}
&__icon {
flex-shrink: 0;
width: 20px;
height: 20px;
}
&__text {
flex: 1;
line-height: 1.4;
}
}
</style>

View File

@@ -20,11 +20,11 @@
@true-bounds="trueBounds = $event"
>
<YandexMapMarker
v-for="marker in pickupPoints"
:key="marker.id"
v-for="pickupPoint in pickupPoints"
:key="pickupPoint.id"
position="top-center left-center"
:settings="{ coordinates: [marker.position.longitude, marker.position.latitude] }"
@click="centerMap([marker.position.longitude, marker.position.latitude])"
:settings="{ coordinates: [pickupPoint.position.longitude, pickupPoint.position.latitude] }"
@click="centerMap(pickupPoint)"
>
<div class="marker">
<Icon name="i-lucide-map-pin" class="marker__icon" />
@@ -41,9 +41,10 @@
</template>
<script setup lang="ts">
import type { LngLat, LngLatBounds, YMap } from '@yandex/ymaps3-types'
import type { 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 { PickupPoint } from '~/server/shared/types/yandex_pvz'
import { useGeolocation } from '@vueuse/core'
import { computed, shallowRef } from 'vue'
import {
@@ -53,15 +54,11 @@ import {
YandexMapDefaultSchemeLayer,
YandexMapMarker,
} from 'vue-yandex-maps'
import { useCheckout } from '~/composables/useCheckout'
defineProps<{
pickupPoints: {
id: string
address: string
position: { latitude: number, longitude: number }
}[]
}>()
defineProps<{ pickupPoints: PickupPoint[] }>()
const { setCheckoutPickupPoint, isPickupPointSelected } = useCheckout()
const { coords } = useGeolocation()
const clusterer = shallowRef<YMapClusterer | null>(null)
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]])
@@ -73,12 +70,15 @@ const location = ref<YMapLocationRequest>({
zoom: 2,
})
function centerMap(coordinates: LngLat) {
function centerMap(pickupPoint: PickupPoint) {
location.value = {
center: coordinates,
center: [pickupPoint.position.longitude, pickupPoint.position.latitude],
zoom: 18,
duration: 2500,
duration: 500,
}
setCheckoutPickupPoint(pickupPoint)
isPickupPointSelected.value = true
}
watch(coords, (newCoords) => {