195 lines
5.7 KiB
Vue
195 lines
5.7 KiB
Vue
<template>
|
||
<div v-if="coords" class="delivery">
|
||
<!-- Desktop -->
|
||
<div v-if="!isMobile" class="delivery__sidebar">
|
||
<DeliveryInfo v-if="isPickupPointSelected" />
|
||
|
||
<DeliverySearch
|
||
v-else
|
||
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||
v-model:search-term="searchTerm"
|
||
:filtered-points="filteredPoints"
|
||
@update:checkout-pickup-point="updatePoint()"
|
||
/>
|
||
</div>
|
||
|
||
<!-- Mobile -->
|
||
<UDrawer
|
||
v-if="isMobile"
|
||
v-model:open="open"
|
||
fixed
|
||
:ui="{
|
||
content: 'fixed bg-default ring ring-default flex focus:outline-none overflow-hidden',
|
||
container: 'w-full flex flex-col gap-4 p-4 overflow-hidden',
|
||
body: 'flex-1 overflow-y-auto',
|
||
}"
|
||
>
|
||
<template #content>
|
||
<div class="px-4 pb-4">
|
||
<DeliveryInfo v-if="isPickupPointSelected" />
|
||
</div>
|
||
</template>
|
||
</UDrawer>
|
||
|
||
<UModal
|
||
v-model:open="openDeliverySearch"
|
||
fullscreen
|
||
:dismissible="false"
|
||
:ui="{
|
||
body: 'overflow-hidden',
|
||
overlay: 'bg-black/50',
|
||
header: 'hidden',
|
||
}"
|
||
>
|
||
<template #body>
|
||
<DeliverySearch
|
||
v-if="activeTab === IPvzMapTabs.LIST"
|
||
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||
v-model:search-term="searchTerm"
|
||
:filtered-points="filteredPoints"
|
||
@update:checkout-pickup-point="updatePoint()"
|
||
/>
|
||
|
||
<UDrawer
|
||
v-if="isMobile"
|
||
v-model:open="open"
|
||
fixed
|
||
:ui="{
|
||
content: 'fixed bg-default ring ring-default flex focus:outline-none overflow-hidden',
|
||
container: 'w-full flex flex-col gap-4 p-4 overflow-hidden',
|
||
body: 'flex-1 overflow-y-auto',
|
||
}"
|
||
>
|
||
<template #content>
|
||
<div class="px-4 pb-4">
|
||
<DeliveryInfo v-if="isPickupPointSelected" />
|
||
</div>
|
||
</template>
|
||
</UDrawer>
|
||
</template>
|
||
|
||
<template #footer>
|
||
<div class="d-flex flex-row w-full justify-between">
|
||
<MapControlFitting v-model:fitting="fitting" />
|
||
|
||
<MapControlTabs v-model:active-tab="activeTab" />
|
||
</div>
|
||
</template>
|
||
</UModal>
|
||
|
||
<!-- Desktop Mobile -->
|
||
<PvzMap
|
||
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||
v-model:active-tab="activeTab"
|
||
v-model:fitting="fitting"
|
||
:pickup-points="filteredPoints"
|
||
@update:checkout-pickup-point="updatePoint()"
|
||
/>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import type { YandexLocationDetectResponse } from '#shared/yandex_location_detect'
|
||
import type { YandexPvzPoint, YandexPvzResponse } from '#shared/yandex_pvz'
|
||
import { IPvzMapFittingTabs, IPvzMapTabs } from '#shared/types'
|
||
import { useGeolocation, useMediaQuery } from '@vueuse/core'
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import DeliveryInfo from '~/components/DeliveryInfo.vue'
|
||
import DeliverySearch from '~/components/DeliverySearch.vue'
|
||
import PvzMap from '~/components/PvzMap.vue'
|
||
import { useCheckout } from '~/composables/useCheckout'
|
||
|
||
const { setCheckoutPickupPoint, isPickupPointSelected, checkoutPickupPoint } = useCheckout()
|
||
const { coords } = useGeolocation()
|
||
const open = ref(false)
|
||
const isMobile = useMediaQuery('(max-width: 1280px)')
|
||
const yandexPvz = ref<YandexPvzResponse>()
|
||
const searchTerm = ref('')
|
||
const activeTab = ref()
|
||
const fitting = ref(IPvzMapFittingTabs.ALL)
|
||
const openDeliverySearch = ref()
|
||
|
||
const waitForCoords = () =>
|
||
new Promise<void>((resolve) => {
|
||
const stop = watch(coords, (newCoords) => {
|
||
if (newCoords.latitude && newCoords.longitude) {
|
||
stop()
|
||
resolve()
|
||
}
|
||
})
|
||
})
|
||
|
||
onMounted(async () => {
|
||
await waitForCoords()
|
||
|
||
// обратное геокодирование (т.е. получаение города из координат)
|
||
const response = await fetch(
|
||
`https://nominatim.openstreetmap.org/reverse?lat=${coords.value.latitude}&lon=${coords.value.longitude}&format=json&accept-language=ru`,
|
||
)
|
||
const openstreetmap = await response.json()
|
||
|
||
// получение geo_id из названию города
|
||
const { data: yandexLocation } = await useFetch<YandexLocationDetectResponse>('/api/yandex_location_detect', {
|
||
method: 'POST',
|
||
body: {
|
||
location: openstreetmap?.address?.city,
|
||
},
|
||
})
|
||
|
||
// получения пунктов выдачи города из geo_id
|
||
const { data: yandexPvzApi } = await useFetch<YandexPvzResponse>('/api/yandex_pvz', {
|
||
method: 'POST',
|
||
body: {
|
||
geo_id: yandexLocation?.value?.variants[0]?.geo_id,
|
||
},
|
||
})
|
||
|
||
yandexPvz.value = yandexPvzApi.value
|
||
})
|
||
|
||
const filteredPoints = computed<YandexPvzPoint[]>(() => {
|
||
const points = yandexPvz.value?.points || []
|
||
const term = searchTerm.value?.toLowerCase() || ''
|
||
|
||
return points.filter((point) => {
|
||
const matchesSearch = !term || point.address.full_address.toLowerCase().includes(term)
|
||
|
||
const isAllowed = point.pickup_services.is_fitting_allowed
|
||
const matchesFitting
|
||
= fitting.value === IPvzMapFittingTabs.ALL
|
||
|| (fitting.value === IPvzMapFittingTabs.ALLOW && isAllowed)
|
||
|| (fitting.value === IPvzMapFittingTabs.FORBID && !isAllowed)
|
||
|
||
return matchesSearch && matchesFitting
|
||
})
|
||
})
|
||
|
||
function updatePoint() {
|
||
setCheckoutPickupPoint(checkoutPickupPoint.value)
|
||
open.value = true
|
||
}
|
||
|
||
watch(() => activeTab.value, () => {
|
||
openDeliverySearch.value = activeTab.value === IPvzMapTabs.LIST
|
||
})
|
||
|
||
definePageMeta({
|
||
layout: 'checkout',
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.delivery {
|
||
display: flex;
|
||
flex-direction: row;
|
||
|
||
&__sidebar {
|
||
flex-shrink: 0;
|
||
max-height: calc(100vh - 40px);
|
||
overflow-y: auto;
|
||
width: 410px;
|
||
padding-inline: 24px;
|
||
}
|
||
}
|
||
</style>
|