Обновление яндекс-маркеров на карте vue3
Есть компонент catalog в который импортится два компонента фильтров компонент FilterTypeBlock, в который импортится FilterTypeSelect, в FilterTypeSelect находятся чекбоксы при клике на которые фильтруются карточки BaseCard, которые находятся в компоненте catalog, карточки подгружаются с api фильтруются и выводятся с помощью функции filteredCatalog, если был клик по чекбоксу то в BaseCard выводится отфильтрованный массив filteredCards из функции filteredCatalog, значения самих фильтров находятся в компоненте catalog в массиве sauFilters, они передаются в компонент FilterTypeSelect и принимаются в <input :id="item.id" type="checkbox" class="filter-item__input" :checked="selectedFilters.includes(item.id)" @change="handleFilterClick(item.id)"/>, выбранные фильтры обновляются после каждого клика в функции updateFilterSelection которая вызывается и обновляется в updateSelectedFilter в компоненте catalog, сами фильтры работают корректно, при клике на чекбокс остаются только карточки соответствующие условиям этого чекбокса, если кликнуть на другой чекбокс, то условия чекбоксов суммируются и т.д, проблема только с отображением меток на карте BaseMap в компоненте catalog, функция filteredCoords отслеживает изменения в уже отфильтрованном массиве и передает результат в :coords в компонент BaseMap, BaseMap - это компонент яндекс-карт, он принимает :coords в виде массива [[lat, lon], [lat, lon]], и выводит метки yamap-marker на карту, если выводить все значения в консоль, то все координаты при кликах работают точно так же как и фильтры, но на деле при первом клике на чекбокс, метки пропадают полностью, а при втором клике выводятся ровно в том же количестве что и у этого же чекбокса, в чем может быть проблема с отображением меток ?
вот код catalog:
<template>
<div class="catalog-page">
<div class="block title">
<p class="h1">
{{ title }}
</p>
</div>
<div class="block side-filters" :class="`${filtersState ? 'side-filters__active' : ''}`">
<div class="side-filters-block">
<SortTypeSelect
v-model="sortingValue"
:sorting-list="sortingList"
class="sorting-block"
/>
<div class="close-btn" @click="closeFilterss" />
<div class="side-filters-block__filters">
<FilterTypeBlock
:filters-list="sauFilters"
@filter-click="updateSelectedFilter"
/>
</div>
</div>
</div>
<div class="date-filters">
<DateTimeFilters
v-model="sortingValue"
:sorting-list="sortingList"
@update:filters-state="toggleFilters"
@update:list-map="toggleListMap"
/>
</div>
<div class="block catalog" :class="`${listMapState === 'list' ? 'catalog__active' : ''}`">
<div v-for="card in filteredCatalog" :key="card.id">
<NuxtLink :to="`/${card.id}`">
<BaseCard
:card-id="card.id"
theme="catalog"
:rating="card.rating"
:shortname="card.shortname"
:title="card.name"
:types="card.properties"
:metro="card.subway"
:address="card.address"
:visitors-number="card.capacity"
:images="getImageUrls(card.image)"
:is-hot-offer="card.isHotOffer"
:price="card.price"
:recommended-price="card.recommendedPrice"
/>
</NuxtLink>
</div>
</div>
<BaseMap class="block map" :class="`${listMapState === 'list' ? '' : 'map__active'}`" :coords="filteredCoords"/>
<BaseShadowOverlay class="overlay" :state="filtersState" @update:state="closeFilters" />
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useStore } from "vuex";
import DateTimeFilters from '~/components/pages/catalog/DateTimeFilters'
import BaseMap from '~/components/base/BaseMap.vue';
import { definePageMeta } from '#imports';
import BaseShadowOverlay from '~/components/base/BaseShadowOverlay.vue';
import SortTypeSelect from '~/components/pages/catalog/SortTypeSelect.vue';
import FilterTypeBlock from '~/components/pages/catalog/FilterTypeBlock.vue';
definePageMeta({
layout: 'catalog'
});
const props = defineProps({
coords: {
type: Array,
required: true
},
price: {
type: Object,
required: true
},
recommendedPrice: {
type: Object,
required: true
},
modelValue: {
type: Object,
required: true
},
sortingList: {
type: Array,
required: true
}
})
const selected = ref('');
const filteredCatalog = computed(() => {
if (selected.value.length === 0) {
return catalog.value;
}
return catalog.value.filter(card => {
return selected.value.some(filter => card.properties[filter]);
});
});
const store = useStore();
onMounted(async () => {
await store.dispatch('GET_CATALOG');
await store.dispatch('GET_PROPERTIES');
updateFilterCounts();
});
const getImageUrls = (imageId) => {
const baseUrl = "api";
return [`${baseUrl}${imageId}`];
};
const catalog = computed(() => store.getters.CATALOG.catalog)
const title = 'Каталог саун'
const sortingValue = ref({ title: 'По релевантности', type: 'relevance' })
const sortingList = [
{ title: 'По релевантности', type: 'relevance' },
{ title: 'По цене', type: 'cost' },
{ title: 'По рейтингу', type: 'rate' },
{ title: 'По удаленности', type: 'distance' }
]
const filtersIsOpen = ref(false)
const filtersState = computed(() => {
return filtersIsOpen.value;
})
const toggleFilters = () => {
filtersIsOpen.value = !filtersIsOpen.value;
}
const closeFilters = (event) => {
filtersIsOpen.value = event;
}
const listMapState = ref('list')
const toggleListMap = (event) => {
listMapState.value = event;
}
const emit = defineEmits(['update:modelValue'])
function closeFilterss () {
emit('update:closeFilters', false)
}
const selectedFilters = ref([]);
const updateFilterSelection = () => {
if (selectedFilters.value === '') {
selected.value = '';
filteredCatalog.value = catalog.value;
} else {
filteredCatalog.value = catalog.value.filter(card => card.properties[selectedFilters]);
}
}
const filteredCoords = computed(() => {
return filteredCatalog.value.map((card) => [card.lat, card.lon])
})
const sauFilters = [
{
title: 'Тип сауны',
filters: [
{ id: 'turkish', filterName: 'Турецкая', filterCount: 0 },
{ id: 'finskaya', filterName: 'Финская', filterCount: 0 },
{ id: 'russian', filterName: 'Русская', filterCount: 0 }
]
}
]
const updateFilterCounts = () => {
const sauCounts = {
turkish: 0,
russian: 0,
finskaya: 0
};
catalog.value.forEach((item) => {
if (item.properties.turkish) {
item.turkish++;
}
if (item.properties.russian) {
item.russian++;
}
if (item.properties.finskaya) {
item.finskaya++;
}
});
sauFilters.forEach((filterGroup) => {
filterGroup.filters.forEach((filter) => {
switch (filter.filterName) {
case 'Турецкая':
filter.filterCount = sauCounts.turkish;
break;
case 'Финская':
filter.filterCount = sauCounts.finskaya;
break;
case 'Русская':
filter.filterCount = sauCounts.russian;
break;
default:
break;
}
});
});
};
updateFilterCounts();
const updateSelectedFilter = (selectedFilters) => {
selected.value = selectedFilters;
updateFilterSelection();
};
</script>
вот код FilterTypeBlock:
<template>
<div class="filter-block">
<div class="filter-block__title" @click="toggleOpen">
<InlineSvg
src="_nuxt/public/arrow-bottom.svg"
class="filter-block__title-svg"
:class="`${isOpen ? 'opened' : ''}`"
/>
<div class="filter-block__title-name" :class="`${isOpen ? 'opened' : ''}`">
{{ title }}
</div>
<div class="filter-block__title-count">
{{ count }}
</div>
</div>
<div class="filter-block__list" :class="`${isOpen ? 'opened' : ''}`">
<FilterTypeSelect
v-for="(filtersItem, idx) in filtersList"
:key="idx"
:title="filtersItem.title"
:filter-list="filtersItem.filters"
:selectedFilters="selectedFilters"
@filter-click="updateSelectedFilters"
/>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import InlineSvg from 'vue-inline-svg';
import FilterTypeSelect from './FilterTypeSelect.vue';
defineProps({
title: {
type: String,
required: true
},
filtersList: {
type: Array,
required: true
}
})
const isOpen = ref(false)
function toggleOpen() {
isOpen.value = !isOpen.value;
}
const emit = defineEmits(['filter-click']);
const selectedFilters = ref([]);
const updateSelectedFilters = (selectedFilter) => {
const filterIndex = selectedFilters.value.indexOf(selectedFilter);
if (filterIndex === -1) {
selectedFilters.value.push(selectedFilter);
} else {
selectedFilters.value.splice(filterIndex, 1);
}
emit('filter-click', selectedFilters.value);
}
const count = 1
</script>
вот код FilterTypeSelect:
<template>
<div class="filter">
<div class="filter__title" @click="toggleOpen">
<div class="filter__title-name" :class="`${isOpen ? 'opened' : ''}`">
{{ title }}
</div>
<InlineSvg
src="_nuxt/public/arrow-bottom.svg"
class="filter__title-svg"
:class="`${isOpen ? 'opened' : ''}`"
/>
</div>
<div class="filter__list" :class="`${isOpen ? 'opened' : ''}`">
<div
v-for="(item, idx) in filterList"
:key="idx"
class="filter-item"
>
<input :id="item.id" type="checkbox" class="filter-item__input" :checked="selectedFilters.includes(item.id)" @change="handleFilterClick(item.id)"/>
<label :for="item.id" class="filter-item__label">
<div class="filter-item__label-check" />
<div class="filter-item__label-name">{{ item.filterName }}</div>
<div class="filter-item__label-count">{{ item.filterCount }}</div>
</label>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import InlineSvg from 'vue-inline-svg';
defineProps({
title: {
type: String,
required: true
},
filterList: {
type: Array,
required: true
},
selectedFilters: {
type: Array,
required: true
}
})
const isOpen = ref(false)
function toggleOpen () {
isOpen.value = !isOpen.value;
}
const emit = defineEmits(['filter-click']);
const handleFilterClick = (filterName) => {
emit('filter-click', filterName)
}
</script>