карты пвз
This commit is contained in:
parent
55103d3778
commit
e5420edc64
@ -48,6 +48,8 @@ const searchTerm = ref('')
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@use '~/assets/scss/utils' as *;
|
||||||
|
|
||||||
.pickup-point-card {
|
.pickup-point-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -62,6 +64,10 @@ const searchTerm = ref('')
|
|||||||
height: calc(100dvh - 54px - 40px - 24px);
|
height: calc(100dvh - 54px - 40px - 24px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
@include mobile {
|
||||||
|
height: calc(100dvh - 72px - 40px - 32px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
|
|||||||
21
components/MapControlFitting.vue
Normal file
21
components/MapControlFitting.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<template>
|
||||||
|
<div class="control">
|
||||||
|
<UCheckbox
|
||||||
|
v-model="modelValue"
|
||||||
|
size="xl"
|
||||||
|
label="с примеркой"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const modelValue = defineModel<boolean>({ required: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.control {
|
||||||
|
background: var(--ui-bg-elevated);
|
||||||
|
padding: 8px 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
components/MapControlTabs.vue
Normal file
36
components/MapControlTabs.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<UTabs v-model="activeTab" :content="false" :items="tabs">
|
||||||
|
<template #map>
|
||||||
|
{{ activeTab === '0' ? 'Карта' : activeTab === '1' ? 'Список' : 'Другое' }}
|
||||||
|
</template>
|
||||||
|
</UTabs>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { TabsItem } from '@nuxt/ui'
|
||||||
|
import { IPvzMapTabs } from '#shared/types'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const activeTab = defineModel('activeTab', { type: String, default: IPvzMapTabs.MAP })
|
||||||
|
|
||||||
|
const tabs = computed<TabsItem[]>(() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
value: IPvzMapTabs.MAP,
|
||||||
|
label: activeTab.value === 'map' ? 'Карта' : '',
|
||||||
|
icon: 'lucide:map-pin',
|
||||||
|
slot: 'map' as const,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: IPvzMapTabs.LIST,
|
||||||
|
label: activeTab.value === 'list' ? 'Список' : '',
|
||||||
|
icon: 'i-lucide-list',
|
||||||
|
slot: 'list' as const,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -40,36 +40,24 @@
|
|||||||
|
|
||||||
<YandexMapControls :settings="{ position: 'bottom left', orientation: 'vertical' }">
|
<YandexMapControls :settings="{ position: 'bottom left', orientation: 'vertical' }">
|
||||||
<YandexMapControl>
|
<YandexMapControl>
|
||||||
<div
|
<MapControlFitting v-model="isFitting" />
|
||||||
class="control"
|
|
||||||
>
|
|
||||||
<UCheckbox
|
|
||||||
v-model="isFitting"
|
|
||||||
size="xl"
|
|
||||||
label="с примеркой"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</YandexMapControl>
|
</YandexMapControl>
|
||||||
</YandexMapControls>
|
</YandexMapControls>
|
||||||
|
|
||||||
<YandexMapControls :settings="{ position: 'bottom right', orientation: 'vertical' }">
|
<YandexMapControls :settings="{ position: 'bottom right', orientation: 'vertical' }">
|
||||||
<YandexMapControl>
|
<YandexMapControl>
|
||||||
<UTabs v-model="activeTab" :content="false" :items="tabs">
|
<MapControlTabs v-model="activeTab" />
|
||||||
<template #map>
|
|
||||||
{{ activeTab === '0' ? 'Карта' : activeTab === '1' ? 'Список' : 'Другое' }}
|
|
||||||
</template>
|
|
||||||
</UTabs>
|
|
||||||
</YandexMapControl>
|
</YandexMapControl>
|
||||||
</YandexMapControls>
|
</YandexMapControls>
|
||||||
</YandexMap>
|
</YandexMap>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TabsItem } from '@nuxt/ui'
|
|
||||||
import type { 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 { 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 type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
import type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
||||||
|
import { IPvzMapTabs } from '#shared/types'
|
||||||
import { useGeolocation } from '@vueuse/core'
|
import { useGeolocation } from '@vueuse/core'
|
||||||
import { computed, shallowRef } from 'vue'
|
import { computed, shallowRef } from 'vue'
|
||||||
import {
|
import {
|
||||||
@ -81,38 +69,23 @@ import {
|
|||||||
YandexMapDefaultSchemeLayer,
|
YandexMapDefaultSchemeLayer,
|
||||||
YandexMapMarker,
|
YandexMapMarker,
|
||||||
} from 'vue-yandex-maps'
|
} from 'vue-yandex-maps'
|
||||||
|
import MapControlFitting from '~/components/MapControlFitting.vue'
|
||||||
|
import MapControlTabs from '~/components/MapControlTabs.vue'
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: PickupPoint, pickupPoints: PickupPoint[] }>()
|
const props = defineProps<{ modelValue: PickupPoint, pickupPoints: PickupPoint[] }>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
defineEmits<{
|
||||||
(e: 'update:modelValue', value: PickupPoint | undefined): void
|
(e: 'update:modelValue', value: PickupPoint | undefined): void
|
||||||
(e: 'activeTab', value: string): void
|
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const activeTab = defineModel('activeTab', { type: String, default: IPvzMapTabs.MAP })
|
||||||
|
const isFitting = defineModel('isFitting', { type: Boolean, default: false })
|
||||||
|
|
||||||
const { coords } = useGeolocation()
|
const { coords } = useGeolocation()
|
||||||
const clusterer = shallowRef<YMapClusterer | null>(null)
|
const clusterer = shallowRef<YMapClusterer | null>(null)
|
||||||
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]])
|
const trueBounds = ref<LngLatBounds>([[0, 0], [0, 0]])
|
||||||
const map = shallowRef<null | YMap>(null)
|
const map = shallowRef<null | YMap>(null)
|
||||||
|
|
||||||
const isFitting = ref(false)
|
|
||||||
const activeTab = ref('map')
|
|
||||||
const tabs = computed<TabsItem[]>(() =>
|
|
||||||
[
|
|
||||||
{
|
|
||||||
value: 'map',
|
|
||||||
label: activeTab.value === 'map' ? 'Карта' : '',
|
|
||||||
icon: 'lucide:map-pin',
|
|
||||||
slot: 'map' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'list',
|
|
||||||
label: activeTab.value === 'list' ? 'Список' : '',
|
|
||||||
icon: 'i-lucide-list',
|
|
||||||
slot: 'list' as const,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
const hasCoords = computed(() => coords.value?.latitude !== Infinity && coords.value?.longitude !== Infinity)
|
const hasCoords = computed(() => coords.value?.latitude !== Infinity && coords.value?.longitude !== Infinity)
|
||||||
|
|
||||||
const location = ref<YMapLocationRequest>({
|
const location = ref<YMapLocationRequest>({
|
||||||
@ -136,10 +109,6 @@ watch(() => props.modelValue, (newPickupPoint) => {
|
|||||||
duration: 500,
|
duration: 500,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => activeTab.value, (newActiveTab) => {
|
|
||||||
emit('activeTab', newActiveTab)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -207,10 +176,4 @@ watch(() => activeTab.value, (newActiveTab) => {
|
|||||||
background-color: #1e293b;
|
background-color: #1e293b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.control {
|
|
||||||
background: var(--ui-bg-elevated);
|
|
||||||
padding: 8px 18px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -25,20 +25,65 @@
|
|||||||
</template>
|
</template>
|
||||||
</UDrawer>
|
</UDrawer>
|
||||||
|
|
||||||
activeTab
|
<UModal
|
||||||
{{ activeTab }}
|
v-model:open="openDeliverySearch"
|
||||||
|
fullscreen
|
||||||
|
:ui="{
|
||||||
|
body: 'overflow-hidden',
|
||||||
|
container: 'p-0',
|
||||||
|
overlay: 'bg-black/50',
|
||||||
|
base: 'rounded-none shadow-none',
|
||||||
|
header: 'hidden',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #body>
|
||||||
|
<DeliverySearch
|
||||||
|
v-if="activeTab === IPvzMapTabs.LIST"
|
||||||
|
:filtered-points="filteredPoints"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<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="isFitting" />
|
||||||
|
|
||||||
|
<MapControlTabs v-model:active-tab="activeTab" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UModal>
|
||||||
|
|
||||||
<!-- Desktop Mobile -->
|
<!-- Desktop Mobile -->
|
||||||
<PvzMap
|
<PvzMap
|
||||||
v-model="checkoutPickupPoint"
|
v-model="checkoutPickupPoint"
|
||||||
|
v-model:active-tab="activeTab"
|
||||||
|
v-model:is-fitting="isFitting"
|
||||||
:pickup-points="filteredPoints"
|
:pickup-points="filteredPoints"
|
||||||
@active-tab="value => activeTab = value"
|
|
||||||
@update:model-value="updatePoint()"
|
@update:model-value="updatePoint()"
|
||||||
|
@update:is-fitting="value => isFitting = value"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PickupPoint, YandexPvzResponse } from '~/server/shared/types/yandex_pvz'
|
import type { PickupPoint, YandexPvzResponse } from '~/server/shared/types/yandex_pvz'
|
||||||
|
import { IPvzMapTabs } from '#shared/types'
|
||||||
import { useGeolocation, useMediaQuery } from '@vueuse/core'
|
import { useGeolocation, useMediaQuery } from '@vueuse/core'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import DeliveryInfo from '~/components/DeliveryInfo.vue'
|
import DeliveryInfo from '~/components/DeliveryInfo.vue'
|
||||||
@ -52,7 +97,9 @@ const open = ref(false)
|
|||||||
const isMobile = useMediaQuery('(max-width: 1280px)')
|
const isMobile = useMediaQuery('(max-width: 1280px)')
|
||||||
const yandexPvz = ref<YandexPvzResponse>()
|
const yandexPvz = ref<YandexPvzResponse>()
|
||||||
const searchTerm = ref('')
|
const searchTerm = ref('')
|
||||||
const activeTab = ref()
|
const activeTab = ref(IPvzMapTabs.MAP)
|
||||||
|
const isFitting = ref()
|
||||||
|
const openDeliverySearch = ref(false)
|
||||||
|
|
||||||
const waitForCoords = () =>
|
const waitForCoords = () =>
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
@ -98,9 +145,8 @@ const filteredPoints = computed<PickupPoint[]>(() => {
|
|||||||
return yandexPvz.value?.points || []
|
return yandexPvz.value?.points || []
|
||||||
}
|
}
|
||||||
|
|
||||||
return yandexPvz.value?.points?.filter(point =>
|
return yandexPvz.value?.points?.filter(point => point?.address?.full_address?.toLowerCase().includes(searchTerm.value.toLowerCase())
|
||||||
point?.address?.full_address?.toLowerCase().includes(searchTerm.value.toLowerCase()),
|
|| isFitting.value === point.pickup_services.is_fitting_allowed) || []
|
||||||
) || []
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function updatePoint() {
|
function updatePoint() {
|
||||||
@ -108,6 +154,10 @@ function updatePoint() {
|
|||||||
open.value = true
|
open.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(() => activeTab.value, () => {
|
||||||
|
openDeliverySearch.value = activeTab.value === IPvzMapTabs.LIST
|
||||||
|
})
|
||||||
|
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'checkout',
|
layout: 'checkout',
|
||||||
})
|
})
|
||||||
|
|||||||
4
shared/types.ts
Normal file
4
shared/types.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export enum IPvzMapTabs {
|
||||||
|
MAP = 'map',
|
||||||
|
LIST = 'list',
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user