This commit is contained in:
parent
e5420edc64
commit
bd4edfdade
@ -11,8 +11,13 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="delivery-info__item">
|
<li class="delivery-info__item">
|
||||||
<Icon class="delivery-info__icon" name="lucide:shirt" size="xl" />
|
<Icon
|
||||||
<span class="delivery-info__text">{{ checkoutPickupPoint?.pickup_services?.is_fitting_allowed ? 'с примеркой' : 'без примерки' }}</span>
|
class="delivery-info__icon"
|
||||||
|
:name="checkoutPickupPoint?.pickup_services?.is_fitting_allowed ? 'lucide:shirt' : 'i-lucide-ban'" size="xl"
|
||||||
|
/>
|
||||||
|
<span class="delivery-info__text">
|
||||||
|
{{ checkoutPickupPoint?.pickup_services?.is_fitting_allowed ? 'с примеркой' : 'без примерки' }}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="delivery-info__item">
|
<li class="delivery-info__item">
|
||||||
|
|||||||
@ -24,12 +24,12 @@
|
|||||||
v-for="pickupPoint in filteredPoints"
|
v-for="pickupPoint in filteredPoints"
|
||||||
:key="pickupPoint.id"
|
:key="pickupPoint.id"
|
||||||
class="pickup-point-card"
|
class="pickup-point-card"
|
||||||
@click="checkoutPickupPoint = pickupPoint"
|
@click="onSelectPoint(pickupPoint)"
|
||||||
>
|
>
|
||||||
<div class="pickup-point-card__content">
|
<div class="pickup-point-card__content">
|
||||||
<h3>Yandex</h3>
|
<h3>Yandex</h3>
|
||||||
<p>{{ `${pickupPoint?.address?.street} ${pickupPoint?.address?.house}` }}</p>
|
<p>{{ `${pickupPoint?.address?.street} ${pickupPoint?.address?.house}` }}</p>
|
||||||
<h3>с day month бесплатно</h3>
|
<h3>Как определить стоимость ?</h3>
|
||||||
</div>
|
</div>
|
||||||
<Icon class="pickup-point-card__action" name="lucide:chevron-right" />
|
<Icon class="pickup-point-card__action" name="lucide:chevron-right" />
|
||||||
</div>
|
</div>
|
||||||
@ -38,13 +38,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from 'vue'
|
||||||
import type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
import type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
||||||
import { useCheckout } from '~/composables/useCheckout'
|
import { defineEmits } from 'vue'
|
||||||
|
|
||||||
defineProps<{ filteredPoints: PickupPoint[] }>()
|
defineProps<{ filteredPoints: PickupPoint[] }>()
|
||||||
|
const emit = defineEmits(['update:checkout-pickup-point'])
|
||||||
|
|
||||||
const { checkoutPickupPoint } = useCheckout()
|
const checkoutPickupPoint = defineModel('checkoutPickupPoint', {
|
||||||
const searchTerm = ref('')
|
type: Object as PropType<PickupPoint>,
|
||||||
|
default: () => undefined,
|
||||||
|
})
|
||||||
|
|
||||||
|
const searchTerm = defineModel('searchTerm', { type: String, default: '' })
|
||||||
|
|
||||||
|
const onSelectPoint = (pickupPoint: PickupPoint) => {
|
||||||
|
checkoutPickupPoint.value = pickupPoint
|
||||||
|
emit('update:checkout-pickup-point', pickupPoint)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|||||||
@ -1,21 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="control">
|
<UTabs v-model="fitting" :content="false" :items="tabs" size="sm" />
|
||||||
<UCheckbox
|
|
||||||
v-model="modelValue"
|
|
||||||
size="xl"
|
|
||||||
label="с примеркой"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const modelValue = defineModel<boolean>({ required: true })
|
import type { TabsItem } from '@nuxt/ui'
|
||||||
</script>
|
import type { PropType } from 'vue'
|
||||||
|
import { IPvzMapFittingTabs } from '#shared/types'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
<style scoped lang="scss">
|
const fitting = defineModel('fitting', { type: Object as PropType<IPvzMapFittingTabs>, default: () => IPvzMapFittingTabs.ALL })
|
||||||
.control {
|
|
||||||
background: var(--ui-bg-elevated);
|
const tabs = computed<TabsItem[]>(() => [
|
||||||
padding: 8px 18px;
|
{
|
||||||
border-radius: 10px;
|
value: IPvzMapFittingTabs.ALL,
|
||||||
}
|
label: fitting.value === IPvzMapFittingTabs.ALL ? 'все' : '',
|
||||||
</style>
|
icon: 'i-lucide-globe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: IPvzMapFittingTabs.ALLOW,
|
||||||
|
label: fitting.value === IPvzMapFittingTabs.ALLOW ? 'с примеркой' : '',
|
||||||
|
icon: 'i-lucide-shirt',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: IPvzMapFittingTabs.FORBID,
|
||||||
|
label: fitting.value === IPvzMapFittingTabs.FORBID ? 'без примерки' : '',
|
||||||
|
icon: 'i-lucide-ban',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
</script>
|
||||||
|
|||||||
@ -1,36 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<UTabs v-model="activeTab" :content="false" :items="tabs">
|
<UTabs v-model="activeTab" :content="false" :items="tabs" size="sm" />
|
||||||
<template #map>
|
|
||||||
{{ activeTab === '0' ? 'Карта' : activeTab === '1' ? 'Список' : 'Другое' }}
|
|
||||||
</template>
|
|
||||||
</UTabs>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { TabsItem } from '@nuxt/ui'
|
import type { TabsItem } from '@nuxt/ui'
|
||||||
|
import type { PropType } from 'vue'
|
||||||
import { IPvzMapTabs } from '#shared/types'
|
import { IPvzMapTabs } from '#shared/types'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const activeTab = defineModel('activeTab', { type: String, default: IPvzMapTabs.MAP })
|
const activeTab = defineModel('activeTab', { type: Object as PropType<IPvzMapTabs>, default: () => IPvzMapTabs.MAP })
|
||||||
|
|
||||||
const tabs = computed<TabsItem[]>(() =>
|
const tabs = computed<TabsItem[]>(() =>
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: IPvzMapTabs.MAP,
|
value: IPvzMapTabs.MAP,
|
||||||
label: activeTab.value === 'map' ? 'Карта' : '',
|
label: activeTab.value === IPvzMapTabs.MAP ? 'Карта' : '',
|
||||||
icon: 'lucide:map-pin',
|
icon: 'lucide:map-pin',
|
||||||
slot: 'map' as const,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: IPvzMapTabs.LIST,
|
value: IPvzMapTabs.LIST,
|
||||||
label: activeTab.value === 'list' ? 'Список' : '',
|
label: activeTab.value === IPvzMapTabs.LIST ? 'Список' : '',
|
||||||
icon: 'i-lucide-list',
|
icon: 'i-lucide-list',
|
||||||
slot: 'list' as const,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
:key="pickupPoint.id"
|
:key="pickupPoint.id"
|
||||||
position="top-center left-center"
|
position="top-center left-center"
|
||||||
:settings="{ coordinates: [pickupPoint.position.longitude, pickupPoint.position.latitude] }"
|
:settings="{ coordinates: [pickupPoint.position.longitude, pickupPoint.position.latitude] }"
|
||||||
@click="$emit('update:modelValue', pickupPoint)"
|
@click="onSelectPoint(pickupPoint)"
|
||||||
>
|
>
|
||||||
<div class="marker">
|
<div class="marker">
|
||||||
<Icon name="i-lucide-map-pin" class="marker__icon" />
|
<Icon name="i-lucide-map-pin" class="marker__icon" />
|
||||||
@ -38,15 +38,15 @@
|
|||||||
</template>
|
</template>
|
||||||
</YandexMapClusterer>
|
</YandexMapClusterer>
|
||||||
|
|
||||||
<YandexMapControls :settings="{ position: 'bottom left', orientation: 'vertical' }">
|
<YandexMapControls :settings="{ position: isMobile ? 'bottom left' : 'top left', orientation: 'vertical' }">
|
||||||
<YandexMapControl>
|
<YandexMapControl>
|
||||||
<MapControlFitting v-model="isFitting" />
|
<MapControlFitting v-model:fitting="fitting" />
|
||||||
</YandexMapControl>
|
</YandexMapControl>
|
||||||
</YandexMapControls>
|
</YandexMapControls>
|
||||||
|
|
||||||
<YandexMapControls :settings="{ position: 'bottom right', orientation: 'vertical' }">
|
<YandexMapControls v-if="isMobile" :settings="{ position: 'bottom right', orientation: 'vertical' }">
|
||||||
<YandexMapControl>
|
<YandexMapControl>
|
||||||
<MapControlTabs v-model="activeTab" />
|
<MapControlTabs v-model:active-tab="activeTab" />
|
||||||
</YandexMapControl>
|
</YandexMapControl>
|
||||||
</YandexMapControls>
|
</YandexMapControls>
|
||||||
</YandexMap>
|
</YandexMap>
|
||||||
@ -56,10 +56,11 @@
|
|||||||
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 { PropType } from 'vue'
|
||||||
import type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
import type { PickupPoint } from '~/server/shared/types/yandex_pvz'
|
||||||
import { IPvzMapTabs } from '#shared/types'
|
import { IPvzMapFittingTabs, IPvzMapTabs } from '#shared/types'
|
||||||
import { useGeolocation } from '@vueuse/core'
|
import { useGeolocation, useMediaQuery } from '@vueuse/core'
|
||||||
import { computed, shallowRef } from 'vue'
|
import { computed, defineEmits, shallowRef } from 'vue'
|
||||||
import {
|
import {
|
||||||
YandexMap,
|
YandexMap,
|
||||||
YandexMapClusterer,
|
YandexMapClusterer,
|
||||||
@ -72,26 +73,30 @@ import {
|
|||||||
import MapControlFitting from '~/components/MapControlFitting.vue'
|
import MapControlFitting from '~/components/MapControlFitting.vue'
|
||||||
import MapControlTabs from '~/components/MapControlTabs.vue'
|
import MapControlTabs from '~/components/MapControlTabs.vue'
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: PickupPoint, pickupPoints: PickupPoint[] }>()
|
defineProps<{ pickupPoints: PickupPoint[] }>()
|
||||||
|
const emit = defineEmits(['update:checkout-pickup-point'])
|
||||||
|
|
||||||
defineEmits<{
|
const checkoutPickupPoint = defineModel('checkoutPickupPoint', { type: Object as PropType<PickupPoint>, default: () => undefined })
|
||||||
(e: 'update:modelValue', value: PickupPoint | undefined): void
|
const activeTab = defineModel('activeTab', { type: Object as PropType<IPvzMapTabs>, default: () => IPvzMapTabs.MAP })
|
||||||
}>()
|
const fitting = defineModel('fitting', { type: Object as PropType<IPvzMapFittingTabs>, default: () => IPvzMapFittingTabs.ALL })
|
||||||
|
|
||||||
const activeTab = defineModel('activeTab', { type: String, default: IPvzMapTabs.MAP })
|
|
||||||
const isFitting = defineModel('isFitting', { type: Boolean, default: false })
|
|
||||||
|
|
||||||
const { coords } = useGeolocation()
|
const { coords } = useGeolocation()
|
||||||
|
const isMobile = useMediaQuery('(max-width: 1280px)')
|
||||||
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 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>({
|
||||||
zoom: 2,
|
zoom: 2,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onSelectPoint = (pickupPoint: PickupPoint) => {
|
||||||
|
checkoutPickupPoint.value = pickupPoint
|
||||||
|
emit('update:checkout-pickup-point', pickupPoint)
|
||||||
|
}
|
||||||
|
|
||||||
watch(coords, (newCoords) => {
|
watch(coords, (newCoords) => {
|
||||||
if (newCoords && hasCoords.value) {
|
if (newCoords && hasCoords.value) {
|
||||||
location.value = {
|
location.value = {
|
||||||
@ -102,7 +107,10 @@ watch(coords, (newCoords) => {
|
|||||||
}
|
}
|
||||||
}, { once: true })
|
}, { once: true })
|
||||||
|
|
||||||
watch(() => props.modelValue, (newPickupPoint) => {
|
watch(() => checkoutPickupPoint.value, (newPickupPoint) => {
|
||||||
|
if (!newPickupPoint)
|
||||||
|
return
|
||||||
|
|
||||||
location.value = {
|
location.value = {
|
||||||
center: [newPickupPoint.position.longitude, newPickupPoint.position.latitude],
|
center: [newPickupPoint.position.longitude, newPickupPoint.position.latitude],
|
||||||
zoom: 18,
|
zoom: 18,
|
||||||
@ -152,6 +160,11 @@ watch(() => props.modelValue, (newPickupPoint) => {
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover &__icon {
|
||||||
|
color: var(--ui-primary);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cluster {
|
.cluster {
|
||||||
|
|||||||
@ -4,7 +4,13 @@
|
|||||||
<div v-if="!isMobile" class="delivery__sidebar">
|
<div v-if="!isMobile" class="delivery__sidebar">
|
||||||
<DeliveryInfo v-if="isPickupPointSelected" />
|
<DeliveryInfo v-if="isPickupPointSelected" />
|
||||||
|
|
||||||
<DeliverySearch v-else :filtered-points="filteredPoints" />
|
<DeliverySearch
|
||||||
|
v-else
|
||||||
|
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||||||
|
v-model:search-term="searchTerm"
|
||||||
|
:filtered-points="filteredPoints"
|
||||||
|
@update:checkout-pickup-point="updatePoint()"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Mobile -->
|
<!-- Mobile -->
|
||||||
@ -28,18 +34,20 @@
|
|||||||
<UModal
|
<UModal
|
||||||
v-model:open="openDeliverySearch"
|
v-model:open="openDeliverySearch"
|
||||||
fullscreen
|
fullscreen
|
||||||
|
:dismissible="false"
|
||||||
:ui="{
|
:ui="{
|
||||||
body: 'overflow-hidden',
|
body: 'overflow-hidden',
|
||||||
container: 'p-0',
|
|
||||||
overlay: 'bg-black/50',
|
overlay: 'bg-black/50',
|
||||||
base: 'rounded-none shadow-none',
|
|
||||||
header: 'hidden',
|
header: 'hidden',
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<template #body>
|
<template #body>
|
||||||
<DeliverySearch
|
<DeliverySearch
|
||||||
v-if="activeTab === IPvzMapTabs.LIST"
|
v-if="activeTab === IPvzMapTabs.LIST"
|
||||||
|
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||||||
|
v-model:search-term="searchTerm"
|
||||||
:filtered-points="filteredPoints"
|
:filtered-points="filteredPoints"
|
||||||
|
@update:checkout-pickup-point="updatePoint()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<UDrawer
|
<UDrawer
|
||||||
@ -62,7 +70,7 @@
|
|||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="d-flex flex-row w-full justify-between">
|
<div class="d-flex flex-row w-full justify-between">
|
||||||
<MapControlFitting v-model="isFitting" />
|
<MapControlFitting v-model:fitting="fitting" />
|
||||||
|
|
||||||
<MapControlTabs v-model:active-tab="activeTab" />
|
<MapControlTabs v-model:active-tab="activeTab" />
|
||||||
</div>
|
</div>
|
||||||
@ -71,19 +79,18 @@
|
|||||||
|
|
||||||
<!-- Desktop Mobile -->
|
<!-- Desktop Mobile -->
|
||||||
<PvzMap
|
<PvzMap
|
||||||
v-model="checkoutPickupPoint"
|
v-model:checkout-pickup-point="checkoutPickupPoint"
|
||||||
v-model:active-tab="activeTab"
|
v-model:active-tab="activeTab"
|
||||||
v-model:is-fitting="isFitting"
|
v-model:fitting="fitting"
|
||||||
:pickup-points="filteredPoints"
|
:pickup-points="filteredPoints"
|
||||||
@update:model-value="updatePoint()"
|
@update:checkout-pickup-point="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 { IPvzMapFittingTabs, 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'
|
||||||
@ -97,9 +104,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(IPvzMapTabs.MAP)
|
const activeTab = ref()
|
||||||
const isFitting = ref()
|
const fitting = ref(IPvzMapFittingTabs.ALL)
|
||||||
const openDeliverySearch = ref(false)
|
const openDeliverySearch = ref()
|
||||||
|
|
||||||
const waitForCoords = () =>
|
const waitForCoords = () =>
|
||||||
new Promise<void>((resolve) => {
|
new Promise<void>((resolve) => {
|
||||||
@ -139,14 +146,21 @@ onMounted(async () => {
|
|||||||
yandexPvz.value = yandexPvzApi.value
|
yandexPvz.value = yandexPvzApi.value
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO сделать отдельный компонент UISearch
|
|
||||||
const filteredPoints = computed<PickupPoint[]>(() => {
|
const filteredPoints = computed<PickupPoint[]>(() => {
|
||||||
if (!searchTerm.value || searchTerm.value === '') {
|
const points = yandexPvz.value?.points || []
|
||||||
return yandexPvz.value?.points || []
|
const term = searchTerm.value?.toLowerCase() || ''
|
||||||
}
|
|
||||||
|
|
||||||
return yandexPvz.value?.points?.filter(point => point?.address?.full_address?.toLowerCase().includes(searchTerm.value.toLowerCase())
|
return points.filter((point) => {
|
||||||
|| isFitting.value === point.pickup_services.is_fitting_allowed) || []
|
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() {
|
function updatePoint() {
|
||||||
|
|||||||
@ -2,3 +2,8 @@ export enum IPvzMapTabs {
|
|||||||
MAP = 'map',
|
MAP = 'map',
|
||||||
LIST = 'list',
|
LIST = 'list',
|
||||||
}
|
}
|
||||||
|
export enum IPvzMapFittingTabs {
|
||||||
|
ALL = 'all',
|
||||||
|
ALLOW = 'allow',
|
||||||
|
FORBID = 'Forbid',
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user