feat(search): improvement search cache (#44)

This commit is contained in:
2026-01-05 15:35:18 +03:00
committed by Nikita Kiselev
parent 089b686722
commit 8a9bac8221
4 changed files with 106 additions and 21 deletions

View File

@@ -17,6 +17,13 @@ export const useSearchStore = defineStore('search', {
isLoadingMore: false,
isSearchPerformed: false,
hasMore: false,
// Placeholder товары для пустого состояния поиска
placeholderProducts: {
data: [],
total: 0,
},
isLoadingPlaceholder: false,
}),
actions: {
@@ -91,6 +98,35 @@ export const useSearchStore = defineStore('search', {
this.isSearchPerformed = true;
}
},
async loadSearchPlaceholder() {
// Если данные уже есть в store, возвращаем их
if (this.placeholderProducts.data.length > 0) {
return {
data: this.placeholderProducts.data,
meta: {
total: this.placeholderProducts.total,
},
};
}
try {
this.isLoadingPlaceholder = true;
// Иначе загружаем с сервера
const response = await ftch('productsSearchPlaceholder');
this.placeholderProducts.data = response.data.slice(0, 3);
this.placeholderProducts.total = response?.meta?.total || 0;
return {
data: this.placeholderProducts.data,
meta: {
total: this.placeholderProducts.total,
},
};
} finally {
this.isLoadingPlaceholder = false;
}
},
},
});

View File

@@ -79,25 +79,38 @@
class="flex flex-col items-center justify-center text-center py-16 px-10"
>
<div class="avatar-group -space-x-6 mb-4">
<template v-for="product in preloadedProducts" :key="product.id">
<div v-if="product.image" class="avatar">
<div class="w-12">
<img :src="product.image" :alt="product.name"/>
</div>
<!-- Skeleton при загрузке -->
<template v-if="searchStore.isLoadingPlaceholder">
<div v-for="n in 3" :key="n" class="avatar">
<div class="w-12 skeleton rounded-full"></div>
</div>
<div v-else class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-12 rounded-full">
<span class="text-3xl">{{ product.name.charAt(0).toUpperCase() }}</span>
</div>
<div class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-12 skeleton"></div>
</div>
</template>
<div class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-12">
<span>{{ renderSmartNumber(productsTotal) }}</span>
<!-- Товары после загрузки -->
<template v-else>
<template v-for="product in preloadedProducts" :key="product.id">
<div v-if="product.image" class="avatar">
<div class="w-12">
<img :src="product.image" :alt="product.name"/>
</div>
</div>
<div v-else class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-12 rounded-full">
<span class="text-3xl">{{ product.name.charAt(0).toUpperCase() }}</span>
</div>
</div>
</template>
<div class="avatar avatar-placeholder">
<div class="bg-neutral text-neutral-content w-12">
<span>{{ renderSmartNumber(productsTotal) }}</span>
</div>
</div>
</div>
</template>
</div>
<h2 class="text-xl font-semibold mb-2">Поиск товаров</h2>
<p class="text-sm mb-4">Введите запрос, чтобы отобразить подходящие товары.</p>
@@ -185,8 +198,8 @@ const handleHideKeyboardClick = () => {
}
};
const preloadedProducts = ref([]);
const productsTotal = ref(0);
const preloadedProducts = computed(() => searchStore.placeholderProducts.data);
const productsTotal = computed(() => searchStore.placeholderProducts.total);
const searchWrapperStyle = computed(() => {
const safeTop = getComputedStyle(document.documentElement)
@@ -208,8 +221,6 @@ onMounted(async () => {
});
yaMetrika.reachGoal(YA_METRIKA_GOAL.VIEW_SEARCH);
const response = await searchStore.fetchProducts('', 1, 3);
productsTotal.value = response?.meta?.total || 0;
preloadedProducts.value = response.data.splice(0, 3);
await searchStore.loadSearchPlaceholder();
});
</script>